Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
name: test

# Consuming apps run their own test suites, not this package's, so without this
# workflow nothing enforces them. bcc-media-app's Semaphore `Test` block does
# fire when `/submodules/` changes, but it only runs that app's own `test/` —
# a regression in here reaches every consuming app unchallenged.
on:
push:
branches: [main]
Expand All @@ -27,3 +23,41 @@ jobs:
- run: flutter analyze

- run: flutter test

android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- uses: subosito/flutter-action@v2
with:
flutter-version: 3.47.2
channel: stable
cache: true

- run: flutter pub get

- run: make android-test

ios:
runs-on: macos-26
env:
DEVELOPER_DIR: /Applications/Xcode_26.6.app
timeout-minutes: 45
steps:
- uses: actions/checkout@v4

- uses: subosito/flutter-action@v2
with:
flutter-version: 3.47.2
channel: stable
cache: true

- run: flutter pub get

- run: make ios-test
30 changes: 28 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: publish pigeons help
.PHONY: publish pigeons help ios-test android-test

BUILD_NUMBER=$(shell grep -i -e "version: " pubspec.yaml | cut -d " " -f 2)

Expand All @@ -15,4 +15,30 @@ publish: ## Publish the package to pub.dev
mkdocs gh-deploy

pigeons: ## Generate pigeon files
for f in pigeons/*.dart; do dart run pigeon --input $$f; done
for f in pigeons/*.dart; do dart run pigeon --input $$f; done

# Which simulator the native iOS tests run on. Defaults to the newest available
# iPhone, so this works on a dev machine and on a CI runner with a different
# set of runtimes installed. Override with either:
# make ios-test IOS_SIM_ID=<udid>
# make ios-test IOS_DESTINATION='platform=iOS Simulator,name=iPhone 16,OS=latest'
IOS_SIM_ID ?= $(shell xcrun simctl list devices available | awk '/^-- iOS /{ok=1; next} /^-- /{ok=0} ok && /iPhone/{l=$$0} END{print l}' | sed -E 's/.*\(([0-9A-Fa-f-]{36})\).*/\1/')
IOS_DESTINATION ?= id=$(IOS_SIM_ID)

ios-test: ## Run the native iOS unit tests (example/ios/RunnerTests) on a simulator
@test -n "$(IOS_SIM_ID)" || (echo "No iPhone simulator available. Install one via Xcode > Settings > Components."; exit 1)
cd example && flutter pub get && flutter build ios --simulator --debug --config-only
cd example/ios && xcodebuild test \
-workspace Runner.xcworkspace \
-scheme Runner \
-destination '$(IOS_DESTINATION)' \
-only-testing:RunnerTests

# `--config-only` rather than `pub get`: the example app's `gradlew` and
# `gradle-wrapper.jar` are gitignored (Flutter's template does this), so a fresh
# clone has no wrapper at all. `flutter build --config-only` resolves
# dependencies, writes local.properties, and injects the wrapper, leaving the
# tracked gradle-wrapper.properties (Gradle 8.14.3) in place.
android-test: ## Run the native Android unit tests (android/src/test)
cd example && flutter build apk --config-only
cd example/android && ./gradlew :bccm_player:testDebugUnitTest
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// Autogenerated from Pigeon (v28.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon

package media.bcc.bccm_player.pigeon;
Expand Down Expand Up @@ -29,6 +29,154 @@
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"})
public class ChromecastControllerPigeon {

static boolean pigeonDoubleEquals(double a, double b) {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (a == 0.0 ? 0.0 : a) == (b == 0.0 ? 0.0 : b) || (Double.isNaN(a) && Double.isNaN(b));
}

static boolean pigeonFloatEquals(float a, float b) {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (a == 0.0f ? 0.0f : a) == (b == 0.0f ? 0.0f : b) || (Float.isNaN(a) && Float.isNaN(b));
}

static int pigeonDoubleHashCode(double d) {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
if (d == 0.0) {
d = 0.0;
}
long bits = Double.doubleToLongBits(d);
return (int) (bits ^ (bits >>> 32));
}

static int pigeonFloatHashCode(float f) {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
if (f == 0.0f) {
f = 0.0f;
}
return Float.floatToIntBits(f);
}

static boolean pigeonDeepEquals(Object a, Object b) {
if (a == b) { return true; }
if (a == null || b == null) { return false; }
if (a instanceof byte[] && b instanceof byte[]) {
return Arrays.equals((byte[]) a, (byte[]) b);
}
if (a instanceof int[] && b instanceof int[]) {
return Arrays.equals((int[]) a, (int[]) b);
}
if (a instanceof long[] && b instanceof long[]) {
return Arrays.equals((long[]) a, (long[]) b);
}
if (a instanceof double[] && b instanceof double[]) {
double[] da = (double[]) a;
double[] db = (double[]) b;
if (da.length != db.length) {
return false;
}
for (int i = 0; i < da.length; i++) {
if (!pigeonDoubleEquals(da[i], db[i])) {
return false;
}
}
return true;
}
if (a instanceof List && b instanceof List) {
List<?> listA = (List<?>) a;
List<?> listB = (List<?>) b;
if (listA.size() != listB.size()) { return false; }
for (int i = 0; i < listA.size(); i++) {
if (!pigeonDeepEquals(listA.get(i), listB.get(i))) {
return false;
}
}
return true;
}
if (a instanceof Map && b instanceof Map) {
Map<?, ?> mapA = (Map<?, ?>) a;
Map<?, ?> mapB = (Map<?, ?>) b;
if (mapA.size() != mapB.size()) { return false; }
for (Map.Entry<?, ?> entryA : mapA.entrySet()) {
Object keyA = entryA.getKey();
Object valueA = entryA.getValue();
boolean found = false;
for (Map.Entry<?, ?> entryB : mapB.entrySet()) {
Object keyB = entryB.getKey();
if (pigeonDeepEquals(keyA, keyB)) {
Object valueB = entryB.getValue();
if (pigeonDeepEquals(valueA, valueB)) {
found = true;
break;
} else {
return false;
}
}
}
if (!found) {
return false;
}
}
return true;
}
if (a instanceof Double && b instanceof Double) {
return pigeonDoubleEquals((double) a, (double) b);
}
if (a instanceof Float && b instanceof Float) {
return pigeonFloatEquals((float) a, (float) b);
}
return a.equals(b);
}

static int pigeonDeepHashCode(Object value) {
if (value == null) { return 0; }
if (value instanceof byte[]) {
return Arrays.hashCode((byte[]) value);
}
if (value instanceof int[]) {
return Arrays.hashCode((int[]) value);
}
if (value instanceof long[]) {
return Arrays.hashCode((long[]) value);
}
if (value instanceof double[]) {
double[] da = (double[]) value;
int result = 1;
for (double d : da) {
result = 31 * result + pigeonDoubleHashCode(d);
}
return result;
}
if (value instanceof List) {
int result = 1;
for (Object item : (List<?>) value) {
result = 31 * result + pigeonDeepHashCode(item);
}
return result;
}
if (value instanceof Map) {
int result = 0;
for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
result += ((pigeonDeepHashCode(entry.getKey()) * 31) ^ pigeonDeepHashCode(entry.getValue()));
}
return result;
}
if (value instanceof Object[]) {
int result = 1;
for (Object item : (Object[]) value) {
result = 31 * result + pigeonDeepHashCode(item);
}
return result;
}
if (value instanceof Double) {
return pigeonDoubleHashCode((double) value);
}
if (value instanceof Float) {
return pigeonFloatHashCode((float) value);
}
return value.hashCode();
}


/** Error class for passing custom error details to Flutter via a thrown PlatformException. */
public static class FlutterError extends RuntimeException {

Expand Down Expand Up @@ -72,12 +220,18 @@ public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
CastSessionUnavailableEvent that = (CastSessionUnavailableEvent) o;
return Objects.equals(playbackPositionMs, that.playbackPositionMs);
return pigeonDeepEquals(playbackPositionMs, that.playbackPositionMs);
}

@Override
public int hashCode() {
return Objects.hash(playbackPositionMs);
Object[] fields = new Object[] {getClass(), playbackPositionMs};
return pigeonDeepHashCode(fields);
}

@Override
public String toString() {
return "CastSessionUnavailableEvent{" + "playbackPositionMs=" + playbackPositionMs + "}";
}

public static final class Builder {
Expand Down
Loading
Loading