@@ -197,6 +197,285 @@ void main() {
197197 await client.shutdown ();
198198 },
199199 );
200+
201+ test.test ('launch_app tool fails when process exits early' , () async {
202+ final mockProcessManager = MockProcessManager ();
203+ mockProcessManager.addCommand (
204+ Command (
205+ [
206+ Platform .isWindows
207+ ? r'C:\path\to\flutter\sdk\bin\cache\dart-sdk\bin\dart.exe'
208+ : '/path/to/flutter/sdk/bin/cache/dart-sdk/bin/dart' ,
209+ 'language-server' ,
210+ '--protocol' ,
211+ 'lsp' ,
212+ ],
213+ stdout:
214+ '''Content-Length: 145\r\n\r\n {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}},"workspaceSymbolProvider":true}}}''' ,
215+ ),
216+ );
217+ mockProcessManager.addCommand (
218+ Command (
219+ [
220+ Platform .isWindows
221+ ? r'C:\path\to\flutter\sdk\bin\flutter.bat'
222+ : '/path/to/flutter/sdk/bin/flutter' ,
223+ 'run' ,
224+ '--print-dtd' ,
225+ '--device-id' ,
226+ 'test-device' ,
227+ ],
228+ stderr: 'Something went wrong' ,
229+ exitCode: Future .value (1 ),
230+ ),
231+ );
232+ final serverAndClient = await createServerAndClient (
233+ processManager: mockProcessManager,
234+ fileSystem: fileSystem,
235+ );
236+ final server = serverAndClient.server;
237+ final client = serverAndClient.client;
238+
239+ // Initialize
240+ await client.initialize (
241+ InitializeRequest (
242+ protocolVersion: ProtocolVersion .latestSupported,
243+ capabilities: ClientCapabilities (),
244+ clientInfo: Implementation (name: 'test_client' , version: '1.0.0' ),
245+ ),
246+ );
247+ client.notifyInitialized ();
248+
249+ // Call the tool
250+ final result = await client.callTool (
251+ CallToolRequest (
252+ name: 'launch_app' ,
253+ arguments: {'root' : projectRoot, 'device' : 'test-device' },
254+ ),
255+ );
256+
257+ test.expect (result.isError, true );
258+ final textOutput = result.content as List <TextContent >;
259+ test.expect (
260+ textOutput.map ((context) => context.text).toList ().join ('\n ' ),
261+ test.stringContainsInOrder ([
262+ 'Flutter application exited with code 1 before the DTD URI was found' ,
263+ 'with log output' ,
264+ 'Something went wrong' ,
265+ ]),
266+ );
267+ await server.shutdown ();
268+ await client.shutdown ();
269+ });
270+
271+ test.test ('stop_app tool stops a running app' , () async {
272+ final dtdUri = 'ws://127.0.0.1:12345/abcdefg=' ;
273+ final processPid = 54321 ;
274+ final mockProcessManager = MockProcessManager ();
275+ mockProcessManager.addCommand (
276+ Command (
277+ [
278+ Platform .isWindows
279+ ? r'C:\path\to\flutter\sdk\bin\cache\dart-sdk\bin\dart.exe'
280+ : '/path/to/flutter/sdk/bin/cache/dart-sdk/bin/dart' ,
281+ 'language-server' ,
282+ '--protocol' ,
283+ 'lsp' ,
284+ ],
285+ stdout:
286+ '''Content-Length: 145\r\n\r\n {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}},"workspaceSymbolProvider":true}}}
287+ ''' ,
288+ ),
289+ );
290+ mockProcessManager.addCommand (
291+ Command (
292+ [
293+ Platform .isWindows
294+ ? r'C:\path\to\flutter\sdk\bin\flutter.bat'
295+ : '/path/to/flutter/sdk/bin/flutter' ,
296+ 'run' ,
297+ '--print-dtd' ,
298+ '--device-id' ,
299+ 'test-device' ,
300+ ],
301+ stdout: 'The Dart Tooling Daemon is available at: $dtdUri \n ' ,
302+ pid: processPid,
303+ ),
304+ );
305+ final serverAndClient = await createServerAndClient (
306+ processManager: mockProcessManager,
307+ fileSystem: fileSystem,
308+ );
309+ final server = serverAndClient.server;
310+ final client = serverAndClient.client;
311+
312+ // Initialize and launch the app
313+ await client.initialize (
314+ InitializeRequest (
315+ protocolVersion: ProtocolVersion .latestSupported,
316+ capabilities: ClientCapabilities (),
317+ clientInfo: Implementation (name: 'test_client' , version: '1.0.0' ),
318+ ),
319+ );
320+ client.notifyInitialized ();
321+ await client.callTool (
322+ CallToolRequest (
323+ name: 'launch_app' ,
324+ arguments: {'root' : projectRoot, 'device' : 'test-device' },
325+ ),
326+ );
327+
328+ // Stop the app
329+ final result = await client.callTool (
330+ CallToolRequest (name: 'stop_app' , arguments: {'pid' : processPid}),
331+ );
332+
333+ test.expect (result.isError, test.isNot (true ));
334+ test.expect (result.structuredContent, {'success' : true });
335+ test.expect (mockProcessManager.killedPids, [processPid]);
336+ await server.shutdown ();
337+ await client.shutdown ();
338+ });
339+
340+ test.test ('get_app_logs tool respects maxLines' , () async {
341+ final dtdUri = 'ws://127.0.0.1:12345/abcdefg=' ;
342+ final processPid = 54321 ;
343+ final mockProcessManager = MockProcessManager ();
344+ mockProcessManager.addCommand (
345+ Command (
346+ [
347+ Platform .isWindows
348+ ? r'C:\path\to\flutter\sdk\bin\cache\dart-sdk\bin\dart.exe'
349+ : '/path/to/flutter/sdk/bin/cache/dart-sdk/bin/dart' ,
350+ 'language-server' ,
351+ '--protocol' ,
352+ 'lsp' ,
353+ ],
354+ stdout:
355+ '''Content-Length: 145\r\n\r\n {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}},"workspaceSymbolProvider":true}}}''' ,
356+ ),
357+ );
358+ mockProcessManager.addCommand (
359+ Command (
360+ [
361+ Platform .isWindows
362+ ? r'C:\path\to\flutter\sdk\bin\flutter.bat'
363+ : '/path/to/flutter/sdk/bin/flutter' ,
364+ 'run' ,
365+ '--print-dtd' ,
366+ '--device-id' ,
367+ 'test-device' ,
368+ ],
369+ stdout:
370+ 'line 1\n line 2\n line 3\n '
371+ 'The Dart Tooling Daemon is available at: $dtdUri \n ' ,
372+ pid: processPid,
373+ ),
374+ );
375+ final serverAndClient = await createServerAndClient (
376+ processManager: mockProcessManager,
377+ fileSystem: fileSystem,
378+ );
379+ final server = serverAndClient.server;
380+ final client = serverAndClient.client;
381+
382+ // Initialize and launch the app
383+ await client.initialize (
384+ InitializeRequest (
385+ protocolVersion: ProtocolVersion .latestSupported,
386+ capabilities: ClientCapabilities (),
387+ clientInfo: Implementation (name: 'test_client' , version: '1.0.0' ),
388+ ),
389+ );
390+ client.notifyInitialized ();
391+ await client.callTool (
392+ CallToolRequest (
393+ name: 'launch_app' ,
394+ arguments: {'root' : projectRoot, 'device' : 'test-device' },
395+ ),
396+ );
397+
398+ // Get the logs
399+ final result = await client.callTool (
400+ CallToolRequest (
401+ name: 'get_app_logs' ,
402+ arguments: {'pid' : processPid, 'maxLines' : 2 },
403+ ),
404+ );
405+
406+ test.expect (result.isError, test.isNot (true ));
407+ test.expect (result.structuredContent, {
408+ 'logs' : [
409+ '[skipping 2 log lines]...' ,
410+ '[stdout] line 3' ,
411+ '[stdout] The Dart Tooling Daemon is available at: ws://127.0.0.1:12345/abcdefg=' ,
412+ ],
413+ });
414+ await server.shutdown ();
415+ await client.shutdown ();
416+ });
417+
418+ test.test ('list_devices tool returns available devices' , () async {
419+ final mockProcessManager = MockProcessManager ();
420+ mockProcessManager.addCommand (
421+ Command (
422+ [
423+ Platform .isWindows
424+ ? r'C:\path\to\flutter\sdk\bin\cache\dart-sdk\bin\dart.exe'
425+ : '/path/to/flutter/sdk/bin/cache/dart-sdk/bin/dart' ,
426+ 'language-server' ,
427+ '--protocol' ,
428+ 'lsp' ,
429+ ],
430+ stdout:
431+ '''Content-Length: 145\r\n\r\n {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}},"workspaceSymbolProvider":true}}}
432+ ''' ,
433+ ),
434+ );
435+ mockProcessManager.addCommand (
436+ Command (
437+ [
438+ Platform .isWindows
439+ ? r'C:\path\to\flutter\sdk\bin\flutter.bat'
440+ : '/path/to/flutter/sdk/bin/flutter' ,
441+ 'devices' ,
442+ '--machine' ,
443+ ],
444+ stdout: jsonEncode ([
445+ {'id' : 'test-device-1' },
446+ {'id' : 'test-device-2' },
447+ ]),
448+ ),
449+ );
450+ final serverAndClient = await createServerAndClient (
451+ processManager: mockProcessManager,
452+ fileSystem: fileSystem,
453+ );
454+ final server = serverAndClient.server;
455+ final client = serverAndClient.client;
456+
457+ // Initialize
458+ await client.initialize (
459+ InitializeRequest (
460+ protocolVersion: ProtocolVersion .latestSupported,
461+ capabilities: ClientCapabilities (),
462+ clientInfo: Implementation (name: 'test_client' , version: '1.0.0' ),
463+ ),
464+ );
465+ client.notifyInitialized ();
466+
467+ // List devices
468+ final result = await client.callTool (
469+ CallToolRequest (name: 'list_devices' , arguments: {}),
470+ );
471+
472+ test.expect (result.isError, test.isNot (true ));
473+ test.expect (result.structuredContent, {
474+ 'devices' : ['test-device-1' , 'test-device-2' ],
475+ });
476+ await server.shutdown ();
477+ await client.shutdown ();
478+ });
200479 });
201480}
202481
0 commit comments