| // 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'; |