| // 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 'package:analyzer/dart/analysis/analysis_context_collection.dart'; |
| import 'package:analyzer/dart/analysis/results.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:path/path.dart' as p; |
| import 'package:pub/src/platform_info.dart'; |
| import 'package:source_span/source_span.dart'; |
| import 'package:test/test.dart'; |
| |
| void main() { |
| test('overriding works', () async { |
| final originalOS = platform.operatingSystem; |
| final fakePlatform = PlatformInfo.override( |
| environment: {'FOO': 'BAR'}, |
| executable: 'dart_fake', |
| isAndroid: false, |
| isFuchsia: false, |
| isIOS: false, |
| isLinux: false, |
| isMacOS: false, |
| isWindows: true, |
| lineTerminator: '\r\n', |
| operatingSystem: 'windows', |
| pathSeparator: '\\', |
| resolvedExecutable: 'C:\\bin\\dart_fake', |
| version: '3.0.0-fake', |
| script: Uri.file('C:\\bin\\dart_fake.dart'), |
| numberOfProcessors: 2, |
| ); |
| |
| await withPlatform(() async { |
| expect(platform.operatingSystem, 'windows'); |
| expect(platform.isWindows, isTrue); |
| expect(platform.isLinux, isFalse); |
| expect(platform.environment['FOO'], 'BAR'); |
| expect(platform.executable, 'dart_fake'); |
| expect(platform.pathSeparator, '\\'); |
| }, platform: fakePlatform); |
| |
| expect(platform.operatingSystem, originalOS); |
| }, testOn: 'vm'); |
| |
| test('overriding works (also in browser)', () async { |
| final fakePlatform = PlatformInfo.override( |
| environment: {'FOO': 'BAR'}, |
| executable: 'dart_fake', |
| isAndroid: false, |
| isFuchsia: false, |
| isIOS: false, |
| isLinux: false, |
| isMacOS: false, |
| isWindows: true, |
| lineTerminator: '\r\n', |
| operatingSystem: 'windows', |
| pathSeparator: '\\', |
| resolvedExecutable: 'C:\\bin\\dart_fake', |
| version: '3.0.0-fake', |
| script: Uri.file('C:\\bin\\dart_fake.dart'), |
| numberOfProcessors: 2, |
| ); |
| |
| await withPlatform(() async { |
| expect(platform.operatingSystem, 'windows'); |
| expect(platform.isWindows, isTrue); |
| expect(platform.isLinux, isFalse); |
| expect(platform.environment['FOO'], 'BAR'); |
| expect(platform.executable, 'dart_fake'); |
| expect(platform.pathSeparator, '\\'); |
| }, platform: fakePlatform); |
| }); |
| |
| test('Platform is not used outside platform_info.dart', () async { |
| // This test exists to ensure that we don't use Platform from dart:io |
| // unintentionally. We only want to use it in lib/src/platform_info.dart! |
| // Everywhere else we should rely on `platform` from here. |
| // This way, we can overrride the platform when we need to. |
| final allowListedFiles = [ |
| 'lib/src/platform_info.dart', |
| 'test/platform_info_test.dart', |
| ]; |
| |
| final root = p.normalize(p.absolute('.')); |
| final collection = AnalysisContextCollection( |
| includedPaths: [p.join(root, 'lib')], |
| ); |
| |
| for (final context in collection.contexts) { |
| for (final filePath in context.contextRoot.analyzedFiles()) { |
| if (!filePath.endsWith('.dart')) continue; |
| |
| // Skip allow listed files |
| if (allowListedFiles.contains(p.relative(filePath, from: root))) { |
| continue; |
| } |
| |
| final result = await context.currentSession.getResolvedUnit(filePath); |
| if (result is ResolvedUnitResult) { |
| SourceSpan? first; |
| result.unit.accept( |
| ForEachIdentifier((element) { |
| if (first == null && |
| element.element?.name == 'Platform' && |
| element.element?.library?.name == 'dart.io') { |
| first = SourceFile.fromString( |
| result.content, |
| url: filePath, |
| ).span(element.offset, element.end); |
| } |
| }), |
| ); |
| if (first != null) { |
| fail( |
| first!.message( |
| 'Found Platform usage from dart:io, ' |
| 'use lib/src/platform_info.dart instead.', |
| ), |
| ); |
| } |
| } |
| } |
| } |
| }, testOn: 'vm && linux'); |
| } |
| |
| final class ForEachIdentifier extends GeneralizingAstVisitor<void> { |
| final void Function(Identifier element) _visitIdentifier; |
| ForEachIdentifier(this._visitIdentifier); |
| |
| @override |
| void visitComment(Comment node) { |
| // Do not walk into comments! They are allowed to reference Platform! |
| } |
| |
| @override |
| void visitIdentifier(Identifier element) { |
| _visitIdentifier(element); |
| super.visitIdentifier(element); |
| } |
| } |