diff --git a/pkgs/dart_mcp_server/test/dart_tooling_mcp_server_test.dart b/pkgs/dart_mcp_server/test/dart_tooling_mcp_server_test.dart index 8d8a4c2c..607c56e6 100644 --- a/pkgs/dart_mcp_server/test/dart_tooling_mcp_server_test.dart +++ b/pkgs/dart_mcp_server/test/dart_tooling_mcp_server_test.dart @@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:async'; import 'dart:io'; import 'package:test/test.dart'; @@ -23,16 +24,43 @@ void main() { }); test('logs traffic to a file', () async { - expect( - await File(logDescriptor.io.path).readAsLines(), - containsAll([ - allOf(startsWith('<<<'), contains('"method":"initialize"')), - allOf(startsWith('>>>'), contains('"serverInfo"')), - allOf(startsWith('<<<'), contains('"notifications/initialized"')), - ]), + // It may take a bit for all the lines to show up in the log. + await doWithRetries( + () async => expect( + await File(logDescriptor.io.path).readAsLines(), + containsAll([ + allOf(startsWith('<<<'), contains('"method":"initialize"')), + allOf(startsWith('>>>'), contains('"serverInfo"')), + allOf(startsWith('<<<'), contains('"notifications/initialized"')), + ]), + ), ); + // Ensure the file handle is released before the file is cleaned up. await testHarness.serverConnectionPair.serverConnection.shutdown(); + + // Wait for the process to release the file. + await doWithRetries(() => File(logDescriptor.io.path).delete()); }); }); } + +/// Performs [action] up to [maxRetries] times, backing off an extra 50ms +/// between each attempt. +FutureOr doWithRetries( + FutureOr Function() action, { + int maxRetries = 5, +}) async { + var count = 0; + while (true) { + try { + return await action(); + } catch (_) { + if (count == maxRetries) { + rethrow; + } + } + count++; + await Future.delayed(Duration(milliseconds: 50 * count)); + } +}