Skip to content

Commit 7e66e9f

Browse files
committed
add unit tests for linux plugin
1 parent a327e6d commit 7e66e9f

4 files changed

Lines changed: 829 additions & 3 deletions

File tree

wakelock_plus/lib/src/wakelock_plus_linux_plugin.dart

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class WakelockPlusLinuxPlugin extends WakelockPlusPlatformInterface {
2020
factory WakelockPlusLinuxPlugin({
2121
@visibleForTesting DBusClient? client,
2222
@visibleForTesting DBusRemoteObject? object,
23+
@visibleForTesting Future<String> Function()? appNameGetter,
2324
}) {
2425
final dbusClient = client ?? DBusClient.session();
2526
final remoteObject = object ??
@@ -28,16 +29,25 @@ class WakelockPlusLinuxPlugin extends WakelockPlusPlatformInterface {
2829
name: 'org.freedesktop.portal.Desktop',
2930
path: DBusObjectPath('/org/freedesktop/portal/desktop'),
3031
);
31-
return WakelockPlusLinuxPlugin._internal(dbusClient, remoteObject);
32+
return WakelockPlusLinuxPlugin._internal(
33+
dbusClient,
34+
remoteObject,
35+
appNameGetter,
36+
);
3237
}
3338

34-
WakelockPlusLinuxPlugin._internal(this._client, this._object);
39+
WakelockPlusLinuxPlugin._internal(
40+
this._client,
41+
this._object,
42+
this._appNameGetter,
43+
);
3544

3645
final DBusClient _client;
3746
final DBusRemoteObject _object;
47+
final Future<String> Function()? _appNameGetter;
3848
DBusObjectPath? _requestHandle;
3949

40-
Future<String> get _appName =>
50+
Future<String> get _appName => _appNameGetter?.call() ??
4151
PackageInfo.fromPlatform().then((info) => info.appName);
4252

4353
@override

wakelock_plus/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ dev_dependencies:
3333
sdk: flutter
3434
flutter_lints: ^6.0.0
3535
pigeon: ^26.0.1 # dart run pigeon --input "pigeons/messages.dart"
36+
mockito: ^5.4.4
37+
build_runner: ^2.4.13
3638

3739
# For information on the generic Dart part of this file, see the
3840
# following page: https://dart.dev/tools/pub/pubspec
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
import 'package:dbus/dbus.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
import 'package:mockito/annotations.dart';
4+
import 'package:mockito/mockito.dart';
5+
import 'package:wakelock_plus/src/wakelock_plus_linux_plugin.dart';
6+
import 'package:wakelock_plus_platform_interface/wakelock_plus_platform_interface.dart';
7+
8+
import 'wakelock_plus_linux_plugin_test.mocks.dart';
9+
10+
@GenerateMocks([DBusClient, DBusRemoteObject])
11+
void main() {
12+
TestWidgetsFlutterBinding.ensureInitialized();
13+
14+
group('WakelockPlusLinuxPlugin', () {
15+
late MockDBusClient mockClient;
16+
late MockDBusRemoteObject mockPortalObject;
17+
late WakelockPlusLinuxPlugin plugin;
18+
19+
setUp(() {
20+
mockClient = MockDBusClient();
21+
mockPortalObject = MockDBusRemoteObject();
22+
plugin = WakelockPlusLinuxPlugin(
23+
client: mockClient,
24+
object: mockPortalObject,
25+
appNameGetter: () async => 'TestApp',
26+
);
27+
});
28+
29+
test('registerWith sets instance', () {
30+
WakelockPlusLinuxPlugin.registerWith();
31+
expect(WakelockPlusPlatformInterface.instance,
32+
isA<WakelockPlusLinuxPlugin>());
33+
});
34+
35+
test('uses org.freedesktop.portal.Desktop', () {
36+
final plugin = WakelockPlusLinuxPlugin();
37+
expect(plugin, isNotNull);
38+
});
39+
40+
test('initially disabled', () async {
41+
expect(await plugin.enabled, isFalse);
42+
});
43+
44+
group('enable', () {
45+
test('calls Inhibit with correct parameters', () async {
46+
final mockResponse = DBusMethodSuccessResponse([
47+
DBusObjectPath('/org/freedesktop/portal/desktop/request/1_1/test')
48+
]);
49+
50+
when(mockPortalObject.callMethod(
51+
'org.freedesktop.portal.Inhibit',
52+
'Inhibit',
53+
any,
54+
replySignature: anyNamed('replySignature'),
55+
)).thenAnswer((_) async => mockResponse);
56+
57+
await plugin.toggle(enable: true);
58+
59+
verify(mockPortalObject.callMethod(
60+
'org.freedesktop.portal.Inhibit',
61+
'Inhibit',
62+
argThat(isA<List>()
63+
.having((l) => l.length, 'length', 3)
64+
.having((l) => l[0], 'window', isA<DBusString>())
65+
.having((l) => l[1], 'flags', isA<DBusUint32>())
66+
.having((l) => l[2], 'options', isA<DBusDict>())),
67+
replySignature: DBusSignature('o'),
68+
)).called(1);
69+
70+
expect(await plugin.enabled, isTrue);
71+
});
72+
73+
test('flags are set to 8 (Idle)', () async {
74+
final mockResponse = DBusMethodSuccessResponse([
75+
DBusObjectPath('/org/freedesktop/portal/desktop/request/1_1/test')
76+
]);
77+
78+
when(mockPortalObject.callMethod(
79+
any,
80+
any,
81+
any,
82+
replySignature: anyNamed('replySignature'),
83+
)).thenAnswer((_) async => mockResponse);
84+
85+
await plugin.toggle(enable: true);
86+
87+
final captured = verify(mockPortalObject.callMethod(
88+
any,
89+
any,
90+
captureAny,
91+
replySignature: anyNamed('replySignature'),
92+
)).captured.single as List;
93+
94+
final flags = captured[1] as DBusUint32;
95+
expect(flags.value, equals(8)); // 8 = Idle flag
96+
});
97+
98+
test('includes reason in options', () async {
99+
final mockResponse = DBusMethodSuccessResponse([
100+
DBusObjectPath('/org/freedesktop/portal/desktop/request/1_1/test')
101+
]);
102+
103+
when(mockPortalObject.callMethod(
104+
any,
105+
any,
106+
any,
107+
replySignature: anyNamed('replySignature'),
108+
)).thenAnswer((_) async => mockResponse);
109+
110+
await plugin.toggle(enable: true);
111+
112+
final captured = verify(mockPortalObject.callMethod(
113+
any,
114+
any,
115+
captureAny,
116+
replySignature: anyNamed('replySignature'),
117+
)).captured.single as List;
118+
119+
final options = captured[2] as DBusDict;
120+
expect(options.children.containsKey(DBusString('reason')), isTrue);
121+
});
122+
});
123+
124+
group('disable', () {
125+
test('calls Request.Close and clears state', () async {
126+
final handlePath =
127+
DBusObjectPath('/org/freedesktop/portal/desktop/request/1_1/test');
128+
final mockInhibitResponse = DBusMethodSuccessResponse([handlePath]);
129+
final mockCloseResponse = DBusMethodSuccessResponse([]);
130+
131+
when(mockPortalObject.callMethod(
132+
'org.freedesktop.portal.Inhibit',
133+
'Inhibit',
134+
any,
135+
replySignature: anyNamed('replySignature'),
136+
)).thenAnswer((_) async => mockInhibitResponse);
137+
138+
// Mock the Close call on DBusClient
139+
when(mockClient.callMethod(
140+
destination: 'org.freedesktop.portal.Desktop',
141+
path: handlePath,
142+
interface: 'org.freedesktop.portal.Request',
143+
name: 'Close',
144+
values: [],
145+
replySignature: DBusSignature.empty,
146+
)).thenAnswer((_) async => mockCloseResponse);
147+
148+
// Enable first
149+
await plugin.toggle(enable: true);
150+
expect(await plugin.enabled, isTrue);
151+
152+
// Now disable
153+
await plugin.toggle(enable: false);
154+
expect(await plugin.enabled, isFalse);
155+
156+
// Verify Close was called
157+
verify(mockClient.callMethod(
158+
destination: 'org.freedesktop.portal.Desktop',
159+
path: handlePath,
160+
interface: 'org.freedesktop.portal.Request',
161+
name: 'Close',
162+
values: [],
163+
replySignature: DBusSignature.empty,
164+
)).called(1);
165+
});
166+
167+
test('does nothing if not enabled', () async {
168+
await plugin.toggle(enable: false);
169+
expect(await plugin.enabled, isFalse);
170+
verifyNever(mockPortalObject.callMethod(
171+
any,
172+
any,
173+
any,
174+
replySignature: anyNamed('replySignature'),
175+
));
176+
});
177+
});
178+
179+
group('enabled getter', () {
180+
test('returns false when not enabled', () async {
181+
expect(await plugin.enabled, isFalse);
182+
});
183+
184+
test('returns true after enable', () async {
185+
final mockResponse = DBusMethodSuccessResponse([
186+
DBusObjectPath('/org/freedesktop/portal/desktop/request/1_1/test')
187+
]);
188+
189+
when(mockPortalObject.callMethod(
190+
any,
191+
any,
192+
any,
193+
replySignature: anyNamed('replySignature'),
194+
)).thenAnswer((_) async => mockResponse);
195+
196+
await plugin.toggle(enable: true);
197+
expect(await plugin.enabled, isTrue);
198+
});
199+
});
200+
201+
group('DBusClient reuse', () {
202+
test('uses the same client instance for all operations', () {
203+
final testClient = MockDBusClient();
204+
final plugin = WakelockPlusLinuxPlugin(
205+
client: testClient,
206+
appNameGetter: () async => 'TestApp',
207+
);
208+
209+
// The plugin should use the same client internally
210+
expect(plugin, isNotNull);
211+
});
212+
213+
test('factory constructor creates single client', () {
214+
// This test verifies that the factory constructor doesn't create
215+
// multiple DBusClient instances
216+
final plugin1 = WakelockPlusLinuxPlugin(
217+
appNameGetter: () async => 'TestApp1',
218+
);
219+
final plugin2 = WakelockPlusLinuxPlugin(
220+
appNameGetter: () async => 'TestApp2',
221+
);
222+
223+
// Each plugin should have its own client, but within a plugin,
224+
// the client should be reused
225+
expect(plugin1, isNotNull);
226+
expect(plugin2, isNotNull);
227+
});
228+
});
229+
});
230+
}

0 commit comments

Comments
 (0)