Files
app-flutter/test/palette_test.dart
T
mikkeli 31a844bb94
/ crosscheck (push) Successful in 1s
CI / Analyze & Test (push) Successful in 22s
CI / Debug APK (push) Successful in 39s
Auto dark/bright follows the sun at the tablet's own location
Without a light sensor an app may read (the Redmi Pad SE has none), auto is dark while
the sun is more than 4° below the horizon (lib/theme/sun.dart), at Android's coarse
location, polled and kept across restarts. The tablet's own, not the Orin's GNSS: the
tablet has its own GNSS and cellular. Android's dark theme is the last fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 22:33:43 +09:00

127 lines
4.4 KiB
Dart

import 'dart:async';
import 'package:display/theme/light.dart';
import 'package:display/theme/palette.dart';
import 'package:display/theme/sun.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final dispatcher = TestWidgetsFlutterBinding.instance.platformDispatcher;
tearDown(dispatcher.clearPlatformBrightnessTestValue);
test(
'auto follows Android, the quick switch holds one, and it is kept',
() async {
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
Themes make() =>
Themes('t', Palette.fallbackDark, Palette.fallbackBright, prefs);
final t = make();
var notified = 0;
t.addListener(() => notified++);
expect(t.mode, VariantMode.auto);
dispatcher.platformBrightnessTestValue = Brightness.light;
expect(t.isDark, isFalse);
expect(notified, 1); // the binding tells its observers
t.toggle(); // bright showing: hold dark
expect((t.mode, t.isDark), (VariantMode.dark, true));
dispatcher.platformBrightnessTestValue = Brightness.dark;
dispatcher.platformBrightnessTestValue = Brightness.light;
expect(notified, 2); // fixed: Android's changes are ignored
expect(make().mode, VariantMode.dark); // kept for the next start
},
);
test('the light switch has a gap and a hold', () {
final s = LightSwitch();
final t0 = DateTime(2026);
DateTime at(int sec) => t0.add(Duration(seconds: sec));
expect(s.feed(30, at(0)), isNull); // in the gap: no verdict yet
expect(s.feed(500, at(1)), isFalse); // the first clear reading decides
expect(s.feed(30, at(2)), isFalse); // the gap keeps it
expect(s.feed(5, at(3)), isFalse); // dark, but not for long enough
expect(s.pending, isTrue);
expect(s.feed(200, at(4)), isFalse); // back: the hold restarts
expect(s.pending, isFalse);
s.feed(5, at(10));
expect(s.feed(5, at(14)), isFalse);
expect(s.feed(5, at(15)), isTrue);
});
testWidgets('auto takes the light sensor over Android', (tester) async {
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
tester.platformDispatcher.platformBrightnessTestValue = Brightness.dark;
final t = Themes('t', Palette.fallbackDark, Palette.fallbackBright, prefs);
final levels = StreamController<double>();
t.followLight(levels.stream);
expect(t.isDark, isTrue); // no reading yet: Android's
levels.add(800);
await tester.pump();
expect((t.isDark, t.lux.value), (false, 800));
levels.add(2);
await tester.pump(
const Duration(seconds: 6),
); // no new reading: the timer ends the hold
expect(t.isDark, isTrue);
t.dispose();
});
test(
'the sun: high at noon, below at midnight, sets near the almanac time',
() {
// Tokyo, 2026-06-21: noon (03:00 UTC) about 77°, midnight well below.
expect(
sunElevation(35.68, 139.77, DateTime.utc(2026, 6, 21, 2, 43)),
closeTo(77.7, 1),
);
expect(
sunElevation(35.68, 139.77, DateTime.utc(2026, 6, 21, 15)),
lessThan(-20),
);
// Helsinki, 2026-10-15: sunset 18:14 EEST (15:14 UTC): -0.8° there.
expect(
sunElevation(60.17, 24.94, DateTime.utc(2026, 10, 15, 15, 14)),
closeTo(-0.8, 0.6),
);
},
);
test(
'auto: the sun before Android, the light sensor before the sun',
() async {
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
dispatcher.platformBrightnessTestValue = Brightness.light;
var now = DateTime.utc(2026, 6, 21, 15); // Tokyo midnight
final t = Themes(
't',
Palette.fallbackDark,
Palette.fallbackBright,
prefs,
() => now,
);
expect((t.isDark, t.autoSource), (false, "Android's dark theme"));
t.setPosition(35.68, 139.77);
expect((t.isDark, t.autoSource), (true, 'the sun'));
now = DateTime.utc(2026, 6, 21, 3);
final again = Themes(
't',
Palette.fallbackDark,
Palette.fallbackBright,
prefs,
() => now,
);
expect(again.isDark, isFalse); // position kept; Tokyo noon
t.dispose();
again.dispose();
},
);
}