blob: e125d95ed974c9a60cf671ab05a5ef537054665e [file] [edit]
// Copyright (c) 2026, 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.
import 'dart:async';
import 'dart:io';
import 'package:dwds/asset_reader.dart';
import 'package:dwds/data/build_result.dart';
import 'package:dwds/expression_compiler.dart';
import 'package:dwds/src/loaders/frontend_server_strategy_provider.dart';
import 'package:dwds/src/loaders/strategy.dart';
import 'package:dwds/src/utilities/server.dart';
import 'package:dwds_test_common/fixtures/context.dart';
import 'package:dwds_test_common/fixtures/utilities.dart';
import 'package:dwds_test_common/utilities.dart';
import 'package:file/local.dart';
import 'package:logging/logging.dart' as logging;
import 'package:path/path.dart' as p;
import 'package:shelf/shelf.dart';
import '../../frontend_server_common/asset_server.dart';
import '../../frontend_server_common/resident_runner.dart';
class FrontendServerTestContext extends TestContext {
ResidentWebRunner? _webRunner;
TestAssetServer? _assetReader;
LoadStrategy? _loadStrategy;
ExpressionCompiler? _expressionCompiler;
late LocalFileSystem frontendServerFileSystem;
final _logger = logging.Logger('FrontendServerContext');
FrontendServerTestContext(super.project, super.sdkConfigurationProvider)
: super.protected();
@override
String get appUrlPath =>
webCompatiblePath([project.directoryToServe, project.filePathToServe]);
@override
String get basePath => assetReader.basePath;
@override
bool get usesFrontendServer => true;
ResidentWebRunner get webRunner => _webRunner!;
@override
TestAssetServer get assetReader => _assetReader!;
@override
Handler get assetHandler => assetReader.handleRequest;
@override
LoadStrategy get loadStrategy => _loadStrategy!;
@override
ExpressionCompiler? get expressionCompiler => _expressionCompiler;
@override
Stream<BuildResult> get buildResults => const Stream<BuildResult>.empty();
@override
Future<void> modeSetUp(
TestSettings testSettings,
TestDebugSettings debugSettings,
TestAppMetadata appMetadata,
Uri reloadedSourcesUri,
) async {
final sdkLayout = sdkConfigurationProvider.sdkLayout;
final buildSettings = TestBuildSettings(
appEntrypoint: project.dartEntryFilePackageUri,
canaryFeatures: testSettings.canaryFeatures,
isFlutterApp: testSettings.isFlutterApp,
experiments: testSettings.experiments,
);
final filePathToServe = webCompatiblePath([
project.directoryToServe,
project.filePathToServe,
]);
_logger.info('Serving: $filePathToServe');
final entry = p.toUri(
p.join(project.webAssetsPath, project.dartEntryFileName),
);
frontendServerFileSystem = const LocalFileSystem();
final packageUriMapper = await PackageUriMapper.create(
frontendServerFileSystem,
project.packageConfigFile,
useDebuggerModuleNames: testSettings.useDebuggerModuleNames,
);
final compilerOptions = TestCompilerOptions(
experiments: buildSettings.experiments,
canaryFeatures: buildSettings.canaryFeatures,
moduleFormat: testSettings.moduleFormat,
);
_webRunner = ResidentWebRunner(
mainUri: entry,
urlTunneler: debugSettings.urlEncoder,
projectDirectory: Directory(project.absolutePackageDirectory).uri,
packageConfigFile: project.packageConfigFile,
packageUriMapper: packageUriMapper,
fileSystemRoots: [Directory(project.absolutePackageDirectory).uri],
fileSystemScheme: 'org-dartlang-app',
outputPath: outputDir.path,
compilerOptions: compilerOptions,
sdkLayout: sdkLayout,
verbose: testSettings.verboseCompiler,
);
final assetServerPort = await findUnusedPort();
await webRunner.run(
frontendServerFileSystem,
hostname: appMetadata.hostname,
port: assetServerPort,
index: filePathToServe,
);
if (testSettings.enableExpressionEvaluation) {
_expressionCompiler = webRunner.expressionCompiler;
} else {
_expressionCompiler = null;
}
_assetReader = webRunner.devFS!.assetServer;
_loadStrategy = switch (testSettings.moduleFormat) {
ModuleFormat.amd => FrontendServerRequireStrategyProvider(
testSettings.reloadConfiguration,
assetReader,
packageUriMapper,
() async => {},
buildSettings,
).strategy,
ModuleFormat.ddc =>
buildSettings.canaryFeatures
? FrontendServerDdcLibraryBundleStrategyProvider(
testSettings.reloadConfiguration,
assetReader,
packageUriMapper,
() async => {},
buildSettings,
reloadedSourcesUri: reloadedSourcesUri,
).strategy
: FrontendServerDdcStrategyProvider(
testSettings.reloadConfiguration,
assetReader,
packageUriMapper,
() async => {},
buildSettings,
).strategy,
_ => throw Exception(
'Unsupported DDC module format ${testSettings.moduleFormat.name}.',
),
};
}
@override
Future<void> modeTearDown() async {
await _webRunner?.stop();
_webRunner = null;
_assetReader = null;
_loadStrategy = null;
_expressionCompiler = null;
}
@override
Future<void> recompile({required bool fullRestart}) async {
await webRunner.rerun(
fullRestart: fullRestart,
fileServerUri: Uri.parse('http://${testServer.host}:${testServer.port}'),
);
}
}