Unverified Commit 354ce1aa authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

More verification on flutter build web, add tests and cleanup (#34090)

parent 3c5a4dc7
...@@ -57,6 +57,9 @@ class ApplicationPackageFactory { ...@@ -57,6 +57,9 @@ class ApplicationPackageFactory {
? MacOSApp.fromMacOSProject(FlutterProject.current().macos) ? MacOSApp.fromMacOSProject(FlutterProject.current().macos)
: MacOSApp.fromPrebuiltApp(applicationBinary); : MacOSApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.web_javascript: case TargetPlatform.web_javascript:
if (!FlutterProject.current().web.existsSync()) {
return null;
}
return WebApplicationPackage(FlutterProject.current()); return WebApplicationPackage(FlutterProject.current());
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
return applicationBinary == null return applicationBinary == null
......
...@@ -580,11 +580,14 @@ class WebProject { ...@@ -580,11 +580,14 @@ class WebProject {
/// Whether this flutter project has a web sub-project. /// Whether this flutter project has a web sub-project.
bool existsSync() { bool existsSync() {
return parent.directory.childDirectory('web').existsSync(); return parent.directory.childDirectory('web').existsSync()
&& indexFile.existsSync();
} }
/// The html file used to host the flutter web application. /// The html file used to host the flutter web application.
File get indexFile => parent.directory.childDirectory('web').childFile('index.html'); File get indexFile => parent.directory
.childDirectory('web')
.childFile('index.html');
Future<void> ensureReadyForPlatformSpecificTooling() async {} Future<void> ensureReadyForPlatformSpecificTooling() async {}
} }
......
...@@ -7,6 +7,7 @@ import 'dart:async'; ...@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
import 'application_package.dart';
import 'asset.dart'; import 'asset.dart';
import 'base/common.dart'; import 'base/common.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
...@@ -112,7 +113,14 @@ class ResidentWebRunner extends ResidentRunner { ...@@ -112,7 +113,14 @@ class ResidentWebRunner extends ResidentRunner {
String route, String route,
bool shouldBuild = true, bool shouldBuild = true,
}) async { }) async {
final FlutterProject currentProject = FlutterProject.current(); final ApplicationPackage package = await ApplicationPackageFactory.instance.getPackageForPlatform(
TargetPlatform.web_javascript,
applicationBinary: null,
);
if (package == null) {
printError('No application found for TargetPlatform.web_javascript');
return 1;
}
if (!fs.isFileSync(mainPath)) { if (!fs.isFileSync(mainPath)) {
String message = 'Tried to run $mainPath, but that file does not exist.'; String message = 'Tried to run $mainPath, but that file does not exist.';
if (target == null) { if (target == null) {
...@@ -124,7 +132,7 @@ class ResidentWebRunner extends ResidentRunner { ...@@ -124,7 +132,7 @@ class ResidentWebRunner extends ResidentRunner {
} }
// Start the web compiler and build the assets. // Start the web compiler and build the assets.
await webCompilationProxy.initialize( await webCompilationProxy.initialize(
projectDirectory: currentProject.directory, projectDirectory: FlutterProject.current().directory,
targets: <String>[target], targets: <String>[target],
); );
_lastCompiled = DateTime.now(); _lastCompiled = DateTime.now();
......
...@@ -19,6 +19,9 @@ import '../usage.dart'; ...@@ -19,6 +19,9 @@ import '../usage.dart';
WebCompilationProxy get webCompilationProxy => context.get<WebCompilationProxy>(); WebCompilationProxy get webCompilationProxy => context.get<WebCompilationProxy>();
Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo buildInfo) async { Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo buildInfo) async {
if (!flutterProject.web.existsSync()) {
throwToolExit('Missing index.html.');
}
final Status status = logger.startProgress('Compiling $target for the Web...', timeout: null); final Status status = logger.startProgress('Compiling $target for the Web...', timeout: null);
final Stopwatch sw = Stopwatch()..start(); final Stopwatch sw = Stopwatch()..start();
final Directory outputDir = fs.directory(getWebBuildDirectory()) final Directory outputDir = fs.directory(getWebBuildDirectory())
......
...@@ -19,23 +19,32 @@ import '../src/context.dart'; ...@@ -19,23 +19,32 @@ import '../src/context.dart';
import '../src/mocks.dart'; import '../src/mocks.dart';
void main() { void main() {
Cache.disableLocking(); MockProcessManager mockProcessManager;
final MockProcessManager mockProcessManager = MockProcessManager(); MockProcess mockProcess;
final MockProcess mockProcess = MockProcess(); MockPlatform linuxPlatform;
final MockPlatform linuxPlatform = MockPlatform(); MockPlatform notLinuxPlatform;
final MockPlatform notLinuxPlatform = MockPlatform();
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async { setUpAll(() {
return 0; Cache.disableLocking();
}); });
when(mockProcess.stderr).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty(); setUp(() {
}); mockProcessManager = MockProcessManager();
when(mockProcess.stdout).thenAnswer((Invocation invocation) { mockProcess = MockProcess();
return const Stream<List<int>>.empty(); linuxPlatform = MockPlatform();
notLinuxPlatform = MockPlatform();
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async {
return 0;
});
when(mockProcess.stderr).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty();
});
when(mockProcess.stdout).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty();
});
when(linuxPlatform.isLinux).thenReturn(true);
when(notLinuxPlatform.isLinux).thenReturn(false);
}); });
when(linuxPlatform.isLinux).thenReturn(true);
when(notLinuxPlatform.isLinux).thenReturn(false);
testUsingContext('Linux build fails when there is no linux project', () async { testUsingContext('Linux build fails when there is no linux project', () async {
final BuildCommand command = BuildCommand(); final BuildCommand command = BuildCommand();
......
...@@ -19,24 +19,34 @@ import '../src/context.dart'; ...@@ -19,24 +19,34 @@ import '../src/context.dart';
import '../src/mocks.dart'; import '../src/mocks.dart';
void main() { void main() {
Cache.disableLocking(); MockProcessManager mockProcessManager;
final MockProcessManager mockProcessManager = MockProcessManager(); MemoryFileSystem memoryFilesystem;
final MemoryFileSystem memoryFilesystem = MemoryFileSystem(); MockProcess mockProcess;
final MockProcess mockProcess = MockProcess(); MockPlatform macosPlatform;
final MockPlatform macosPlatform = MockPlatform(); MockPlatform notMacosPlatform;
final MockPlatform notMacosPlatform = MockPlatform();
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async { setUpAll(() {
return 0; Cache.disableLocking();
});
when(mockProcess.stderr).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty();
}); });
when(mockProcess.stdout).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty(); setUp(() {
mockProcessManager = MockProcessManager();
memoryFilesystem = MemoryFileSystem();
mockProcess = MockProcess();
macosPlatform = MockPlatform();
notMacosPlatform = MockPlatform();
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async {
return 0;
});
when(mockProcess.stderr).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty();
});
when(mockProcess.stdout).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty();
});
when(macosPlatform.isMacOS).thenReturn(true);
when(notMacosPlatform.isMacOS).thenReturn(false);
}); });
when(macosPlatform.isMacOS).thenReturn(true);
when(notMacosPlatform.isMacOS).thenReturn(false);
testUsingContext('macOS build fails when there is no macos project', () async { testUsingContext('macOS build fails when there is no macos project', () async {
final BuildCommand command = BuildCommand(); final BuildCommand command = BuildCommand();
......
// 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:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/resident_web_runner.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:flutter_tools/src/web/compile.dart';
import 'package:mockito/mockito.dart';
import '../src/common.dart';
import '../src/testbed.dart';
void main() {
MockWebCompilationProxy mockWebCompilationProxy;
Testbed testbed;
MockPlatform mockPlatform;
setUpAll(() {
Cache.disableLocking();
});
setUp(() {
mockWebCompilationProxy = MockWebCompilationProxy();
testbed = Testbed(setup: () {
fs.file('pubspec.yaml')
..createSync()
..writeAsStringSync('name: foo\n');
fs.file('.packages').createSync();
fs.file(fs.path.join('web', 'index.html')).createSync(recursive: true);
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
when(mockWebCompilationProxy.initialize(
projectDirectory: anyNamed('projectDirectory'),
targets: anyNamed('targets'),
release: anyNamed('release')
)).thenAnswer((Invocation invocation) {
final String path = fs.path.join('.dart_tool', 'build', 'flutter_web', 'foo', 'lib', 'main_web_entrypoint.dart.js');
fs.file(path).createSync(recursive: true);
fs.file('$path.map').createSync();
return Future<bool>.value(true);
});
}, overrides: <Type, Generator>{
WebCompilationProxy: () => mockWebCompilationProxy,
Platform: () => mockPlatform,
FlutterVersion: () => MockFlutterVersion(),
});
});
test('Refuses to build for web when missing index.html', () => testbed.run(() async {
fs.file(fs.path.join('web', 'index.html')).deleteSync();
expect(buildWeb(
FlutterProject.current(),
fs.path.join('lib', 'main.dart'),
BuildInfo.debug,
), throwsA(isInstanceOf<ToolExit>()));
}));
test('Refuses to build using runner when missing index.html', () => testbed.run(() async {
fs.file(fs.path.join('web', 'index.html')).deleteSync();
final ResidentWebRunner runner = ResidentWebRunner(
<FlutterDevice>[],
flutterProject: FlutterProject.current(),
ipv6: false,
);
expect(await runner.run(), 1);
}));
test('Can build for web', () => testbed.run(() async {
await buildWeb(
FlutterProject.current(),
fs.path.join('lib', 'main.dart'),
BuildInfo.debug,
);
}));
}
class MockWebCompilationProxy extends Mock implements WebCompilationProxy {}
class MockPlatform extends Mock implements Platform {
@override
Map<String, String> environment = <String, String>{
'FLUTTER_ROOT': '/',
};
}
class MockFlutterVersion extends Mock implements FlutterVersion {
@override
bool get isStable => false;
}
...@@ -19,29 +19,40 @@ import '../src/context.dart'; ...@@ -19,29 +19,40 @@ import '../src/context.dart';
import '../src/mocks.dart'; import '../src/mocks.dart';
void main() { void main() {
Cache.disableLocking(); MockProcessManager mockProcessManager;
final MockProcessManager mockProcessManager = MockProcessManager(); MemoryFileSystem memoryFilesystem;
final MemoryFileSystem memoryFilesystem = MemoryFileSystem(style: FileSystemStyle.windows); MockProcess mockProcess;
final MockProcess mockProcess = MockProcess(); MockPlatform windowsPlatform;
final MockPlatform windowsPlatform = MockPlatform() MockPlatform notWindowsPlatform;
..environment['PROGRAMFILES(X86)'] = r'C:\Program Files (x86)\'; MockVisualStudio mockVisualStudio;
final MockPlatform notWindowsPlatform = MockPlatform();
final MockVisualStudio mockVisualStudio = MockVisualStudio();
const String solutionPath = r'C:\windows\Runner.sln'; const String solutionPath = r'C:\windows\Runner.sln';
const String visualStudioPath = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community'; const String visualStudioPath = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community';
const String vcvarsPath = visualStudioPath + r'\VC\Auxiliary\Build\vcvars64.bat'; const String vcvarsPath = visualStudioPath + r'\VC\Auxiliary\Build\vcvars64.bat';
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async { setUpAll(() {
return 0; Cache.disableLocking();
}); });
when(mockProcess.stderr).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty(); setUp(() {
}); mockProcessManager = MockProcessManager();
when(mockProcess.stdout).thenAnswer((Invocation invocation) { memoryFilesystem = MemoryFileSystem(style: FileSystemStyle.windows);
return const Stream<List<int>>.empty(); mockProcess = MockProcess();
windowsPlatform = MockPlatform()
..environment['PROGRAMFILES(X86)'] = r'C:\Program Files (x86)\';
notWindowsPlatform = MockPlatform();
mockVisualStudio = MockVisualStudio();
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async {
return 0;
});
when(mockProcess.stderr).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty();
});
when(mockProcess.stdout).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty();
});
when(windowsPlatform.isWindows).thenReturn(true);
when(notWindowsPlatform.isWindows).thenReturn(false);
}); });
when(windowsPlatform.isWindows).thenReturn(true);
when(notWindowsPlatform.isWindows).thenReturn(false);
testUsingContext('Windows build fails when there is no vcvars64.bat', () async { testUsingContext('Windows build fails when there is no vcvars64.bat', () async {
final BuildCommand command = BuildCommand(); final BuildCommand command = BuildCommand();
......
...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/logger.dart'; ...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/context_runner.dart'; import 'package:flutter_tools/src/context_runner.dart';
import 'package:flutter_tools/src/usage.dart';
import 'context.dart'; import 'context.dart';
...@@ -24,6 +25,7 @@ final Map<Type, Generator> _testbedDefaults = <Type, Generator>{ ...@@ -24,6 +25,7 @@ final Map<Type, Generator> _testbedDefaults = <Type, Generator>{
FileSystem: () => MemoryFileSystem(), // Keeps tests fast by avoid actual file system. FileSystem: () => MemoryFileSystem(), // Keeps tests fast by avoid actual file system.
Logger: () => BufferLogger(), // Allows reading logs and prevents stdout. Logger: () => BufferLogger(), // Allows reading logs and prevents stdout.
OutputPreferences: () => OutputPreferences(showColor: false), // configures BufferLogger to avoid color codes. OutputPreferences: () => OutputPreferences(showColor: false), // configures BufferLogger to avoid color codes.
Usage: () => NoOpUsage(), // prevent addition of analytics from burdening test mocks
}; };
/// Manages interaction with the tool injection and runner system. /// Manages interaction with the tool injection and runner system.
...@@ -60,7 +62,7 @@ class Testbed { ...@@ -60,7 +62,7 @@ class Testbed {
/// `overrides` provides more overrides in addition to the test defaults. /// `overrides` provides more overrides in addition to the test defaults.
/// `setup` may be provided to apply mocks within the tool managed zone, /// `setup` may be provided to apply mocks within the tool managed zone,
/// including any specified overrides. /// including any specified overrides.
Testbed({Future<void> Function() setup, Map<Type, Generator> overrides}) Testbed({FutureOr<void> Function() setup, Map<Type, Generator> overrides})
: _setup = setup, : _setup = setup,
_overrides = overrides; _overrides = overrides;
...@@ -101,3 +103,41 @@ class Testbed { ...@@ -101,3 +103,41 @@ class Testbed {
}); });
} }
} }
/// A no-op implementation of [Usage] for testing.
class NoOpUsage implements Usage {
@override
bool enabled = false;
@override
bool suppressAnalytics = true;
@override
String get clientId => 'test';
@override
Future<void> ensureAnalyticsSent() {
return null;
}
@override
bool get isFirstRun => false;
@override
Stream<Map<String, Object>> get onSend => const Stream<Object>.empty();
@override
void printWelcome() {}
@override
void sendCommand(String command, {Map<String, String> parameters}) {}
@override
void sendEvent(String category, String parameter, {Map<String, String> parameters}) {}
@override
void sendException(dynamic exception, StackTrace trace) {}
@override
void sendTiming(String category, String variableName, Duration duration, {String label}) {}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment