pipe/test/sdk_test.dart
trvon 70262a8c2f
fix(sdk): refuse an unreadable SDK clearly, and fix the runtime fallback
An SDK directory that exists but cannot be read passed discovery, and the
analyzer then died with a raw FileSystemException stack trace. This
happens for real under macOS privacy protection: a Flutter SDK under
~/Documents stats fine but every open fails, and every app whose
package_config.json names that flutterRoot crashed:

  Unhandled exception:
  FileSystemException(path=.../dart-sdk/lib/_internal/allowed_experiments.json; ...)

The chosen SDK is now probed before the analysis collection is built, and
an unreadable one is a PipeException naming the path, where it was
discovered from, and the --sdk remedy.

The last-resort fallback to the running Dart SDK took the executable's
directory (<sdk>/bin) as the SDK root, which never contains
lib/libraries.json, so it could never succeed. sdkFromExecutable now
maps <sdk>/bin/dart to <sdk>.
2026-09-22 13:36:10 -06:00

61 lines
2.1 KiB
Dart

import 'dart:io';
import 'package:pipe/pipe.dart';
import 'package:pipe/src/sdk.dart';
import 'package:test/test.dart';
/// A directory laid out like a Dart SDK: `bin/dart`, `lib/libraries.json`,
/// `version`.
Directory _fakeSdk(String label) {
final sdk = Directory.systemTemp.createTempSync('pipe-$label');
File('${sdk.path}/version').writeAsStringSync('3.13.0\n');
File('${sdk.path}/lib/libraries.json')
..createSync(recursive: true)
..writeAsStringSync('{}');
File('${sdk.path}/bin/dart').createSync(recursive: true);
return sdk;
}
void main() {
test('an SDK that exists but cannot be read is a clear error', () {
// macOS privacy protection (a Flutter SDK under ~/Documents) lets stat
// succeed while every open fails, so discovery accepted the SDK and the
// analyzer later died with a raw FileSystemException stack trace.
final sdk = _fakeSdk('unreadable-sdk');
final version = File('${sdk.path}/version');
Process.runSync('chmod', ['000', version.path]);
try {
version.readAsStringSync();
markTestSkipped('chmod 000 does not restrict this process');
return;
} on FileSystemException {
// Expected: the restriction is real, so the check below is meaningful.
}
final resolution = resolveSdk(explicit: sdk.path);
expect(
() => requireReadableSdk(resolution),
throwsA(
isA<PipeException>()
.having((e) => e.message, 'message', contains(sdk.path))
.having((e) => e.remedy, 'remedy', contains('--sdk')),
),
);
});
test('a readable SDK passes the check', () {
final sdk = _fakeSdk('readable-sdk');
expect(
() => requireReadableSdk(resolveSdk(explicit: sdk.path)),
returnsNormally,
);
});
test('the running dart binary maps to its SDK root, not to bin/', () {
// dirname(<sdk>/bin/dart) is <sdk>/bin, which never contains
// lib/libraries.json, so this fallback could never succeed.
final sdk = _fakeSdk('runtime-sdk');
expect(sdkFromExecutable('${sdk.path}/bin/dart'), sdk.path);
expect(sdkFromExecutable('${sdk.path}/version'), isNull);
});
}