Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions test/util/run_test_in_process.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:spot/src/flutter/flutter_sdk.dart';
import 'package:spot/src/utils/invoker.dart';
import 'package:test/test.dart';
import 'package:test_process/test_process.dart';

Expand Down Expand Up @@ -38,6 +40,7 @@ Future<String?> runTestInProcessAndCaptureOutPut({
arguments,
environment: {
'CI': 'true',
..._ownBuildDirectory(),
},
);
final stdoutBuffer = StringBuffer();
Expand Down Expand Up @@ -81,6 +84,86 @@ Future<String?> runTestInProcessAndCaptureOutPut({
return stdout;
}

/// Environment that sends the nested `flutter test` to a build directory of its
/// own instead of the one this repository is being tested in.
///
/// The nested process inherits the working directory, so without this every run
/// writes into the same `build/`. `flutter test` empties
/// `build/native_assets/<os>/` on startup, writes the `native_assets.json` it
/// copies into the test assets into that same directory, and copies it only
/// after building the asset bundle. A run that starts while another one is
/// between those two steps deletes the file that run is about to copy:
///
/// ```text
/// Flutter failed to copy file from ".../build/native_assets/linux/native_assets.json"
/// to ".../build/unit_test_assets/NativeAssetsManifest.json".
/// The file or directory could not be found.
/// ```
///
/// The build directory is only settable through Flutter's config file, so the
/// nested process gets a config file of its own. Every test file gets one build
/// directory, not every run: runs of one file are sequential and cannot collide,
/// and sharing keeps their compilation cache warm.
Map<String, String> _ownBuildDirectory() {
final configDir = Directory.systemTemp.createTempSync('spot_flutter_config');
addTearDown(() {
_deleteTempDir(configDir);
});

final settings = jsonEncode({'build-dir': _buildDirectoryOfTestFile()});
// Flutter looks for `$HOME/.flutter_settings` first and falls back to
// `$XDG_CONFIG_HOME/settings`, on Windows it reads `$APPDATA/.flutter_settings`.
// Both files exist here and all three variables point at them, so the config
// the nested run finds is this one whichever rule it follows. Pointing only
// `XDG_CONFIG_HOME` here would leave machines that still carry the long
// deprecated `$HOME/.flutter_settings` with the shared build directory.
File('${configDir.path}/settings').writeAsStringSync(settings);
File('${configDir.path}/.flutter_settings').writeAsStringSync(settings);

final pubCache = _pubCache();
return {
'HOME': configDir.path,
'XDG_CONFIG_HOME': configDir.path,
'APPDATA': configDir.path,
if (pubCache != null) 'PUB_CACHE': pubCache,
};
}

/// The package cache this process resolved its own dependencies from.
///
/// It is named explicitly because the nested run looks for it in the home
/// directory it just lost, and would download everything again into the
/// throwaway one.
String? _pubCache() {
final configured = Platform.environment['PUB_CACHE'];
if (configured != null) {
return configured;
}
final home = Platform.environment['HOME'];
if (home == null) {
return null;
}
return '$home/.pub-cache';
}

/// A directory under `build/`, named after the test file that is running.
///
/// The path has to be relative, Flutter rejects an absolute one.
String _buildDirectoryOfTestFile() {
final path = getLiveTest()?.suite.path;
if (path == null) {
// Only null outside a running test, which is not where nested runs start.
return 'build/nested_test/unknown';
}
final current = Directory.current.path;
final relative =
path.startsWith(current) ? path.substring(current.length) : path;
final name = relative
.replaceAll(RegExp('[^A-Za-z0-9]+'), '_')
.replaceFirst(RegExp('^_'), '');
return 'build/nested_test/$name';
}

Future<File> _createTempTestFile(String content) async {
final tempDir = Directory.systemTemp.createTempSync();
final tempTestFile = File('${tempDir.path}/temp_test.dart');
Expand Down
Loading