diff --git a/dwds/test/fixtures/test_sdk_layout.dart b/dwds/test/fixtures/test_sdk_layout.dart deleted file mode 100644 index 488fdd009..000000000 --- a/dwds/test/fixtures/test_sdk_layout.dart +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// 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. - -// TODO:(annagrin) Move to a test_common package. -import 'package:dwds/src/utilities/sdk_configuration.dart'; -import 'package:path/path.dart' as p; - -/// Test Dart SDK layout. -/// -/// Contains definition of the default SDK layout required for tests. -/// We keep all the path constants in one place for ease of update. -class TestSdkLayout { - static final defaultSdkDirectory = SdkLayout.defaultSdkDirectory; - static TestSdkLayout defaultSdkLayout = - TestSdkLayout.createDefault(defaultSdkDirectory); - static SdkConfiguration defaultSdkConfiguration = - createConfiguration(defaultSdkLayout); - - factory TestSdkLayout.createDefault(String sdkDirectory) => - TestSdkLayout.createDefaultFromSdkLayout( - SdkLayout.createDefault(sdkDirectory)); - - factory TestSdkLayout.createDefaultFromSdkLayout(SdkLayout sdkLayout) => - TestSdkLayout( - sdkDirectory: sdkLayout.sdkDirectory, - soundSummaryPath: sdkLayout.soundSummaryPath, - soundFullDillPath: p.join( - sdkLayout.sdkDirectory, - 'lib', - '_internal', - 'ddc_platform.dill', - ), - soundJsPath: p.join( - sdkLayout.sdkDirectory, - 'lib', - 'dev_compiler', - 'kernel', - 'amd', - 'dart_sdk.js', - ), - soundJsMapPath: p.join( - sdkLayout.sdkDirectory, - 'lib', - 'dev_compiler', - 'kernel', - 'amd', - 'dart_sdk.js.map', - ), - weakSummaryPath: sdkLayout.weakSummaryPath, - weakFullDillPath: p.join( - sdkLayout.sdkDirectory, - 'lib', - '_internal', - 'ddc_platform_unsound.dill', - ), - weakJsPath: p.join( - sdkLayout.sdkDirectory, - 'lib', - 'dev_compiler', - 'kernel', - 'amd', - 'dart_sdk_unsound.js', - ), - weakJsMapPath: p.join( - sdkLayout.sdkDirectory, - 'lib', - 'dev_compiler', - 'kernel', - 'amd', - 'dart_sdk_unsound.js.map', - ), - requireJsPath: p.join( - sdkLayout.sdkDirectory, - 'lib', - 'dev_compiler', - 'amd', - 'require.js', - ), - stackTraceMapperPath: p.join( - sdkLayout.sdkDirectory, - 'lib', - 'dev_compiler', - 'web', - 'dart_stack_trace_mapper.js', - ), - dartPath: p.join(sdkLayout.sdkDirectory, 'bin', 'dart'), - frontendServerSnapshotPath: p.join( - sdkLayout.sdkDirectory, - 'bin', - 'snapshots', - 'frontend_server.dart.snapshot', - ), - dartdevcSnapshotPath: sdkLayout.dartdevcSnapshotPath, - kernelWorkerSnapshotPath: p.join( - sdkLayout.sdkDirectory, - 'bin', - 'snapshots', - 'kernel_worker.dart.snapshot', - ), - ); - - final String sdkDirectory; - - String get soundJsFileName => p.basename(soundJsPath); - String get soundJsMapFileName => p.basename(soundJsMapPath); - String get soundSummaryFileName => p.basename(soundSummaryPath); - String get soundFullDillFileName => p.basename(soundFullDillPath); - - final String soundJsPath; - final String soundJsMapPath; - final String soundSummaryPath; - final String soundFullDillPath; - - String get weakJsFileName => p.basename(weakJsPath); - String get weakJsMapFileName => p.basename(weakJsMapPath); - String get weakSummaryFileName => p.basename(weakSummaryPath); - String get weakFullDillFileName => p.basename(weakFullDillPath); - - final String weakJsPath; - final String weakJsMapPath; - final String weakSummaryPath; - final String weakFullDillPath; - - final String requireJsPath; - final String stackTraceMapperPath; - - final String dartPath; - final String frontendServerSnapshotPath; - final String dartdevcSnapshotPath; - final String kernelWorkerSnapshotPath; - - const TestSdkLayout({ - required this.sdkDirectory, - required this.soundJsPath, - required this.soundJsMapPath, - required this.soundSummaryPath, - required this.soundFullDillPath, - required this.weakJsPath, - required this.weakJsMapPath, - required this.weakSummaryPath, - required this.weakFullDillPath, - required this.requireJsPath, - required this.stackTraceMapperPath, - required this.dartPath, - required this.frontendServerSnapshotPath, - required this.dartdevcSnapshotPath, - required this.kernelWorkerSnapshotPath, - }); - - /// Creates configuration from sdk layout. - static SdkConfiguration createConfiguration(TestSdkLayout sdkLayout) => - SdkConfiguration( - sdkDirectory: sdkLayout.sdkDirectory, - weakSdkSummaryPath: sdkLayout.weakSummaryPath, - soundSdkSummaryPath: sdkLayout.soundSummaryPath, - compilerWorkerPath: sdkLayout.dartdevcSnapshotPath, - ); -} diff --git a/test_common/lib/sdk_asset_generator.dart b/test_common/lib/sdk_asset_generator.dart index 21bcf55ec..5d6061137 100644 --- a/test_common/lib/sdk_asset_generator.dart +++ b/test_common/lib/sdk_asset_generator.dart @@ -13,7 +13,7 @@ import 'package:test_common/test_sdk_layout.dart'; /// - sound null safety: js, source map, full dill. /// - weak null safety: js, source map, full dill, summary. class SdkAssetGenerator { - static bool _sdkAssetsGenerated = false; + bool _sdkAssetsGenerated = false; final _logger = Logger('SdkAssetGenerator'); final FileSystem fileSystem; @@ -88,10 +88,7 @@ class SdkAssetGenerator { 'org-dartlang-sdk:///lib/libraries.json', '--modules', 'amd', - if (soundNullSafety) - '--sound-null-safety' - else - '--no-sound-null-safety', + soundNullSafety ? '--sound-null-safety' : '--no-sound-null-safety', 'dart:core', '-o', jsPath, @@ -99,7 +96,7 @@ class SdkAssetGenerator { final output = []; _logger.fine('Executing dart ${args.join(' ')}'); - final process = await Process.start(Platform.resolvedExecutable, args, + final process = await Process.start(sdkLayout.dartPath, args, workingDirectory: sdkLayout.sdkDirectory); process.stdout @@ -187,7 +184,7 @@ class SdkAssetGenerator { ]; _logger.fine('Executing dart ${args.join(' ')}'); - final process = await Process.start(Platform.resolvedExecutable, args, + final process = await Process.start(sdkLayout.dartPath, args, workingDirectory: sdkLayout.sdkDirectory); final output = []; diff --git a/test_common/lib/test_sdk_configuration.dart b/test_common/lib/test_sdk_configuration.dart index f93fe691d..dabb616e0 100644 --- a/test_common/lib/test_sdk_configuration.dart +++ b/test_common/lib/test_sdk_configuration.dart @@ -2,12 +2,85 @@ // 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. -// TODO:(annagrin) Move to a test_common package. +import 'dart:io'; + import 'package:dwds/sdk_configuration.dart'; +import 'package:logging/logging.dart'; import 'package:test_common/sdk_asset_generator.dart'; import 'package:test_common/test_sdk_layout.dart'; +/// Implementation for SDK configuration for tests that can generate +/// missing assets. +/// +/// - Generate SDK js, source map, and full dill for weak and sound +/// modes (normally included in flutter SDK or produced by build). +/// - Need to generate SDK summary for weak null safety mode as it +/// is not provided by the SDK installation. +/// +/// TODO(annagrin): update to only generating missing sound artifacts +/// for frontend server after we have no uses of weak null safety. +class TestSdkCopyConfigurationProvider extends SdkConfigurationProvider { + final _logger = Logger('TestSdkConfigurationProvider'); + + final bool _verbose; + late final Directory _sdkDirectory; + SdkConfiguration? _configuration; + + late final TestSdkLayout sdkLayout; + + TestSdkCopyConfigurationProvider({bool verbose = false}) + : _verbose = verbose { + _sdkDirectory = Directory.systemTemp.createTempSync('sdk copy'); + sdkLayout = TestSdkLayout.createDefault(_sdkDirectory.path); + } + + @override + Future get configuration async => + _configuration ??= await _create(); + + /// Generate missing assets in the default SDK layout. + /// + /// Creates a copy of the SDK directory where all the missing assets + /// are generated. Tests using this configuration run using the copy + /// sdk layout to make sure the actual SDK is not modified. + Future _create() async { + try { + await copyDirectory( + TestSdkLayout.defaultSdkDirectory, _sdkDirectory.path); + } catch (e, s) { + _logger.severe('Failed to create SDK directory copy', e, s); + dispose(); + rethrow; + } + + try { + final assetGenerator = SdkAssetGenerator( + sdkLayout: sdkLayout, + verboseCompiler: _verbose, + ); + + await assetGenerator.generateSdkAssets(); + return TestSdkLayout.createConfiguration(sdkLayout); + } catch (e, s) { + _logger.severe('Failed generate missing assets', e, s); + dispose(); + rethrow; + } + } + + void dispose({bool retry = true}) { + try { + if (_sdkDirectory.existsSync()) { + _sdkDirectory.deleteSync(recursive: true); + } + } catch (e, s) { + _logger.warning('Failed delete SDK directory copy', e, s); + dispose(retry: false); + } + } +} + /// Implementation for SDK configuration for tests that can generate /// missing assets. /// diff --git a/test_common/lib/test_sdk_layout.dart b/test_common/lib/test_sdk_layout.dart index 78a00c1d4..072bd3f1f 100644 --- a/test_common/lib/test_sdk_layout.dart +++ b/test_common/lib/test_sdk_layout.dart @@ -2,6 +2,8 @@ // 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:io'; + import 'package:dwds/sdk_configuration.dart'; import 'package:path/path.dart' as p; @@ -83,7 +85,11 @@ class TestSdkLayout { 'web', 'dart_stack_trace_mapper.js', ), - dartPath: p.join(sdkLayout.sdkDirectory, 'bin', 'dart'), + dartPath: p.join( + sdkLayout.sdkDirectory, + 'bin', + Platform.isWindows ? 'dart.exe' : 'dart', + ), frontendServerSnapshotPath: p.join( sdkLayout.sdkDirectory, 'bin', @@ -97,6 +103,12 @@ class TestSdkLayout { 'snapshots', 'kernel_worker.dart.snapshot', ), + devToolsDirectory: p.join( + sdkLayout.sdkDirectory, + 'bin', + 'resources', + 'devtools', + ), ); final String sdkDirectory; @@ -128,6 +140,7 @@ class TestSdkLayout { final String frontendServerSnapshotPath; final String dartdevcSnapshotPath; final String kernelWorkerSnapshotPath; + final String devToolsDirectory; const TestSdkLayout({ required this.sdkDirectory, @@ -145,6 +158,7 @@ class TestSdkLayout { required this.frontendServerSnapshotPath, required this.dartdevcSnapshotPath, required this.kernelWorkerSnapshotPath, + required this.devToolsDirectory, }); /// Creates configuration from sdk layout. @@ -156,3 +170,20 @@ class TestSdkLayout { compilerWorkerPath: sdkLayout.dartdevcSnapshotPath, ); } + +// Update modified files. +Future copyDirectory(String from, String to) async { + if (!Directory(from).existsSync()) return; + await Directory(to).create(recursive: true); + + await for (final file in Directory(from).list(followLinks: false)) { + final copyTo = p.join(to, p.relative(file.path, from: from)); + if (file is Directory) { + await copyDirectory(file.path, copyTo); + } else if (file is File) { + await File(file.path).copy(copyTo); + } else if (file is Link) { + await Link(copyTo).create(await file.target(), recursive: true); + } + } +} diff --git a/test_common/test/sdk_asset_generator_test.dart b/test_common/test/sdk_asset_generator_test.dart index 5a7cad7aa..5c54ceadd 100644 --- a/test_common/test/sdk_asset_generator_test.dart +++ b/test_common/test/sdk_asset_generator_test.dart @@ -7,7 +7,6 @@ import 'dart:io'; -import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:test_common/logging.dart'; import 'package:test_common/sdk_asset_generator.dart'; @@ -44,7 +43,7 @@ void main() { compilerWorkerPath = copySdkLayout.dartdevcSnapshotPath; // Copy the SDK directory into a temp directory. - await _copy(TestSdkLayout.defaultSdkDirectory, sdkDirectory); + await copyDirectory(TestSdkLayout.defaultSdkDirectory, sdkDirectory); // Simulate missing sound assets. soundSdkFullDillPath = copySdkLayout.soundFullDillPath; @@ -112,7 +111,8 @@ void main() { }); } -Matcher _exists = predicate((String path) => File(path).existsSync()); +Matcher _exists = + predicate((String path) => File(path).existsSync(), 'File exists'); void _deleteIfExists(String path) { final file = File(path); @@ -120,20 +120,3 @@ void _deleteIfExists(String path) { file.deleteSync(); } } - -// Update modified files. -Future _copy(String from, String to) async { - if (!Directory(from).existsSync()) return; - await Directory(to).create(recursive: true); - - await for (final file in Directory(from).list()) { - final copyTo = p.join(to, p.relative(file.path, from: from)); - if (file is Directory) { - await _copy(file.path, copyTo); - } else if (file is File) { - await File(file.path).copy(copyTo); - } else if (file is Link) { - await Link(copyTo).create(await file.target(), recursive: true); - } - } -} diff --git a/test_common/test/test_sdk_configuration_test.dart b/test_common/test/test_sdk_configuration_test.dart new file mode 100644 index 000000000..bbb88dd08 --- /dev/null +++ b/test_common/test/test_sdk_configuration_test.dart @@ -0,0 +1,85 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// 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. + +@TestOn('vm') +@Timeout(Duration(minutes: 2)) + +import 'dart:io'; + +import 'package:test/test.dart'; +import 'package:test_common/logging.dart'; +import 'package:test_common/test_sdk_configuration.dart'; + +void main() { + final debug = false; + + group('Test SDK configuration provider |', () { + setUpAll(() { + setCurrentLogWriter(debug: debug); + }); + + test('Creates and deletes SDK directory copy', () async { + final provider = TestSdkCopyConfigurationProvider(verbose: debug); + final sdkDirectory = provider.sdkLayout.sdkDirectory; + final weakSdkSummary = provider.sdkLayout.weakSummaryPath; + try { + expect(sdkDirectory, _directoryExists, + reason: 'SDK directory should be created'); + expect(weakSdkSummary, isNot(_fileExists), + reason: 'Weak SDK summary should not be generated yet.'); + + await provider.configuration; + expect(weakSdkSummary, _fileExists, + reason: 'Weak SDK summary should be generated'); + } finally { + provider.dispose(); + expect(sdkDirectory, isNot(_directoryExists), + reason: 'SDK directory copy should be deleted on dispose'); + } + }); + }); + + group('Test SDK configuration |', () { + setCurrentLogWriter(debug: debug); + final provider = TestSdkCopyConfigurationProvider(verbose: debug); + tearDownAll(provider.dispose); + + test('Can validate configuration with generated assets', () async { + final sdkConfiguration = await provider.configuration; + sdkConfiguration.validateSdkDir(); + sdkConfiguration.validate(); + }); + + test('SDK layout exists', () async { + await provider.configuration; + final sdkLayout = provider.sdkLayout; + + expect(sdkLayout.sdkDirectory, _directoryExists); + expect(sdkLayout.soundJsPath, _fileExists); + expect(sdkLayout.soundJsMapPath, _fileExists); + expect(sdkLayout.soundSummaryPath, _fileExists); + expect(sdkLayout.soundFullDillPath, _fileExists); + + expect(sdkLayout.weakJsPath, _fileExists); + expect(sdkLayout.weakJsMapPath, _fileExists); + expect(sdkLayout.weakSummaryPath, _fileExists); + expect(sdkLayout.weakFullDillPath, _fileExists); + + expect(sdkLayout.requireJsPath, _fileExists); + expect(sdkLayout.stackTraceMapperPath, _fileExists); + + expect(sdkLayout.dartPath, _fileExists); + expect(sdkLayout.frontendServerSnapshotPath, _fileExists); + expect(sdkLayout.dartdevcSnapshotPath, _fileExists); + expect(sdkLayout.kernelWorkerSnapshotPath, _fileExists); + expect(sdkLayout.devToolsDirectory, _directoryExists); + }); + }); +} + +Matcher _fileExists = + predicate((String path) => File(path).existsSync(), 'File exists'); + +Matcher _directoryExists = predicate( + (String path) => Directory(path).existsSync(), 'Directory exists');