Unverified Commit f530b809 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Reland: More verification on flutter build web, add tests and cleanup (#34173)

parent 7a877f62
......@@ -57,6 +57,9 @@ class ApplicationPackageFactory {
? MacOSApp.fromMacOSProject(FlutterProject.current().macos)
: MacOSApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.web_javascript:
if (!FlutterProject.current().web.existsSync()) {
return null;
}
return WebApplicationPackage(FlutterProject.current());
case TargetPlatform.linux_x64:
return applicationBinary == null
......
......@@ -580,11 +580,14 @@ class WebProject {
/// Whether this flutter project has a web sub-project.
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.
File get indexFile => parent.directory.childDirectory('web').childFile('index.html');
File get indexFile => parent.directory
.childDirectory('web')
.childFile('index.html');
Future<void> ensureReadyForPlatformSpecificTooling() async {}
}
......
......@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:meta/meta.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
import 'application_package.dart';
import 'asset.dart';
import 'base/common.dart';
import 'base/file_system.dart';
......@@ -112,7 +113,14 @@ class ResidentWebRunner extends ResidentRunner {
String route,
bool shouldBuild = true,
}) 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)) {
String message = 'Tried to run $mainPath, but that file does not exist.';
if (target == null) {
......@@ -124,7 +132,7 @@ class ResidentWebRunner extends ResidentRunner {
}
// Start the web compiler and build the assets.
await webCompilationProxy.initialize(
projectDirectory: currentProject.directory,
projectDirectory: FlutterProject.current().directory,
targets: <String>[target],
);
_lastCompiled = DateTime.now();
......
......@@ -19,6 +19,9 @@ import '../usage.dart';
WebCompilationProxy get webCompilationProxy => context.get<WebCompilationProxy>();
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 Stopwatch sw = Stopwatch()..start();
final Directory outputDir = fs.directory(getWebBuildDirectory())
......
......@@ -19,23 +19,32 @@ import '../src/context.dart';
import '../src/mocks.dart';
void main() {
Cache.disableLocking();
final MockProcessManager mockProcessManager = MockProcessManager();
final MockProcess mockProcess = MockProcess();
final MockPlatform linuxPlatform = MockPlatform();
final MockPlatform notLinuxPlatform = MockPlatform();
MockProcessManager mockProcessManager;
MockProcess mockProcess;
MockPlatform linuxPlatform;
MockPlatform notLinuxPlatform;
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async {
return 0;
setUpAll(() {
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();
mockProcess = MockProcess();
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 {
final BuildCommand command = BuildCommand();
......
......@@ -19,24 +19,34 @@ import '../src/context.dart';
import '../src/mocks.dart';
void main() {
Cache.disableLocking();
final MockProcessManager mockProcessManager = MockProcessManager();
final MemoryFileSystem memoryFilesystem = MemoryFileSystem();
final MockProcess mockProcess = MockProcess();
final MockPlatform macosPlatform = MockPlatform();
final MockPlatform notMacosPlatform = MockPlatform();
MockProcessManager mockProcessManager;
MemoryFileSystem memoryFilesystem;
MockProcess mockProcess;
MockPlatform macosPlatform;
MockPlatform notMacosPlatform;
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async {
return 0;
});
when(mockProcess.stderr).thenAnswer((Invocation invocation) {
return const Stream<List<int>>.empty();
setUpAll(() {
Cache.disableLocking();
});
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 {
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/device.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,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
);
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';
import '../src/mocks.dart';
void main() {
Cache.disableLocking();
final MockProcessManager mockProcessManager = MockProcessManager();
final MemoryFileSystem memoryFilesystem = MemoryFileSystem(style: FileSystemStyle.windows);
final MockProcess mockProcess = MockProcess();
final MockPlatform windowsPlatform = MockPlatform()
..environment['PROGRAMFILES(X86)'] = r'C:\Program Files (x86)\';
final MockPlatform notWindowsPlatform = MockPlatform();
final MockVisualStudio mockVisualStudio = MockVisualStudio();
MockProcessManager mockProcessManager;
MemoryFileSystem memoryFilesystem;
MockProcess mockProcess;
MockPlatform windowsPlatform;
MockPlatform notWindowsPlatform;
MockVisualStudio mockVisualStudio;
const String solutionPath = r'C:\windows\Runner.sln';
const String visualStudioPath = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community';
const String vcvarsPath = visualStudioPath + r'\VC\Auxiliary\Build\vcvars64.bat';
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async {
return 0;
setUpAll(() {
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(style: FileSystemStyle.windows);
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 {
final BuildCommand command = BuildCommand();
......
......@@ -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/cache.dart';
import 'package:flutter_tools/src/context_runner.dart';
import 'package:flutter_tools/src/usage.dart';
import 'context.dart';
......@@ -24,6 +25,7 @@ final Map<Type, Generator> _testbedDefaults = <Type, Generator>{
FileSystem: () => MemoryFileSystem(), // Keeps tests fast by avoid actual file system.
Logger: () => BufferLogger(), // Allows reading logs and prevents stdout.
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.
......@@ -60,7 +62,7 @@ class Testbed {
/// `overrides` provides more overrides in addition to the test defaults.
/// `setup` may be provided to apply mocks within the tool managed zone,
/// including any specified overrides.
Testbed({Future<void> Function() setup, Map<Type, Generator> overrides})
Testbed({FutureOr<void> Function() setup, Map<Type, Generator> overrides})
: _setup = setup,
_overrides = overrides;
......@@ -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