blob: 63ca651da40b0a7fb2517756319b41507594866d [file] [log] [blame]
// Copyright (c) 2014, 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.
library trydart.incremental_compilation_update_test;
import 'dart:html' hide
Element;
import 'dart:async' show
Future;
import 'package:async_helper/async_helper.dart' show
asyncTest;
import 'package:expect/expect.dart' show
Expect;
import 'package:try/src/interaction_manager.dart' show
splitLines;
import 'package:try/poi/scope_information_visitor.dart' show
ScopeInformationVisitor;
import 'sandbox.dart' show
appendIFrame,
listener;
import 'web_compiler_test_case.dart' show
WebCompilerTestCase,
WebInputProvider;
import '../poi/compiler_test_case.dart' show
CompilerTestCase;
import 'package:compiler/src/elements/elements.dart' show
Element,
LibraryElement;
import 'package:compiler/src/dart2jslib.dart' show
Compiler;
import 'package:dart2js_incremental/dart2js_incremental.dart' show
IncrementalCompilationFailed;
import 'program_result.dart';
const int TIMEOUT = 100;
const List<EncodedResult> tests = const <EncodedResult>[
// Basic hello-world test.
const EncodedResult(
const [
"main() { print('Hello, ",
const ["", "Brave New "],
"World!'); }",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['Hello, World!']),
const ProgramExpectation(
const <String>['Hello, Brave New World!']),
]),
// Test that the test framework handles more than one update.
const EncodedResult(
const [
"main() { print('",
const [
"Hello darkness, my old friend",
"I\\'ve come to talk with you again",
"Because a vision softly creeping",
],
"'); }",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['Hello darkness, my old friend']),
const ProgramExpectation(
const <String>['I\'ve come to talk with you again']),
const ProgramExpectation(
const <String>['Because a vision softly creeping']),
]),
// Test that that isolate support works.
const EncodedResult(
const [
"main(arguments) { print(",
const [
"'Hello, Isolated World!'",
"arguments"
],
"); }",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['Hello, Isolated World!']),
const ProgramExpectation(
const <String>['[]']),
]),
const EncodedResult(
const [
r"""
// Test that a stored closure changes behavior when updated.
var closure;
foo(a, [b = 'b']) {
""",
const [
r"""
print('$a $b');
""",
r"""
print('$b $a');
""",
],
r"""
}
main() {
if (closure == null) {
print('[closure] is null.');
closure = foo;
}
closure('a');
closure('a', 'c');
}
"""],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['[closure] is null.', 'a b', 'a c']),
const ProgramExpectation(
const <String>['b a', 'c a']),
]),
const EncodedResult(
const [
"""
// Test modifying a static method works.
class C {
static m() {
""",
const [
r"""
print('v1');
""",
r"""
print('v2');
""",
],
"""
}
}
main() {
C.m();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
"""
// Test modifying an instance method works.
class C {
m() {
""",
const [
r"""
print('v1');
""",
r"""
print('v2');
""",
],
"""
}
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
instance.m();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
"""
// Test that a stored instance tearoff changes behavior when updated.
class C {
m() {
""",
const [
r"""
print('v1');
""",
r"""
print('v2');
""",
],
"""
}
}
var closure;
main() {
if (closure == null) {
print('closure is null');
closure = new C().m;
}
closure();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['closure is null', 'v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
"""
// Test that deleting an instance method works.
class C {
""",
const [
"""
m() {
print('v1');
}
""",
"""
""",
],
"""
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
try {
instance.m();
} catch (e) {
print('threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['threw']),
]),
const EncodedResult(
const [
"""
// Test that deleting an instance method works, even when accessed through
// super.
class A {
m() {
print('v2');
}
}
class B extends A {
""",
const [
"""
m() {
print('v1');
}
""",
"""
""",
],
"""
}
class C extends B {
m() {
super.m();
}
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
instance.m();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
"""
// Test that deleting a top-level method works.
""",
const [
"""
toplevel() {
print('v1');
}
""",
"""
""",
],
"""
class C {
m() {
try {
toplevel();
} catch (e) {
print('threw');
}
}
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
instance.m();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['threw']),
]),
const EncodedResult(
const [
"""
// Test that deleting a static method works.
class B {
""",
const [
"""
static staticMethod() {
print('v1');
}
""",
"""
""",
],
"""
}
class C {
m() {
try {
B.staticMethod();
} catch (e) {
print('threw');
}
try {
// Ensure that noSuchMethod support is compiled. This test is not about
// adding new classes.
B.missingMethod();
print('bad');
} catch (e) {
}
}
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
instance.m();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['threw']),
]),
const EncodedResult(
const [
"""
// Test that a newly instantiated class is handled.
class A {
m() {
print('Called A.m');
}
}
class B {
m() {
print('Called B.m');
}
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new A();
""",
const [
"""
""",
"""
} else {
instance = new B();
""",
],
"""
}
instance.m();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'Called A.m']),
const ProgramExpectation(
const <String>['Called B.m']),
]),
const EncodedResult(
const [
"""
// Test that source maps don't throw exceptions.
main() {
print('a');
""",
const [
"""
""",
"""
print('b');
print('c');
""",
],
"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['a']),
const ProgramExpectation(
const <String>['a', 'b', 'c']),
]),
const EncodedResult(
const [
r"""
// Test that a newly instantiated class is handled.
class A {
get name => 'A.m';
m() {
print('Called $name');
}
}
class B extends A {
get name => 'B.m';
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new A();
""",
const [
r"""
""",
r"""
} else {
instance = new B();
""",
],
r"""
}
instance.m();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'Called A.m']),
const ProgramExpectation(
const <String>['Called B.m']),
]),
const EncodedResult(
const [
r"""
// Test that fields of a newly instantiated class are handled.
class A {
var x;
A(this.x);
}
var instance;
foo() {
if (instance != null) {
print(instance.x);
} else {
print('v1');
}
}
main() {
""",
const [
r"""
""",
r"""
instance = new A('v2');
""",
],
r"""
foo();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that top-level functions can be added.
""",
const [
"",
r"""
foo() {
print('v2');
}
""",
],
r"""
main() {
try {
foo();
} catch(e) {
print('threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['threw']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that static methods can be added.
class C {
""",
const [
"",
r"""
static foo() {
print('v2');
}
""",
],
r"""
}
main() {
try {
C.foo();
} catch(e) {
print('threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['threw']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that instance methods can be added.
class C {
""",
const [
"",
r"""
foo() {
print('v2');
}
""",
],
r"""
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
try {
instance.foo();
} catch(e) {
print('threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'threw']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that top-level functions can have signature changed.
""",
const [
r"""
foo() {
print('v1');
""",
r"""
void foo() {
print('v2');
""",
],
r"""
}
main() {
foo();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that static methods can have signature changed.
class C {
""",
const [
r"""
static foo() {
print('v1');
""",
r"""
static void foo() {
print('v2');
""",
],
r"""
}
}
main() {
C.foo();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that instance methods can have signature changed.
class C {
""",
const [
r"""
foo() {
print('v1');
""",
r"""
void foo() {
print('v2');
""",
],
r"""
}
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
instance.foo();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that adding a class is supported.
""",
const [
"",
r"""
class C {
void foo() {
print('v2');
}
}
""",
],
r"""
main() {
""",
const [
r"""
print('v1');
""",
r"""
new C().foo();
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that removing a class is supported, using constructor.
""",
const [
r"""
class C {
}
""",
""
],
r"""
main() {
try {
new C();
print('v1');
} catch (e) {
print('v2');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that removing a class is supported, using a static method.
""",
const [
r"""
class C {
static m() {
print('v1');
}
}
""",
"",
],
r"""
main() {
try {
C.m();
} catch (e) {
print('v2');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that changing the supertype of a class.
class A {
m() {
print('v2');
}
}
class B extends A {
m() {
print('v1');
}
}
""",
const [
r"""
class C extends B {
""",
r"""
class C extends A {
""",
],
r"""
m() {
super.m();
}
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
instance.m();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test adding a field to a class works.
class A {
""",
const [
"",
r"""
var x;
""",
],
r"""
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new A();
}
try {
instance.x = 'v2';
} catch(e) {
print('setter threw');
}
try {
print(instance.x);
} catch (e) {
print('getter threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'setter threw', 'getter threw']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test removing a field from a class works.
class A {
""",
const [
r"""
var x;
""",
"",
],
r"""
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new A();
}
try {
instance.x = 'v1';
} catch(e) {
print('setter threw');
}
try {
print(instance.x);
} catch (e) {
print('getter threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['setter threw', 'getter threw']),
]),
const EncodedResult(
const [
r"""
// Test that named arguments can be called.
class C {
foo({a, named: 'v1', x}) {
print(named);
}
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
""",
const [
r"""
instance.foo();
""",
r"""
instance.foo(named: 'v2');
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test than named arguments can be called.
class C {
foo({a, named: 'v2', x}) {
print(named);
}
}
var instance;
main() {
if (instance == null) {
print('instance is null');
instance = new C();
}
""",
const [
r"""
instance.foo(named: 'v1');
""",
r"""
instance.foo();
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['instance is null', 'v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that an instance tear-off with named parameters can be called.
class C {
foo({a, named: 'v1', x}) {
print(named);
}
}
var closure;
main() {
if (closure == null) {
print('closure is null');
closure = new C().foo;
}
""",
const [
r"""
closure();
""",
r"""
closure(named: 'v2');
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['closure is null', 'v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that a lazy static is supported.
var normal;
""",
const [
r"""
foo() {
print(normal);
}
""",
r"""
var lazy = bar();
foo() {
print(lazy);
}
bar() {
print('v2');
return 'lazy';
}
""",
],
r"""
main() {
if (normal == null) {
normal = 'v1';
} else {
normal = '';
}
foo();
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2', 'lazy']),
]),
const EncodedResult(
const [
r"""
// Test that superclasses of directly instantiated classes are also emitted.
class A {
}
class B extends A {
}
main() {
""",
const [
r"""
print('v1');
""",
r"""
new B();
print('v2');
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that interceptor classes are handled correctly.
main() {
""",
const [
r"""
print('v1');
""",
r"""
['v2'].forEach(print);
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that newly instantiated superclasses are handled correctly when there
// is more than one change.
class A {
foo() {
print('Called foo');
}
bar() {
print('Called bar');
}
}
class B extends A {
}
main() {
""",
const [
r"""
new B().foo();
""",
r"""
new B().foo();
""",
r"""
new A().bar();
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['Called foo']),
const ProgramExpectation(
const <String>['Called foo']),
const ProgramExpectation(
const <String>['Called bar']),
]),
const EncodedResult(
const [
r"""
// Test that newly instantiated subclasses are handled correctly when there is
// more than one change.
class A {
foo() {
print('Called foo');
}
bar() {
print('Called bar');
}
}
class B extends A {
}
main() {
""",
const [
r"""
new A().foo();
""",
r"""
new A().foo();
""",
r"""
new B().bar();
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['Called foo']),
const ProgramExpectation(
const <String>['Called foo']),
const ProgramExpectation(
const <String>['Called bar']),
]),
const EncodedResult(
const [
r"""
// Test that constants are handled correctly.
class C {
final String value;
const C(this.value);
}
main() {
""",
const [
r"""
print(const C('v1').value);
""",
r"""
print(const C('v2').value);
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['v1']),
const ProgramExpectation(
const <String>['v2']),
]),
const EncodedResult(
const [
r"""
// Test that an instance field can be added to a compound declaration.
class C {
""",
const [
r"""
int x;
""",
r"""
int x, y;
""",
],
r"""
}
var instance;
main() {
if (instance == null) {
print('[instance] is null');
instance = new C();
instance.x = 'v1';
} else {
instance.y = 'v2';
}
try {
print(instance.x);
} catch (e) {
print('[instance.x] threw');
}
try {
print(instance.y);
} catch (e) {
print('[instance.y] threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>[
'[instance] is null', 'v1', '[instance.y] threw']),
const ProgramExpectation(
const <String>['v1', 'v2'],
// TODO(ahe): Shouldn't throw.
compileUpdatesShouldThrow: true),
]),
const EncodedResult(
const [
r"""
// Test that an instance field can be removed from a compound declaration.
class C {
""",
const [
r"""
int x, y;
""",
r"""
int x;
""",
],
r"""
}
var instance;
main() {
if (instance == null) {
print('[instance] is null');
instance = new C();
instance.x = 'v1';
instance.y = 'v2';
}
try {
print(instance.x);
} catch (e) {
print('[instance.x] threw');
}
try {
print(instance.y);
} catch (e) {
print('[instance.y] threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['[instance] is null', 'v1', 'v2']),
const ProgramExpectation(
const <String>['v1', '[instance.y] threw'],
// TODO(ahe): Shouldn't throw.
compileUpdatesShouldThrow: true),
]),
const EncodedResult(
const [
r"""
// Test that a static field can be made an instance field.
class C {
""",
const [
r"""
static int x;
""",
r"""
int x;
""",
],
r"""
}
var instance;
main() {
if (instance == null) {
print('[instance] is null');
instance = new C();
C.x = 'v1';
} else {
instance.x = 'v2';
}
try {
print(C.x);
} catch (e) {
print('[C.x] threw');
}
try {
print(instance.x);
} catch (e) {
print('[instance.x] threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['[instance] is null', 'v1', '[instance.x] threw']),
const ProgramExpectation(
const <String>['[C.x] threw', 'v2'],
// TODO(ahe): Shouldn't throw.
compileUpdatesShouldThrow: true),
]),
const EncodedResult(
const [
r"""
// Test that instance field can be made static.
class C {
""",
const [
r"""
int x;
""",
r"""
static int x;
""",
],
r"""
}
var instance;
main() {
if (instance == null) {
print('[instance] is null');
instance = new C();
instance.x = 'v1';
} else {
C.x = 'v2';
}
try {
print(C.x);
} catch (e) {
print('[C.x] threw');
}
try {
print(instance.x);
} catch (e) {
print('[instance.x] threw');
}
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['[instance] is null', '[C.x] threw', 'v1']),
const ProgramExpectation(
const <String>['v2', '[instance.x] threw'],
// TODO(ahe): Shouldn't throw.
compileUpdatesShouldThrow: true),
]),
const EncodedResult(
const [
r"""
// Test compound constants.
class A {
final value;
const A(this.value);
toString() => 'A($value)';
}
class B {
final value;
const B(this.value);
toString() => 'B($value)';
}
main() {
""",
const [
r"""
print(const A('v1'));
print(const B('v1'));
""",
r"""
print(const B(const A('v2')));
print(const A(const B('v2')));
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['A(v1)', 'B(v1)']),
const ProgramExpectation(
const <String>['B(A(v2))', 'A(B(v2))']),
]),
const EncodedResult(
const [
r"""
// Test constants of new classes.
class A {
final value;
const A(this.value);
toString() => 'A($value)';
}
""",
const [
"",
r"""
class B {
final value;
const B(this.value);
toString() => 'B($value)';
}
""",
],
r"""
main() {
""",
const [
r"""
print(const A('v1'));
""",
r"""
print(const A('v2'));
print(const B('v2'));
print(const B(const A('v2')));
print(const A(const B('v2')));
""",
],
r"""
}
""",
],
const <ProgramExpectation>[
const ProgramExpectation(
const <String>['A(v1)']),
const ProgramExpectation(
const <String>['A(v2)', 'B(v2)', 'B(A(v2))', 'A(B(v2))']),
]),
const EncodedResult(
r"""
==> main.dart <==
// Test that a change in a part is handled.
library test.main;
part 'part.dart';
==> part.dart.patch <==
part of test.main;
main() {
<<<<<<<
print('Hello, World!');
=======
print('Hello, Brave New World!');
>>>>>>>
}
""",
const [
'Hello, World!',
'Hello, Brave New World!',
]),
const EncodedResult(
r"""
==> main.dart.patch <==
// Test that a change in library name is handled.
<<<<<<<
library test.main1;
=======
library test.main2;
>>>>>>>
main() {
print('Hello, World!');
}
""",
const [
'Hello, World!',
const ProgramExpectation(
const <String>['Hello, World!'],
// TODO(ahe): Shouldn't throw.
compileUpdatesShouldThrow: true),
]),
const EncodedResult(
r"""
==> main.dart.patch <==
// Test that adding an import is handled.
<<<<<<<
=======
import 'dart:core';
>>>>>>>
main() {
print('Hello, World!');
}
""",
const [
'Hello, World!',
const ProgramExpectation(
const <String>['Hello, World!'],
// TODO(ahe): Shouldn't throw.
compileUpdatesShouldThrow: true),
]),
const EncodedResult(
r"""
==> main.dart.patch <==
// Test that adding an export is handled.
<<<<<<<
=======
export 'dart:core';
>>>>>>>
main() {
print('Hello, World!');
}
""",
const [
'Hello, World!',
const ProgramExpectation(
const <String>['Hello, World!'],
// TODO(ahe): Shouldn't throw.
compileUpdatesShouldThrow: true),
]),
const EncodedResult(
r"""
==> main.dart.patch <==
// Test that adding a part is handled.
library test.main;
<<<<<<<
=======
part 'part.dart';
>>>>>>>
main() {
print('Hello, World!');
}
==> part.dart <==
part of test.main
""",
const [
'Hello, World!',
const ProgramExpectation(
const <String>['Hello, World!'],
// TODO(ahe): Shouldn't throw.
compileUpdatesShouldThrow: true),
]),
const EncodedResult(
r"""
==> main.dart <==
// Test that changes in multiple libraries is handled.
import 'library1.dart' as lib1;
import 'library2.dart' as lib2;
main() {
lib1.method();
lib2.method();
}
==> library1.dart.patch <==
library test.library1;
method() {
<<<<<<<
print('lib1.v1');
=======
print('lib1.v2');
=======
print('lib1.v3');
>>>>>>>
}
==> library2.dart.patch <==
library test.library2;
method() {
<<<<<<<
print('lib2.v1');
=======
print('lib2.v2');
=======
print('lib2.v3');
>>>>>>>
}
""",
const [
const <String>['lib1.v1', 'lib2.v1'],
const <String>['lib1.v2', 'lib2.v2'],
const <String>['lib1.v3', 'lib2.v3'],
]),
];
void main() {
listener.start();
document.head.append(lineNumberStyle());
summary = new SpanElement();
document.body.append(new HeadingElement.h1()
..appendText("Incremental compiler tests")
..append(summary));
String query = window.location.search;
int skip = 0;
if (query != null && query.length > 1) {
query = query.substring(1);
String skipParameter = Uri.splitQueryString(window.location.search)['skip'];
if (skipParameter != null) {
skip = int.parse(skipParameter);
}
String verboseParameter =
Uri.splitQueryString(window.location.search)['verbose'];
verboseStatus = verboseParameter != null;
}
testCount += skip;
return asyncTest(() => Future.forEach(tests.skip(skip), compileAndRun)
.then(updateSummary));
}
SpanElement summary;
int testCount = 1;
bool verboseStatus = false;
void updateSummary(_) {
summary.text = " (${testCount - 1}/${tests.length})";
}
Future compileAndRun(EncodedResult encodedResult) {
updateSummary(null);
List<ProgramResult> programs = encodedResult.decode();
var status = new DivElement();
document.body.append(status);
IFrameElement iframe =
appendIFrame(
'/root_dart/tests/try/web/incremental_compilation_update.html',
document.body)
..style.width = '100%'
..style.height = '600px';
return listener.expect('iframe-ready').then((_) {
ProgramResult program = programs.first;
status.append(
new HeadingElement.h2()
..appendText("Full program #${testCount++}:"));
status.append(numberedLines(program.code));
status.style.color = 'orange';
WebCompilerTestCase test = new WebCompilerTestCase(program.code);
return test.run().then((String jsCode) {
status.style.color = 'red';
var objectUrl =
Url.createObjectUrl(new Blob([jsCode], 'application/javascript'));
iframe.contentWindow.postMessage(['add-script', objectUrl], '*');
Future future =
listener.expect(program.messagesWith('iframe-dart-main-done'));
return future.then((_) {
int version = 2;
return Future.forEach(programs.skip(1), (ProgramResult program) {
status.append(new HeadingElement.h2()..appendText("Update:"));
status.append(numberedLines(program.code));
WebInputProvider inputProvider =
test.incrementalCompiler.inputProvider;
Uri base = test.scriptUri;
Map<String, String> code = program.code is String
? { 'main.dart': program.code }
: program.code;
Map<Uri, Uri> uriMap = <Uri, Uri>{};
for (String name in code.keys) {
Uri uri = base.resolve('$name?v${version++}');
inputProvider.cachedSources[uri] = new Future.value(code[name]);
uriMap[base.resolve(name)] = uri;
}
Future future = test.incrementalCompiler.compileUpdates(
uriMap, logVerbose: logger, logTime: logger);
bool compileUpdatesThrew = false;
future = future.catchError((error, trace) {
String statusMessage;
Future result;
compileUpdatesThrew = true;
if (program.compileUpdatesShouldThrow &&
error is IncrementalCompilationFailed) {
statusMessage = "Expected error in compileUpdates.";
result = null;
} else {
statusMessage = "Unexpected error in compileUpdates.";
result = new Future.error(error, trace);
}
status.append(new HeadingElement.h3()..appendText(statusMessage));
return result;
});
return future.then((String update) {
if (program.compileUpdatesShouldThrow) {
Expect.isTrue(
compileUpdatesThrew,
"Expected an exception in compileUpdates");
Expect.isNull( update, "Expected update == null");
return null;
}
print({'update': update});
iframe.contentWindow.postMessage(['apply-update', update], '*');
return listener.expect(
program.messagesWith('iframe-dart-updated-main-done'))
.then((_) {
// TODO(ahe): Enable SerializeScopeTestCase for multiple
// parts.
if (program.code is! String) return null;
return new SerializeScopeTestCase(
program.code, test.incrementalCompiler.mainApp,
test.incrementalCompiler.compiler).run();
});
});
});
});
});
}).then((_) {
status.style.color = 'limegreen';
// Remove the iframe and status to work around a bug in test.dart
// (https://code.google.com/p/dart/issues/detail?id=21691).
if (!verboseStatus) status.remove();
iframe.remove();
});
}
class SerializeScopeTestCase extends CompilerTestCase {
final String scopeInfo;
SerializeScopeTestCase(
String source,
LibraryElement library,
Compiler compiler)
: scopeInfo = computeScopeInfo(compiler, library),
super(source, '${library.canonicalUri}');
Future run() => loadMainApp().then(checkScopes);
void checkScopes(LibraryElement library) {
Expect.stringEquals(computeScopeInfo(compiler, library), scopeInfo);
}
static String computeScopeInfo(Compiler compiler, LibraryElement library) {
ScopeInformationVisitor visitor =
new ScopeInformationVisitor(compiler, library, 0);
visitor.ignoreImports = true;
visitor.sortMembers = true;
visitor.indented.write('[\n');
visitor.indentationLevel++;
visitor.indented;
library.accept(visitor);
library.forEachLocalMember((Element member) {
if (member.isClass) {
visitor.buffer.write(',\n');
visitor.indented;
member.accept(visitor);
}
});
visitor.buffer.write('\n');
visitor.indentationLevel--;
visitor.indented.write(']');
return '${visitor.buffer}';
}
}
void logger(x) {
print(x);
bool isCheckedMode = false;
assert(isCheckedMode = true);
int timeout = isCheckedMode ? TIMEOUT * 2 : TIMEOUT;
if (listener.elapsed > timeout) {
throw 'Test timed out.';
}
}
DivElement numberedLines(code) {
if (code is! Map) {
code = {'main.dart': code};
}
DivElement result = new DivElement();
code.forEach((String fileName, String code) {
result.append(new HeadingElement.h4()..appendText(fileName));
DivElement lines = new DivElement();
result.append(lines);
lines.classes.add("output");
for (String text in splitLines(code)) {
PreElement line = new PreElement()
..appendText(text.trimRight())
..classes.add("line");
lines.append(line);
}
});
return result;
}
StyleElement lineNumberStyle() {
StyleElement style = new StyleElement()..appendText('''
h2, h3, h4 {
color: black;
}
.output {
padding: 0px;
counter-reset: line-number;
padding-bottom: 1em;
}
.line {
white-space: pre-wrap;
padding-left: 3.5em;
margin-top: 0;
margin-bottom: 0;
}
.line::before {
counter-increment: line-number;
content: counter(line-number) " ";
position: absolute;
left: 0px;
width: 3em;
text-align: right;
background-color: lightgoldenrodyellow;
}
''');
style.type = 'text/css';
return style;
}