// Copyright 2019 The Chromium Authors. 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:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/windows/visual_studio.dart'; import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; import '../src/common.dart'; import '../src/context.dart'; class MockPlatform extends Mock implements Platform { @override Map environment = {}; } class MockProcessManager extends Mock implements ProcessManager {} class MockProcessResult extends Mock implements ProcessResult {} void main() { const String programFilesPath = r'C:\Program Files (x86)'; const String visualStudioPath = programFilesPath + r'\Microsoft Visual Studio\2017\Community'; const String vcvarsPath = visualStudioPath + r'\VC\Auxiliary\Build\vcvars64.bat'; const String vswherePath = programFilesPath + r'\Microsoft Visual Studio\Installer\vswhere.exe'; final MockPlatform windowsPlatform = MockPlatform() ..environment['PROGRAMFILES(X86)'] = r'C:\Program Files (x86)\'; MockProcessManager mockProcessManager; final MemoryFileSystem memoryFilesystem = MemoryFileSystem(style: FileSystemStyle.windows); // Sets up the mock environment so that searching for Visual Studio with // exactly the given required components will provide a result. By default it // return a preset installation, but the response can be overridden. void setMockVswhereResponse([List requiredComponents, String response]) { fs.file(vswherePath).createSync(recursive: true); fs.file(vcvarsPath).createSync(recursive: true); final MockProcessResult result = MockProcessResult(); when(result.exitCode).thenReturn(0); when(result.stdout).thenReturn(response == null ? json.encode(>[ { 'installationPath': visualStudioPath, 'displayName': 'Visual Studio Community 2017', 'installationVersion': '15.9.28307.665', 'catalog': { 'productDisplayVersion': '15.9.12', }, } ]) : response); final List requirementArguments = requiredComponents == null ? [] : ['-requires', ...requiredComponents]; when(mockProcessManager.runSync([ vswherePath, '-format', 'json', '-utf8', '-latest', ...?requirementArguments, ])).thenAnswer((Invocation invocation) { return result; }); } // Sets whether or not a vswhere query without components will return an // installation. void setMockIncompleteVisualStudioExists(bool exists) { setMockVswhereResponse(null, exists ? null : '[]'); } // Sets whether or not a vswhere query with the required components will // return an installation. void setMockCompatibleVisualStudioExists(bool exists) { setMockVswhereResponse([ 'Microsoft.Component.MSBuild', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', 'Microsoft.VisualStudio.Component.Windows10SDK.17763', ], exists ? null : '[]'); } group('Visual Studio', () { VisualStudio visualStudio; setUp(() { mockProcessManager = MockProcessManager(); }); testUsingContext('isInstalled returns false when vswhere is missing', () { when(mockProcessManager.runSync(any)) .thenThrow(const ProcessException('vswhere', [])); visualStudio = VisualStudio(); expect(visualStudio.isInstalled, false); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); testUsingContext('vcvarsPath returns null when vswhere is missing', () { when(mockProcessManager.runSync(any)) .thenThrow(const ProcessException('vswhere', [])); visualStudio = VisualStudio(); expect(visualStudio.vcvarsPath, isNull); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); testUsingContext('isInstalled returns false when vswhere returns non-zero', () { when(mockProcessManager.runSync(any)) .thenThrow(const ProcessException('vswhere', [])); final MockProcessResult result = MockProcessResult(); when(result.exitCode).thenReturn(1); when(mockProcessManager.runSync(any)).thenAnswer((Invocation invocation) { return result; }); visualStudio = VisualStudio(); expect(visualStudio.isInstalled, false); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); testUsingContext('isInstalled returns true when VS is present but missing components', () { setMockIncompleteVisualStudioExists(true); setMockCompatibleVisualStudioExists(false); visualStudio = VisualStudio(); expect(visualStudio.isInstalled, true); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); testUsingContext('hasNecessaryComponents returns false when VS is present but missing components', () { setMockIncompleteVisualStudioExists(true); setMockCompatibleVisualStudioExists(false); visualStudio = VisualStudio(); expect(visualStudio.hasNecessaryComponents, false); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); testUsingContext('vcvarsPath returns null when VS is present but missing components', () { setMockIncompleteVisualStudioExists(true); setMockCompatibleVisualStudioExists(false); visualStudio = VisualStudio(); expect(visualStudio.vcvarsPath, isNull); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); testUsingContext('VS metadata is available when VS is present, even if missing components', () { setMockIncompleteVisualStudioExists(true); setMockCompatibleVisualStudioExists(false); visualStudio = VisualStudio(); expect(visualStudio.displayName, equals('Visual Studio Community 2017')); expect(visualStudio.displayVersion, equals('15.9.12')); expect(visualStudio.installLocation, equals(visualStudioPath)); expect(visualStudio.fullVersion, equals('15.9.28307.665')); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); testUsingContext('isInstalled returns true when VS is present but missing components', () { setMockIncompleteVisualStudioExists(true); setMockCompatibleVisualStudioExists(false); visualStudio = VisualStudio(); expect(visualStudio.isInstalled, true); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); testUsingContext('Everything returns good values when VS is present with all components', () { setMockCompatibleVisualStudioExists(true); visualStudio = VisualStudio(); expect(visualStudio.isInstalled, true); expect(visualStudio.hasNecessaryComponents, true); expect(visualStudio.vcvarsPath, equals(vcvarsPath)); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); testUsingContext('Metadata is for compatible version when latest is missing components', () { setMockCompatibleVisualStudioExists(true); // Return a different version for queries without the required packages. final String incompleteVersionResponse = json.encode(>[ { 'installationPath': visualStudioPath, 'displayName': 'Visual Studio Community 2019', 'installationVersion': '16.1.1.1', 'catalog': { 'productDisplayVersion': '16.1', }, } ]); setMockVswhereResponse(null, incompleteVersionResponse); visualStudio = VisualStudio(); expect(visualStudio.displayName, equals('Visual Studio Community 2017')); expect(visualStudio.displayVersion, equals('15.9.12')); }, overrides: { FileSystem: () => memoryFilesystem, Platform: () => windowsPlatform, ProcessManager: () => mockProcessManager, }); }); }