blob: 2925bafa4e6ac967dd0248b14840c4f9a3ea8801 [file] [log] [blame]
// Copyright (c) 2021, 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.
@TestOn('vm && !windows')
library;
import 'dart:io';
import 'package:test/test.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;
import 'builder_test_base.dart';
void main() {
test('builds renderers from multiple annotations', () async {
await testMustachioBuilder(
'''
class Foo {
String s1 = 'hello';
}
class Bar {}
class Baz {}
''',
libraryFrontMatter: '''
@Renderer(#renderFoo, Context<Foo>(), 'foo')
@Renderer(#renderBar, Context<Bar>(), 'bar')
library foo;
import 'annotations.dart';
''',
additionalAssets: () => [
d.dir('lib/templates', [
d.file('foo.html', '{{ >foo_header }}'),
d.file('_foo_header.html', 'EMPTY'),
]),
],
);
var renderersLibrary =
await resolveGeneratedLibrary2(aotRenderersForHtmlPath);
expect(renderersLibrary.getTopLevelFunction('renderFoo'), isNotNull);
expect(renderersLibrary.getTopLevelFunction('renderBar'), isNotNull);
expect(
renderersLibrary.getTopLevelFunction('_renderFoo_partial_foo_header_0'),
isNotNull);
}, timeout: Timeout.factor(2));
test('builds a public API render function', () async {
await testMustachioBuilder(
'''
class Foo<T> {
String s1 = 'hello';
}
''',
libraryFrontMatter: '''
@Renderer(#renderFoo, Context<Foo>(), 'foo')
library foo;
import 'annotations.dart';
''',
additionalAssets: () => [
d.dir('lib/templates', [
d.file('foo.html', 's1 is {{ s1 }}'),
]),
d.dir('md', [
d.file('foo.md', 's1 is {{ s1 }}'),
]),
],
);
var generatedContent = await File(aotRenderersForHtmlPath).readAsString();
expect(generatedContent, contains('String renderFoo<T>(Foo<T> context0)'));
});
test('builds a private render function for a partial', () async {
await testMustachioBuilder(
'''
class Foo<T> {
String s1 = 'hello';
}
''',
libraryFrontMatter: '''
@Renderer(#renderFoo, Context<Foo>(), 'foo')
library foo;
import 'annotations.dart';
''',
additionalAssets: () => [
d.dir('lib', [
d.dir('templates', [
d.file('foo.html', '{{ >foo_header }}'),
d.file('_foo_header.html', 's1 is {{ s1 }}'),
]),
]),
],
);
var generatedContent = await File(aotRenderersForHtmlPath).readAsString();
expect(generatedContent,
contains('String _renderFoo_partial_foo_header_0<T>(Foo<T> context0)'));
});
test('builds a renderer for a generic, bounded type', () async {
await testMustachioBuilder('''
class Foo<T extends num> {
String s1 = 'hello';
}
class Bar {}
class Baz {}
''');
var renderersLibrary =
await resolveGeneratedLibrary2(aotRenderersForHtmlPath);
var fooRenderFunction = renderersLibrary.getTopLevelFunction('renderFoo')!;
expect(fooRenderFunction.typeParameters2, hasLength(1));
var fBound = fooRenderFunction.typeParameters2.single.bound!;
expect(fBound.getDisplayString(), equals('num'));
});
test('deduplicates partials which share context type LUB', () async {
await testMustachioBuilder(
'''
abstract class Base {
String get s1;
}
class Foo implements Base {
@override
String s1 = 'F';
}
class Bar implements Base {
@override
String s1 = 'B';
}
''',
libraryFrontMatter: '''
@Renderer(#renderFoo, Context<Foo>(), 'foo')
@Renderer(#renderBar, Context<Bar>(), 'bar')
library foo;
import 'annotations.dart';
''',
additionalAssets: () => [
d.dir('lib/templates', [
d.file('foo.html', '{{ >base }}'),
d.file('bar.html', '{{ >base }}'),
d.file('_base.html', 's1 is {{ s1 }}'),
]),
],
);
var generatedContent = await File(aotRenderersForHtmlPath).readAsString();
expect(
generatedContent,
contains('String _renderFoo_partial_base_0(Foo context0) => '
'_deduplicated__base(context0);\n'),
);
expect(
generatedContent,
contains('String _renderBar_partial_base_0(Bar context0) => '
'_deduplicated__base(context0);\n'),
);
expect(
generatedContent,
contains('String _deduplicated__base(Base context0)'),
);
});
test('deduplicates partials used multiple times in the same template',
() async {
await testMustachioBuilder(
'''
abstract class Foo {
List<A> get l1;
List<B> get l2;
}
class Base {
String s1 = 's1';
}
class A extends Base {}
class B extends Base {}
''',
libraryFrontMatter: '''
@Renderer(#renderFoo, Context<Foo>(), 'foo')
library foo;
import 'annotations.dart';
''',
additionalAssets: () => [
d.dir('lib', [
d.dir('templates', [
d.file('foo.html', '''
{{ #l1 }}
{{ >base }}
{{ /l1 }}
{{ #l2 }}
{{ >base }}
{{ /l2 }}
'''),
d.file('_base.html', 's1 is {{ s1 }}'),
]),
]),
],
);
var generatedContent = await File(aotRenderersForHtmlPath).readAsString();
expect(
generatedContent,
contains('String _renderFoo_partial_base_0(A context1) => '
'_deduplicated__base(context1);\n'),
);
expect(
generatedContent,
contains('String _renderFoo_partial_base_1(B context1) => '
'_deduplicated__base(context1);\n'),
);
expect(
generatedContent,
contains('String _deduplicated__base(Base context0)'),
);
});
test('does not deduplicate partials when attempting to do so throws',
() async {
await testMustachioBuilder(
'''
abstract class Base {}
class Foo implements Base {
// Not part of the interface of [Base].
String s1 = 'F';
}
class Bar implements Base {
// Not part of the interface of [Base].
String s1 = 'B';
}
''',
libraryFrontMatter: '''
@Renderer(#renderFoo, Context<Foo>(), 'foo')
@Renderer(#renderBar, Context<Bar>(), 'bar')
library foo;
import 'annotations.dart';
''',
additionalAssets: () => [
d.dir('lib/templates', [
d.file('foo.html', '{{ >base }}'),
d.file('bar.html', '{{ >base }}'),
d.file('_base.html', 's1 is {{ s1 }}'),
]),
],
);
var generatedContent = await File(aotRenderersForHtmlPath).readAsString();
expect(
generatedContent,
contains('String _renderFoo_partial_base_0(Foo context0) {'),
);
expect(
generatedContent,
contains('String _renderBar_partial_base_0(Bar context0) {'),
);
});
}
String get aotRenderersForHtmlPath =>
'${d.sandbox}/foo_package/lib/foo.aot_renderers_for_html.dart';