1// Copyright 2018 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Integration tests which invoke flutter instead of unit testing the code 6// will not produce meaningful coverage information - we can measure coverage 7// from the isolate running the test, but not from the isolate started via 8// the command line process. 9@Tags(<String>['no_coverage']) 10import 'dart:async'; 11 12import 'package:file/file.dart'; 13import 'package:flutter_tools/src/base/common.dart'; 14import 'package:flutter_tools/src/base/file_system.dart'; 15import 'package:vm_service/vm_service.dart'; 16 17import '../src/common.dart'; 18import 'test_data/hot_reload_project.dart'; 19import 'test_driver.dart'; 20import 'test_utils.dart'; 21 22void main() { 23 group('hot reload tests', () { 24 Directory tempDir; 25 final HotReloadProject _project = HotReloadProject(); 26 FlutterRunTestDriver _flutter; 27 28 setUp(() async { 29 tempDir = createResolvedTempDirectorySync('hot_reload_test.'); 30 await _project.setUpIn(tempDir); 31 _flutter = FlutterRunTestDriver(tempDir); 32 }); 33 34 tearDown(() async { 35 await _flutter?.stop(); 36 tryToDelete(tempDir); 37 }); 38 39 test('hot reload works without error', () async { 40 await _flutter.run(); 41 await _flutter.hotReload(); 42 }); 43 44 test('newly added code executes during hot reload', () async { 45 await _flutter.run(); 46 _project.uncommentHotReloadPrint(); 47 final StringBuffer stdout = StringBuffer(); 48 final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln); 49 try { 50 await _flutter.hotReload(); 51 expect(stdout.toString(), contains('(((((RELOAD WORKED)))))')); 52 } finally { 53 await subscription.cancel(); 54 } 55 }); 56 57 test('hot restart works without error', () async { 58 await _flutter.run(); 59 await _flutter.hotRestart(); 60 }); 61 62 test('breakpoints are hit after hot reload', () async { 63 Isolate isolate; 64 await _flutter.run(withDebugger: true, startPaused: true); 65 final Completer<void> sawTick1 = Completer<void>(); 66 final Completer<void> sawTick3 = Completer<void>(); 67 final Completer<void> sawDebuggerPausedMessage = Completer<void>(); 68 final StreamSubscription<String> subscription = _flutter.stdout.listen( 69 (String line) { 70 if (line.contains('((((TICK 1))))')) { 71 expect(sawTick1.isCompleted, isFalse); 72 sawTick1.complete(); 73 } 74 if (line.contains('((((TICK 3))))')) { 75 expect(sawTick3.isCompleted, isFalse); 76 sawTick3.complete(); 77 } 78 if (line.contains('The application is paused in the debugger on a breakpoint.')) { 79 expect(sawDebuggerPausedMessage.isCompleted, isFalse); 80 sawDebuggerPausedMessage.complete(); 81 } 82 }, 83 ); 84 await _flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts 85 unawaited(sawTick1.future.timeout( 86 const Duration(seconds: 5), 87 onTimeout: () { print('The test app is taking longer than expected to print its synchronization line...'); }, 88 )); 89 await sawTick1.future; // after this, app is in steady state 90 await _flutter.addBreakpoint( 91 _project.scheduledBreakpointUri, 92 _project.scheduledBreakpointLine, 93 ); 94 await _flutter.hotReload(); // reload triggers code which eventually hits the breakpoint 95 isolate = await _flutter.waitForPause(); 96 expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); 97 await _flutter.resume(); 98 await _flutter.addBreakpoint( 99 _project.buildBreakpointUri, 100 _project.buildBreakpointLine, 101 ); 102 bool reloaded = false; 103 final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; }); 104 await sawTick3.future; // this should happen before it pauses 105 isolate = await _flutter.waitForPause(); 106 expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); 107 await sawDebuggerPausedMessage.future; 108 expect(reloaded, isFalse); 109 await _flutter.resume(); 110 await reloadFuture; 111 expect(reloaded, isTrue); 112 reloaded = false; 113 await subscription.cancel(); 114 }); 115 116 test('hot reload doesn\'t reassemble if paused', () async { 117 await _flutter.run(withDebugger: true); 118 final Completer<void> sawTick2 = Completer<void>(); 119 final Completer<void> sawTick3 = Completer<void>(); 120 final Completer<void> sawDebuggerPausedMessage1 = Completer<void>(); 121 final Completer<void> sawDebuggerPausedMessage2 = Completer<void>(); 122 final StreamSubscription<String> subscription = _flutter.stdout.listen( 123 (String line) { 124 if (line.contains('((((TICK 2))))')) { 125 expect(sawTick2.isCompleted, isFalse); 126 sawTick2.complete(); 127 } 128 if (line.contains('The application is paused in the debugger on a breakpoint.')) { 129 expect(sawDebuggerPausedMessage1.isCompleted, isFalse); 130 sawDebuggerPausedMessage1.complete(); 131 } 132 if (line.contains('The application is paused in the debugger on a breakpoint; interface might not update.')) { 133 expect(sawDebuggerPausedMessage2.isCompleted, isFalse); 134 sawDebuggerPausedMessage2.complete(); 135 } 136 }, 137 ); 138 await _flutter.addBreakpoint( 139 _project.buildBreakpointUri, 140 _project.buildBreakpointLine, 141 ); 142 bool reloaded = false; 143 final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; }); 144 await sawTick2.future; // this should happen before it pauses 145 final Isolate isolate = await _flutter.waitForPause(); 146 expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); 147 expect(reloaded, isFalse); 148 await sawDebuggerPausedMessage1.future; // this is the one where it say "uh, you broke into the debugger while reloading" 149 await reloadFuture; // this is the one where it times out because you're in the debugger 150 expect(reloaded, isTrue); 151 await _flutter.hotReload(); // now we're already paused 152 expect(sawTick3.isCompleted, isFalse); 153 await sawDebuggerPausedMessage2.future; // so we just get told that nothing is going to happen 154 await _flutter.resume(); 155 await subscription.cancel(); 156 }); 157 }, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow. 158} 159