blob: 12aa98a87491e9ee17d499a8292f7919e4115bc6 [file] [log] [blame]
// Copyright (c) 2013, 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:unittest/unittest.dart';
import 'dart:async' show Future;
import 'dart:io' show Platform;
import '../lib/src/export_map.dart';
import '../../../../../tests/compiler/dart2js/memory_source_file_helper.dart'
show MemorySourceFileProvider;
import '../../compiler/implementation/mirrors/mirrors.dart'
show MirrorSystem, LibraryMirror;
import '../../compiler/implementation/mirrors/dart2js_mirror.dart'
as source_mirrors;
Future<MirrorSystem> mirrorSystemFor(Map<String, String> memorySourceFiles) {
var provider = new MemorySourceFileProvider(memorySourceFiles);
handler(Uri uri, int begin, int end, String message, kind) {}
var script = Uri.base.resolveUri(Platform.script);
var libraryRoot = script.resolve('../../../../');
// Read packages from 'memory:packages/'.
var packageRoot = Uri.parse('memory:packages/');
var libraries = <Uri>[];
memorySourceFiles.forEach((String path, _) {
if (path.startsWith('packages/')) {
// Analyze files from 'packages/' as packages.
libraries.add(new Uri(scheme: 'package', path: path.substring(9)));
} else {
libraries.add(new Uri(scheme: 'memory', path: path));
}
});
libraries.add(new Uri(scheme: 'dart', path: 'async'));
return source_mirrors.analyze(
libraries, libraryRoot, packageRoot, provider, handler);
}
Future<ExportMap> testExports(Map<String, String> sourceFiles) {
return mirrorSystemFor(sourceFiles).then((mirrors) {
libMirror = (uri) => mirrors.libraries[Uri.parse(uri)];
return new ExportMap(mirrors);
});
}
Function libMirror;
main() {
group('ExportMap', () {
test('with an empty library', () {
return testExports({'lib.dart': ''}).then((map) {
var lib = libMirror('memory:lib.dart');
var nonexistent = libMirror('memory:nonexistent.dart');
var expectedExports = {};
expectedExports[lib] = [];
expect(map.exports, equals(expectedExports));
expect(map.transitiveExports(lib), isEmpty);
expect(map.transitiveExports(nonexistent), isEmpty);
});
});
test('with one library with one export', () {
return testExports({
'a.dart': 'export "b.dart";',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b)
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b)
]));
expect(map.exports[b], isEmpty);
expect(map.transitiveExports(b), isEmpty);
});
});
test('with one library with multiple exports', () {
return testExports({
'a.dart': 'export "b.dart";\nexport "c.dart";',
'b.dart': '', 'c.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b),
new Export(a, c)
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b),
new Export(a, c)
]));
expect(map.exports[b], isEmpty);
expect(map.transitiveExports(b), isEmpty);
expect(map.exports[c], isEmpty);
expect(map.transitiveExports(c), isEmpty);
});
});
test('with two libraries each with one export', () {
return testExports({
'a.dart': 'export "a_export.dart";',
'b.dart': 'export "b_export.dart";',
'a_export.dart': '',
'b_export.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var a_export = libMirror('memory:a_export.dart');
var b_export = libMirror('memory:b_export.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, a_export),
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, a_export),
]));
expect(map.transitiveExports(b), unorderedEquals([
new Export(b, b_export),
]));
expect(map.exports[b], unorderedEquals([
new Export(b, b_export)
]));
expect(map.exports[a_export], isEmpty);
expect(map.transitiveExports(a_export), isEmpty);
expect(map.exports[b_export], isEmpty);
expect(map.transitiveExports(b_export), isEmpty);
});
});
test('with a transitive export', () {
return testExports({
'a.dart': 'export "b.dart";',
'b.dart': 'export "c.dart";',
'c.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b),
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b),
new Export(a, c),
]));
expect(map.exports[b], unorderedEquals([
new Export(b, c),
]));
expect(map.transitiveExports(b), unorderedEquals([
new Export(b, c),
]));
expect(map.exports[c], isEmpty);
expect(map.transitiveExports(c), isEmpty);
});
});
test('with an export through an import', () {
return testExports({
'a.dart': 'import "b.dart";',
'b.dart': 'export "c.dart";',
'c.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
expect(map.exports[b], unorderedEquals([
new Export(b, c),
]));
expect(map.transitiveExports(b), unorderedEquals([
new Export(b, c),
]));
expect(map.exports[a], isEmpty);
expect(map.exports[c], isEmpty);
expect(map.transitiveExports(a), isEmpty);
expect(map.transitiveExports(c), isEmpty);
});
});
test('with an export with a show combinator', () {
return testExports({
'a.dart': 'export "b.dart" show x, y;',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b, show: ['x', 'y'])
]));
});
});
test('with an export with a hide combinator', () {
return testExports({
'a.dart': 'export "b.dart" hide x, y;',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b, hide: ['x', 'y'])
]));
});
});
test('with an export with a show and a hide combinator', () {
return testExports({
'a.dart': 'export "b.dart" show x, y hide y, z;',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b, show: ['x'])
]));
});
});
test('composes transitive exports', () {
return testExports({
'a.dart': 'export "b.dart" hide x;',
'b.dart': 'export "c.dart" hide y;',
'c.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b, hide: ['x']),
new Export(a, c, hide: ['x', 'y'])
]));
});
});
test('merges adjacent exports', () {
return testExports({
'a.dart': '''
export "b.dart" show x, y;
export "b.dart" hide y, z;
''',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b, hide: ['z']),
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b, hide: ['z']),
]));
});
});
test('merges adjacent exports transitively', () {
return testExports({
'a.dart': 'export "b.dart";\nexport "c.dart";',
'b.dart': 'export "d.dart" show x, y;',
'c.dart': 'export "d.dart" hide y, z;',
'd.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
var d = libMirror('memory:d.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b),
new Export(a, c),
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b),
new Export(a, c),
new Export(a, d, hide: ['z']),
]));
});
});
test('resolves package: exports', () {
return testExports({
'a.dart': 'export "package:b/b.dart";',
'packages/b/b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('package:b/b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b)
]));
});
});
test('ignores dart: exports', () {
return testExports({'a.dart': 'export "dart:async";'}).then((map) {
var a = libMirror('memory:a.dart');
expect(map.exports[a], isEmpty);
});
});
test('.parse() resolves package: imports', () {
return testExports({
'packages/a/a.dart': 'export "package:b/b.dart";',
'packages/b/b.dart': ''
}).then((map) {
var a = libMirror('package:a/a.dart');
var b = libMirror('package:b/b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b)
]));
});
});
test('.parse() ignores dart: imports', () {
return testExports({}).then((map) {
expect(map.exports, isEmpty);
});
});
});
group('Export', () {
test('normalizes hide and show', () {
expect(new Export(null, null, show: ['x', 'y'], hide: ['y', 'z']),
equals(new Export(null, null, show: ['x'])));
});
test("doesn't care about the order of show or hide", () {
expect(new Export(null, null, show: ['x', 'y']),
equals(new Export(null, null, show: ['y', 'x'])));
expect(new Export(null, null, hide: ['x', 'y']),
equals(new Export(null, null, hide: ['y', 'x'])));
});
test('with no combinators considers anything visible', () {
var export = new Export(null, null);
expect(export.isMemberVisible('x'), isTrue);
expect(export.isMemberVisible('y'), isTrue);
expect(export.isMemberVisible('z'), isTrue);
});
test('with hide combinators considers anything not hidden visible', () {
var export = new Export(null, null, hide: ['x', 'y']);
expect(export.isMemberVisible('x'), isFalse);
expect(export.isMemberVisible('y'), isFalse);
expect(export.isMemberVisible('z'), isTrue);
});
test('with show combinators considers anything not shown invisible', () {
var export = new Export(null, null, show: ['x', 'y']);
expect(export.isMemberVisible('x'), isTrue);
expect(export.isMemberVisible('y'), isTrue);
expect(export.isMemberVisible('z'), isFalse);
});
test('composing uses the parent exporter and child path', () {
return testExports({
'exporter1.dart': '',
'exporter2.dart': '',
'path1.dart': '',
'path2.dart': ''
}).then((map) {
var exporter1 = libMirror('memory:exporter1.dart');
var exporter2 = libMirror('memory:exporter2.dart');
var path1 = libMirror('memory:path1.dart');
var path2 = libMirror('memory:path2.dart');
expect(new Export(exporter1, path1)
.compose(new Export(exporter2, path2)),
equals(new Export(exporter1, path2)));
});
});
test('composing show . show takes the intersection', () {
expect(new Export(null, null, show: ['x', 'y'])
.compose(new Export(null, null, show: ['y', 'z'])),
equals(new Export(null, null, show: ['y'])));
});
test('composing show . hide takes the difference', () {
expect(new Export(null, null, show: ['x', 'y'])
.compose(new Export(null, null, hide: ['y', 'z'])),
equals(new Export(null, null, show: ['x'])));
});
test('composing hide . show takes the reverse difference', () {
expect(new Export(null, null, hide: ['x', 'y'])
.compose(new Export(null, null, show: ['y', 'z'])),
equals(new Export(null, null, show: ['z'])));
});
test('composing hide . hide takes the union', () {
expect(new Export(null, null, hide: ['x', 'y'])
.compose(new Export(null, null, hide: ['y', 'z'])),
equals(new Export(null, null, hide: ['x', 'y', 'z'])));
});
test('merging requires identical exporters and paths', () {
return testExports({
'exporter1.dart': '',
'exporter2.dart': '',
'path1.dart': '',
'path2.dart': ''
}).then((map) {
var exporter1 = libMirror('memory:exporter1.dart');
var exporter2 = libMirror('memory:exporter2.dart');
var path1 = libMirror('memory:path1.dart');
var path2 = libMirror('memory:path2.dart');
expect(() => new Export(exporter1, null)
.merge(new Export(exporter2, null)),
throwsA(isArgumentError));
expect(() => new Export(null, path1)
.merge(new Export(null, path2)),
throwsA(isArgumentError));
expect(new Export(null, null)
.merge(new Export(null, null)),
equals(new Export(null, null)));
});
});
test('merging show + show takes the union', () {
expect(new Export(null, null, show: ['x', 'y'])
.merge(new Export(null, null, show: ['y', 'z'])),
equals(new Export(null, null, show: ['x', 'y', 'z'])));
});
test('merging show + hide takes the difference', () {
expect(new Export(null, null, show: ['x', 'y'])
.merge(new Export(null, null, hide: ['y', 'z'])),
equals(new Export(null, null, hide: ['z'])));
});
test('merging hide + show takes the difference', () {
expect(new Export(null, null, hide: ['x', 'y'])
.merge(new Export(null, null, show: ['y', 'z'])),
equals(new Export(null, null, hide: ['x'])));
});
test('merging hide + hide takes the intersection', () {
expect(new Export(null, null, hide: ['x', 'y'])
.merge(new Export(null, null, hide: ['y', 'z'])),
equals(new Export(null, null, hide: ['y'])));
});
});
}