2e718613aa
Some of the kernel unit tests can now be run using: tools/test.py -cdartk -rself_check language co19 This will search the pkg/ folder for files matching *_self_check.dart and run each program with the compiled output as argument. If there is no compiler, the test case itself is given as argument. These testers are always run in batch-mode. This type of test has no expected output, but is intended to check itself by testing that certain invariants are not violated while processing the given data set. The 'self_check' runtime is not specifically tied to kernel, although only kernel is using it at the moment. There is also a new option --skip-compilation which skips the compiler step. It doesn't interact nicely with the status files, but can still be useful for a quick offline test. Current limitations: - All self-check tests are treated as the same test case. If one fails, the remaining self-check testers don't run for that input. - There is no way to run a subset of the self-check tests, or filter them based on what compiler was used. - Tests that are expected to fail in the compiler show up as errors when skipping compilation. BUG= R=kustermann@google.com, whesse@google.com Review-Url: https://codereview.chromium.org/2549793002 .
160 lines
5.9 KiB
Dart
160 lines
5.9 KiB
Dart
// Copyright (c) 2016, 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:kernel/kernel.dart';
|
|
import 'package:kernel/class_hierarchy.dart';
|
|
import 'package:test/test.dart';
|
|
import 'class_hierarchy_basic.dart';
|
|
import 'dart:io';
|
|
import 'dart:math';
|
|
import 'self_check_util.dart';
|
|
|
|
main(List<String> args) {
|
|
runSelfCheck(args, (String filename) {
|
|
testClassHierarchyOnProgram(loadProgramFromBinary(filename));
|
|
});
|
|
}
|
|
|
|
void testClassHierarchyOnProgram(Program program, {bool verbose: false}) {
|
|
BasicClassHierarchy basic = new BasicClassHierarchy(program);
|
|
ClassHierarchy classHierarchy = new ClassHierarchy(program);
|
|
int total = classHierarchy.classes.length;
|
|
int progress = 0;
|
|
for (var class1 in classHierarchy.classes) {
|
|
for (var class2 in classHierarchy.classes) {
|
|
bool isSubclass = classHierarchy.isSubclassOf(class1, class2);
|
|
bool isSubmixture = classHierarchy.isSubmixtureOf(class1, class2);
|
|
bool isSubtype = classHierarchy.isSubtypeOf(class1, class2);
|
|
var asInstance = classHierarchy.getClassAsInstanceOf(class1, class2);
|
|
if (isSubclass != basic.isSubclassOf(class1, class2)) {
|
|
fail('isSubclassOf(${class1.name}, ${class2.name}) returned '
|
|
'$isSubclass but should be ${!isSubclass}');
|
|
}
|
|
if (isSubmixture != basic.isSubmixtureOf(class1, class2)) {
|
|
fail('isSubmixtureOf(${class1.name}, ${class2.name}) returned '
|
|
'$isSubclass but should be ${!isSubclass}');
|
|
}
|
|
if (isSubtype != basic.isSubtypeOf(class1, class2)) {
|
|
fail('isSubtypeOf(${class1.name}, ${class2.name}) returned '
|
|
'$isSubtype but should be ${!isSubtype}');
|
|
}
|
|
if (asInstance != basic.getClassAsInstanceOf(class1, class2)) {
|
|
fail('asInstanceOf(${class1.name}, ${class2.name}) returned '
|
|
'$asInstance but should be '
|
|
'${basic.getClassAsInstanceOf(class1, class2)}');
|
|
}
|
|
}
|
|
++progress;
|
|
if (verbose) {
|
|
stdout.write('\rSubclass queries ${100 * progress ~/ total}%');
|
|
}
|
|
}
|
|
Set<Name> names = new Set<Name>();
|
|
for (var classNode in classHierarchy.classes) {
|
|
for (var member in classNode.members) {
|
|
names.add(member.name);
|
|
}
|
|
}
|
|
List<Name> nameList = names.toList();
|
|
progress = 0;
|
|
for (var classNode in classHierarchy.classes) {
|
|
Iterable<Name> candidateNames = <Iterable<Name>>[
|
|
basic.gettersAndCalls[classNode].keys,
|
|
basic.setters[classNode].keys,
|
|
pickRandom(nameList, 100)
|
|
].expand((x) => x);
|
|
for (Name name in candidateNames) {
|
|
Member expectedGetter =
|
|
basic.getDispatchTarget(classNode, name, setter: false);
|
|
Member expectedSetter =
|
|
basic.getDispatchTarget(classNode, name, setter: true);
|
|
Member actualGetter =
|
|
classHierarchy.getDispatchTarget(classNode, name, setter: false);
|
|
Member actualSetter =
|
|
classHierarchy.getDispatchTarget(classNode, name, setter: true);
|
|
if (actualGetter != expectedGetter) {
|
|
fail('lookupGetter($classNode, $name) returned '
|
|
'$actualGetter but should be $expectedGetter');
|
|
}
|
|
if (actualSetter != expectedSetter) {
|
|
fail('lookupSetter($classNode, $name) returned '
|
|
'$actualSetter but should be $expectedSetter');
|
|
}
|
|
}
|
|
++progress;
|
|
if (verbose) {
|
|
stdout.write('\rDispatch queries ${100 * progress ~/ total}%');
|
|
}
|
|
}
|
|
progress = 0;
|
|
for (var classNode in classHierarchy.classes) {
|
|
Iterable<Name> candidateNames = [
|
|
basic.interfaceGettersAndCalls[classNode].keys,
|
|
basic.interfaceSetters[classNode].keys,
|
|
pickRandom(nameList, 100)
|
|
].expand((x) => x);
|
|
for (Name name in candidateNames) {
|
|
Member expectedGetter =
|
|
basic.getInterfaceMember(classNode, name, setter: false);
|
|
Member expectedSetter =
|
|
basic.getInterfaceMember(classNode, name, setter: true);
|
|
Member actualGetter =
|
|
classHierarchy.getInterfaceMember(classNode, name, setter: false);
|
|
Member actualSetter =
|
|
classHierarchy.getInterfaceMember(classNode, name, setter: true);
|
|
if (actualGetter != expectedGetter) {
|
|
fail('getInterfaceMember($classNode, $name) returned '
|
|
'$actualGetter but should be $expectedGetter');
|
|
}
|
|
if (actualSetter != expectedSetter) {
|
|
fail('getInterfaceMember($classNode, $name, setter: true) '
|
|
'returned $actualSetter but should be $expectedSetter');
|
|
}
|
|
}
|
|
++progress;
|
|
if (verbose) {
|
|
stdout.write('\rInterface queries ${100 * progress ~/ total}%');
|
|
}
|
|
}
|
|
for (var classNode in classHierarchy.classes) {
|
|
String getHash(member, superMember, setter) {
|
|
String eq = setter ? '=' : '';
|
|
return '$member$eq overrides $superMember$eq';
|
|
}
|
|
|
|
Set<String> expectedOverrides = new Set<String>();
|
|
basic.forEachOverridePair(classNode, (member, superMember, setter) {
|
|
expectedOverrides.add(getHash(member, superMember, setter));
|
|
});
|
|
Set<String> actualOverrides = new Set<String>();
|
|
classHierarchy.forEachOverridePair(classNode,
|
|
(member, superMember, setter) {
|
|
actualOverrides.add(getHash(member, superMember, setter));
|
|
});
|
|
for (var actual in actualOverrides) {
|
|
if (!expectedOverrides.contains(actual)) {
|
|
fail("forEachOverridePair($classNode) should not report that $actual");
|
|
}
|
|
}
|
|
for (var expected in expectedOverrides) {
|
|
if (!actualOverrides.contains(expected)) {
|
|
fail("forEachOverridePair($classNode) did not report that $expected");
|
|
}
|
|
}
|
|
}
|
|
if (verbose) {
|
|
print('\rProgress 100%. Done.');
|
|
}
|
|
}
|
|
|
|
var random = new Random(12345);
|
|
|
|
List/*<T>*/ pickRandom/*<T>*/(List/*<T>*/ items, int n) {
|
|
var result = /*<T>*/ [];
|
|
for (int i = 0; i < n; ++i) {
|
|
result.add(items[random.nextInt(items.length)]);
|
|
}
|
|
return result;
|
|
}
|