blob: f45adff2563ea9c112b5c577caefea84247d273a [file] [log] [blame] [edit]
// Copyright (c) 2025, 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:convert';
import 'dart:io' as io;
import 'package:code_assets/code_assets.dart' show OS;
import 'package:dartdev/src/sdk_cache.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:hooks_runner/hooks_runner.dart' show Target;
import 'package:http/http.dart' as http;
import 'package:http/testing.dart' as http;
import 'package:test/test.dart';
const dartArchiveUri = 'https://storage.googleapis.com/dart-archive';
void main() {
late SdkCache cache;
late FileSystem fs;
late StringBuffer stderr;
late Map<String, http.Response> expectedRequests;
late Map<String, io.ProcessResult> chmodRuns;
setUp(() {
// Make sure we support both path separators.
fs = MemoryFileSystem(
style: io.Platform.isWindows
? FileSystemStyle.windows
: FileSystemStyle.posix);
stderr = StringBuffer();
expectedRequests = <String, http.Response>{};
chmodRuns = <String, io.ProcessResult>{};
cache = SdkCache(
directory: fs.directory(Uri.file('/tmp/cache')).path,
stderr: stderr,
verbose: true,
fs: fs,
httpClient: http.MockClient((request) async {
final key = '${request.method.toUpperCase()} ${request.url}';
if (!expectedRequests.containsKey(key)) {
throw Exception('Unexpected request $key');
}
return expectedRequests[key]!;
}),
chmod: (path) => chmodRuns[path]!);
});
group('resolveStage', () {
test('Uses signed on macOS dev for executables', () {
expect(
SdkCache.resolveStage(
channel: Channel.dev, isExecutable: true, hostOS: OS.macOS),
Stage.signed);
});
test('Uses raw on macOS main for executables', () {
expect(
SdkCache.resolveStage(
channel: Channel.main, isExecutable: true, hostOS: OS.macOS),
Stage.raw);
});
test('Uses raw on macOS stable for non-executables', () {
expect(
SdkCache.resolveStage(
channel: Channel.stable, isExecutable: false, hostOS: OS.macOS),
Stage.raw);
});
});
group('resolveVersion', () {
test('Resolves release version', () async {
final version = '3.4.4';
final revision = '60465149414572c8ca189d8f65fdb39795c4b97d';
final folder = await cache.resolveVersion(
version: version,
revision: revision,
channelName: 'stable',
host: Target.linuxArm64);
expect(folder.version, version);
expect(folder.revision, revision);
expect(folder.channel, Channel.stable);
expect(folder.fileUri('VERSION', stage: Stage.raw).toString(),
'$dartArchiveUri/channels/stable/raw/hash/$revision/VERSION');
});
test('Falls back to the latest on empty main revision', () async {
final version = '3.8.0';
final revision = '';
final latestVersion =
'3.8.0-edge.7ee7416a62efb7eb23eb2a87eb34d2895559ba06';
final latestRevision = '7ee7416a62efb7eb23eb2a87eb34d2895559ba06';
expectedRequests['GET $dartArchiveUri/channels/main/raw/latest/VERSION'] =
http.Response(
json.encode(
{'version': latestVersion, 'revision': latestRevision}),
io.HttpStatus.ok);
final folder = await cache.resolveVersion(
version: version,
revision: revision,
channelName: 'main',
host: Target.macOSArm64);
expect(folder.version, latestVersion);
expect(folder.revision, latestRevision);
expect(folder.channel, Channel.main);
});
test('Falls back to the latest on unknown main revision', () async {
final version = '3.8.0';
final revision = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
final latestVersion =
'3.8.0-edge.7ee7416a62efb7eb23eb2a87eb34d2895559ba06';
final latestRevision = '7ee7416a62efb7eb23eb2a87eb34d2895559ba06';
expectedRequests.addAll({
// Revision validation request.
'HEAD $dartArchiveUri/channels/main/raw/hash/$revision/VERSION':
http.Response('', io.HttpStatus.notFound),
// Revision resolution request.
'GET $dartArchiveUri/channels/main/raw/latest/VERSION': http.Response(
json.encode({'version': latestVersion, 'revision': latestRevision}),
io.HttpStatus.ok)
});
final folder = await cache.resolveVersion(
version: version,
revision: revision,
channelName: 'main',
host: Target.macOSArm64);
expect(folder.version, latestVersion);
expect(folder.revision, latestRevision);
expect(folder.channel, Channel.main);
});
test('Reports unknown channel', () async {
try {
await cache.resolveVersion(
channelName: 'wat',
host: Target.linuxArm64,
revision: '',
version: '4.0');
fail('expected to throw');
} on ArgumentError catch (e) {
expect(e.message, contains('Unsupported channel'));
}
});
});
group('ensureArtifact', () {
test('Skips download when file exists', () async {
final version = '3.4.4';
final revision = '60465149414572c8ca189d8f65fdb39795c4b97d';
final genSnapshotFile = fs.file(Uri.file(
'/tmp/cache/$version/gen_snapshot_windows_arm64_linux_x64.exe'));
genSnapshotFile.createSync(exclusive: true, recursive: true);
final path = await cache.ensureGenSnapshot(
archiveFolder: ArchiveFolder(
channel: Channel.stable, version: version, revision: revision),
host: Target.windowsArm64,
target: Target.linuxX64);
expect(path, genSnapshotFile.path);
});
test('Downloads signed gen_snapshot on macOS beta', () async {
final version = '3.8.0-171.2.beta';
final revision = '54cec4d7d36e7a5066770287998f425606a2f983';
final archiveFolder = ArchiveFolder(
channel: Channel.beta, version: version, revision: revision);
expectedRequests['GET $dartArchiveUri/channels/beta/signed/hash/'
'$revision/sdk/gen_snapshot_macos_arm64_linux_x64'] =
http.Response('i am gen_snapshot', io.HttpStatus.ok);
final path = await cache.ensureGenSnapshot(
archiveFolder: archiveFolder,
host: Target.macOSArm64,
target: Target.linuxX64);
expect(
path,
fs
.file(Uri.file(
'/tmp/cache/$version/gen_snapshot_macos_arm64_linux_x64'))
.path);
expect(fs.file(path).readAsStringSync(), 'i am gen_snapshot');
});
test('Downloads raw dartaotruntime on macOS beta', () async {
final version = '3.8.0-171.2.beta';
final revision = '54cec4d7d36e7a5066770287998f425606a2f983';
final archiveFolder = ArchiveFolder(
channel: Channel.beta, version: version, revision: revision);
expectedRequests['GET $dartArchiveUri/channels/beta/raw/hash/'
'$revision/sdk/dartaotruntime_linux_x64'] =
http.Response('i am dartaotruntime', io.HttpStatus.ok);
final path = await cache.ensureDartAotRuntime(
archiveFolder: archiveFolder,
host: Target.macOSArm64,
target: Target.linuxX64);
expect(
path,
fs
.file(Uri.file('/tmp/cache/$version/dartaotruntime_linux_x64'))
.path);
expect(fs.file(path).readAsStringSync(), 'i am dartaotruntime');
});
test('Downloads gen_snapshot.exe on Windows stable', () async {
final version = '3.7.2';
final channel = Channel.stable;
final revision = '9594995093f642957b780603c6435d9e7a61b923';
final binary = 'gen_snapshot_windows_x64_linux_x64.exe';
final archiveFolder =
ArchiveFolder(channel: channel, version: version, revision: revision);
expectedRequests['GET $dartArchiveUri/channels/stable/raw/hash/'
'$revision/sdk/$binary'] =
http.Response('i am gen_snapshot', io.HttpStatus.ok);
final path = await cache.ensureGenSnapshot(
archiveFolder: archiveFolder,
host: Target.windowsX64,
target: Target.linuxX64);
expect(path, fs.file(Uri.file('/tmp/cache/$version/$binary')).path);
expect(fs.file(path).readAsStringSync(), 'i am gen_snapshot');
});
test('Downloads dartaotruntime on Windows stable', () async {
final version = '3.7.2';
final channel = Channel.stable;
final revision = '9594995093f642957b780603c6435d9e7a61b923';
final binary = 'dartaotruntime_linux_x64';
final archiveFolder =
ArchiveFolder(channel: channel, version: version, revision: revision);
expectedRequests['GET $dartArchiveUri/channels/stable/raw/hash/'
'$revision/sdk/$binary'] =
http.Response('i am dartaotruntime', io.HttpStatus.ok);
final path = await cache.ensureDartAotRuntime(
archiveFolder: archiveFolder,
host: Target.windowsX64,
target: Target.linuxX64);
expect(path, fs.file(Uri.file('/tmp/cache/$version/$binary')).path);
expect(fs.file(path).readAsStringSync(), 'i am dartaotruntime');
});
});
}