blob: 8bbdc7749750201c20d77058416b4d8f56a73f28 [file] [log] [blame]
// Copyright (c) 2024, 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.
import 'package:dev_compiler/src/kernel/hot_reload_delta_inspector.dart';
import 'package:kernel/ast.dart';
import 'package:test/test.dart';
import 'memory_compiler.dart';
Future<void> main() async {
group('const classes', () {
final deltaInspector = HotReloadDeltaInspector();
test('rejection when removing only const constructor', () async {
final initialSource = '''
var globalVariable;
class A {
final String s;
const A(this.s);
}
main() {
globalVariable = const A('hello');
print(globalVariable.s);
}
''';
final deltaSource = '''
var globalVariable;
class A {
final String s;
A(this.s);
}
main() {
print('hello world');
}
''';
final (:initial, :delta) =
await compileComponents(initialSource, deltaSource);
expect(
deltaInspector.compareGenerations(initial, delta),
unorderedEquals([
'Const class cannot become non-const: '
"Library:'memory:///main.dart' "
'Class: A'
]));
});
test('multiple rejections when removing only const constructors', () async {
final initialSource = '''
var globalA, globalB, globalC, globalD;
class A {
final String s;
const A(this.s);
}
class B {
final String s;
const B(this.s);
}
class C {
final String s;
C(this.s);
}
class D {
final String s;
const D(this.s);
}
main() {
globalA = const A('hello');
globalB = const B('world');
globalC = C('hello');
globalD = const D('world');
print(globalA.s);
print(globalB.s);
print(globalC.s);
print(globalD.s);
}
''';
final deltaSource = '''
var globalA, globalB, globalC, globalD;
class A {
final String s;
A(this.s);
}
class B {
final String s;
const B(this.s);
}
class C {
final String s;
C(this.s);
}
class D {
final String s;
D(this.s);
}
main() {
print('hello world');
}
''';
final (:initial, :delta) =
await compileComponents(initialSource, deltaSource);
expect(
deltaInspector.compareGenerations(initial, delta),
unorderedEquals([
'Const class cannot become non-const: '
"Library:'memory:///main.dart' "
'Class: A',
'Const class cannot become non-const: '
"Library:'memory:///main.dart' "
'Class: D'
]));
});
test('no error when removing const constructor while adding another',
() async {
final initialSource = '''
var globalVariable;
class A {
final String s;
const A(this.s);
}
main() {
globalVariable = const A('hello');
print(globalVariable.s);
}
''';
final deltaSource = '''
var globalVariable;
class A {
final String s;
A(this.s);
const A.named(this.s);
}
main() {
print('hello world');
}
''';
final (:initial, :delta) =
await compileComponents(initialSource, deltaSource);
expect(deltaInspector.compareGenerations(initial, delta), isEmpty);
});
test('rejection when removing a field', () async {
final initialSource = '''
var globalVariable;
class A {
final String s, t, w;
const A(this.s, this.t, this.w);
}
main() {
globalVariable = const A('hello', 'world', '!');
print(globalVariable.s);
}
''';
final deltaSource = '''
var globalVariable;
class A {
final String s, t;
const A(this.s, this.t);
}
main() {
print('hello world');
}
''';
final (:initial, :delta) =
await compileComponents(initialSource, deltaSource);
expect(
deltaInspector.compareGenerations(initial, delta),
unorderedEquals([
'Const class cannot remove fields: '
"Library:'memory:///main.dart' Class: A"
]));
});
test('rejection when removing a field while adding another', () async {
final initialSource = '''
var globalVariable;
class A {
final String s, t, w;
const A(this.s, this.t, this.w);
}
main() {
globalVariable = const A('hello', 'world', '!');
print(globalVariable.s);
}
''';
final deltaSource = '''
var globalVariable;
class A {
final String s, t, x;
const A(this.s, this.t, this.x);
}
main() {
print('hello world');
}
''';
final (:initial, :delta) =
await compileComponents(initialSource, deltaSource);
expect(
deltaInspector.compareGenerations(initial, delta),
unorderedEquals([
'Const class cannot remove fields: '
"Library:'memory:///main.dart' Class: A"
]));
});
test('no error when removing field while also making class const',
() async {
final initialSource = '''
var globalVariable;
class A {
final String s, t, w;
A(this.s, this.t, this.w);
}
main() {
globalVariable = A('hello', 'world', '!');
print(globalVariable.s);
}
''';
final deltaSource = '''
var globalVariable;
class A {
final String s, t;
const A(this.s, this.t);
}
main() {
print('hello world');
}
''';
final (:initial, :delta) =
await compileComponents(initialSource, deltaSource);
expect(() => deltaInspector.compareGenerations(initial, delta),
returnsNormally);
});
});
}
/// Test only helper compiles [initialSource] and [deltaSource] and returns two
/// kernel components.
Future<({Component initial, Component delta})> compileComponents(
String initialSource, String deltaSource) async {
final fileName = 'main.dart';
final fileUri = Uri(scheme: 'memory', host: '', path: fileName);
final memoryFileMap = {fileName: initialSource};
final initialResult = await componentFromMemory(memoryFileMap, fileUri);
expect(initialResult.errors, isEmpty,
reason: 'Initial source produced compile time errors.');
memoryFileMap[fileName] = deltaSource;
final deltaResult = await componentFromMemory(memoryFileMap, fileUri);
expect(deltaResult.errors, isEmpty,
reason: 'Delta source produced compile time errors.');
return (
initial: initialResult.ddcResult.component,
delta: deltaResult.ddcResult.component
);
}