blob: 116bddd10cc8e99a2964d5c5562ea2c1f99c962c [file] [log] [blame]
// 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.
library kernel.type_substitute_bounds_test;
import 'package:kernel/kernel.dart';
import 'package:kernel/type_algebra.dart';
import 'package:test/test.dart';
import 'type_parser.dart';
final List<TestCase> testCases = <TestCase>[
testCase('T', {'T': bound('_', 'String')}, 'String'),
testCase('List<T>', {'T': bound('_', 'String')}, 'List<String>'),
testCase('List<List<T>>', {'T': bound('_', 'String')}, 'List<List<String>>'),
testCase('(T) => T', {'T': bound('_', 'String')}, '(_) => String'),
testCase('<G>(G,T) => T', {'T': bound('_', 'String')}, '<G>(G,_) => String'),
testCase(
'<G>(G,x:T) => T', {'T': bound('_', 'String')}, '<G>(G,x:_) => String'),
testCase('<G:T>(G) => G', {'T': bound('_', 'String')}, '<G:_>(G) => G'),
testCase('<G:T>(G) => G', {'T': bound('int', 'num')}, '<G:int>(G) => G'),
testCase('<G>(T,G) => void', {'T': bound('_', 'String')}, '<G>(_,G) => void'),
testCase('(T) => void', {'T': bound('_', 'String')}, '(_) => void'),
testCase('(int) => T', {'T': bound('_', 'String')}, '(int) => String'),
testCase('(int) => int', {'T': bound('_', 'String')}, '(int) => int'),
testCase('((T) => int) => int', {'T': bound('_', 'String')},
'((String) => int) => int'),
testCase('<E>(<F>(T) => int) => int', {'T': bound('_', 'String')},
'<E>(<F>(String) => int) => int'),
testCase('(<F>(T) => int) => int', {'T': bound('_', 'String')},
'(<F>(String) => int) => int'),
testCase('<E>((T) => int) => int', {'T': bound('_', 'String')},
'<E>((String) => int) => int'),
testCase('(T?) =>* String', {'T': bound('int', 'int')}, '(int?) =>* String'),
];
class TestCase {
final String type;
final Map<String, TypeBound> bounds;
final String expected;
TestCase(this.type, this.bounds, this.expected);
@override
String toString() {
var substitution = bounds.keys.map((key) {
var bound = bounds[key]!;
return '${bound.lower} <: $key <: ${bound.upper}';
}).join(',');
return '$type [$substitution] <: $expected';
}
}
class TypeBound {
final String lower, upper;
TypeBound(this.lower, this.upper);
}
TypeBound bound(String lower, String upper) => new TypeBound(lower, upper);
TestCase testCase(String type, Map<String, TypeBound> bounds, String expected) {
return new TestCase(type, bounds, expected);
}
void main() {
for (var testCase in testCases) {
test('$testCase', () {
var environment = new LazyTypeEnvironment();
var type = environment.parse(testCase.type);
var upperBounds = <TypeParameter, DartType>{};
var lowerBounds = <TypeParameter, DartType>{};
testCase.bounds.forEach((String name, TypeBound bounds) {
var parameter = environment.getTypeParameter(name)!;
upperBounds[parameter] = environment.parse(bounds.upper);
lowerBounds[parameter] = environment.parse(bounds.lower);
});
var substituted =
Substitution.fromUpperAndLowerBounds(upperBounds, lowerBounds)
.substituteType(type);
var expected = environment.parse(testCase.expected);
if (substituted != expected) {
fail('Expected `$expected` but got `$substituted`');
}
});
}
}