blob: ec333a24029cf09815a007ced1865133ce2ff466 [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 '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))']),
]),
];
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 uri = test.scriptUri.resolve('?v${version++}');
inputProvider.cachedSources[uri] = new Future.value(program.code);
Future future = test.incrementalCompiler.compileUpdates(
{test.scriptUri: uri}, logVerbose: logger, logTime: logger);
future = future.catchError((error, trace) {
String statusMessage;
Future result;
if (program.compileUpdatesShouldThrow) {
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.isNull(update);
return null;
}
print({'update': update});
iframe.contentWindow.postMessage(['apply-update', update], '*');
return listener.expect(
program.messagesWith('iframe-dart-updated-main-done'))
.then((_) {
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(String code) {
DivElement result = new DivElement();
result.classes.add("output");
for (String text in splitLines(code)) {
PreElement line = new PreElement()
..appendText(text.trimRight())
..classes.add("line");
result.append(line);
}
return result;
}
StyleElement lineNumberStyle() {
StyleElement style = new StyleElement()..appendText('''
h2, h3 {
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;
}