| Index: third_party/pkg/di/test/injector_generator_spec.dart
|
| diff --git a/third_party/pkg/di/test/injector_generator_spec.dart b/third_party/pkg/di/test/injector_generator_spec.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ca7faa02e9ccecb9fd095b8b0d0375ad05ec7b90
|
| --- /dev/null
|
| +++ b/third_party/pkg/di/test/injector_generator_spec.dart
|
| @@ -0,0 +1,706 @@
|
| +library di.test.injector_generator_spec;
|
| +
|
| +import 'dart:async';
|
| +
|
| +import 'package:barback/barback.dart';
|
| +import 'package:code_transformers/resolver.dart';
|
| +import 'package:code_transformers/tests.dart' as tests;
|
| +import 'package:di/transformer/injector_generator.dart';
|
| +import 'package:di/transformer/options.dart';
|
| +
|
| +import 'fixed-unittest.dart';
|
| +
|
| +main() {
|
| + describe('generator', () {
|
| + var injectableAnnotations = [];
|
| + var options = new TransformOptions(
|
| + injectableAnnotations: injectableAnnotations,
|
| + sdkDirectory: dartSdkDirectory);
|
| +
|
| + var resolvers = new Resolvers(dartSdkDirectory);
|
| +
|
| + var phases = [
|
| + [new InjectorGenerator(options, resolvers)]
|
| + ];
|
| +
|
| + it('transforms imports', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': 'import "package:a/car.dart"; main() {}',
|
| + 'a|lib/car.dart': '''
|
| + import 'package:inject/inject.dart';
|
| + import 'package:a/engine.dart';
|
| + import 'package:a/seat.dart' as seat;
|
| +
|
| + class Car {
|
| + @inject
|
| + Car(Engine e, seat.Seat s) {}
|
| + }
|
| + ''',
|
| + 'a|lib/engine.dart': CLASS_ENGINE,
|
| + 'a|lib/seat.dart': '''
|
| + import 'package:inject/inject.dart';
|
| + class Seat {
|
| + @inject
|
| + Seat();
|
| + }
|
| + ''',
|
| + },
|
| + imports: [
|
| + "import 'package:a/car.dart' as import_0;",
|
| + "import 'package:a/engine.dart' as import_1;",
|
| + "import 'package:a/seat.dart' as import_2;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Car: (f) => new import_0.Car(f(import_1.Engine), '
|
| + 'f(import_2.Seat)),',
|
| + 'import_1.Engine: (f) => new import_1.Engine(),',
|
| + 'import_2.Seat: (f) => new import_2.Seat(),',
|
| + ]);
|
| + });
|
| +
|
| + it('warns about parameterized classes', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': 'import "package:a/a.dart"; main() {}',
|
| + 'a|lib/a.dart': '''
|
| + import 'package:inject/inject.dart';
|
| + class Parameterized<T> {
|
| + @inject
|
| + Parameterized();
|
| + }
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'package:a/a.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Parameterized: (f) => new import_0.Parameterized(),',
|
| + ],
|
| + messages: [
|
| + 'warning: Parameterized is a parameterized type. '
|
| + '(package:a/a.dart 1 16)',
|
| + ]);
|
| + });
|
| +
|
| + it('skips and warns about parameterized constructor parameters', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': 'import "package:a/a.dart"; main() {}',
|
| + 'a|lib/a.dart': '''
|
| + import 'package:inject/inject.dart';
|
| + class Foo<T> {}
|
| + class Bar {
|
| + @inject
|
| + Bar(Foo<bool> f);
|
| + }
|
| + '''
|
| + },
|
| + messages: [
|
| + 'warning: Bar cannot be injected because Foo<bool> is a '
|
| + 'parameterized type. (package:a/a.dart 3 18)'
|
| + ]);
|
| + });
|
| +
|
| + it('allows un-parameterized parameters', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import 'package:inject/inject.dart';
|
| + class Foo<T> {}
|
| + class Bar {
|
| + @inject
|
| + Bar(Foo f);
|
| + }
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Bar: (f) => new import_0.Bar(f(import_0.Foo)),',
|
| + ]);
|
| + });
|
| +
|
| + it('follows exports', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': 'import "package:a/a.dart"; main() {}',
|
| + 'a|lib/a.dart': 'export "package:a/b.dart";',
|
| + 'a|lib/b.dart': CLASS_ENGINE
|
| + },
|
| + imports: [
|
| + "import 'package:a/b.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + ]);
|
| + });
|
| +
|
| + it('handles parts', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': 'import "package:a/a.dart"; main() {}',
|
| + 'a|lib/a.dart':
|
| + 'import "package:inject/inject.dart";\n'
|
| + 'part "b.dart";',
|
| + 'a|lib/b.dart': '''
|
| + part of a.a;
|
| + class Engine {
|
| + @inject
|
| + Engine();
|
| + }
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'package:a/a.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + ]);
|
| + });
|
| +
|
| + it('follows relative imports', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': 'import "package:a/a.dart"; main() {}',
|
| + 'a|lib/a.dart': 'import "b.dart";',
|
| + 'a|lib/b.dart': CLASS_ENGINE
|
| + },
|
| + imports: [
|
| + "import 'package:a/b.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + ]);
|
| + });
|
| +
|
| + it('handles relative imports', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': 'import "package:a/a.dart"; main() {}',
|
| + 'a|lib/a.dart': '''
|
| + import "package:inject/inject.dart";
|
| + import 'b.dart';
|
| + class Car {
|
| + @inject
|
| + Car(Engine engine);
|
| + }
|
| + ''',
|
| + 'a|lib/b.dart': CLASS_ENGINE
|
| + },
|
| + imports: [
|
| + "import 'package:a/a.dart' as import_0;",
|
| + "import 'package:a/b.dart' as import_1;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Car: (f) => new import_0.Car(f(import_1.Engine)),',
|
| + 'import_1.Engine: (f) => new import_1.Engine(),',
|
| + ]);
|
| + });
|
| +
|
| + it('handles web imports beside main', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': 'import "a.dart"; main() {}',
|
| + 'a|web/a.dart': CLASS_ENGINE
|
| + },
|
| + imports: [
|
| + "import 'a.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + ]);
|
| + });
|
| +
|
| + it('handles imports in main', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + $CLASS_ENGINE
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + ]);
|
| + });
|
| +
|
| + it('skips and warns on named constructors', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + class Engine {
|
| + @inject
|
| + Engine.foo();
|
| + }
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + messages: ['warning: Named constructors cannot be injected. '
|
| + '(web/main.dart 2 20)']);
|
| + });
|
| +
|
| + it('handles inject on classes', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + @inject
|
| + class Engine {}
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + ]);
|
| + });
|
| +
|
| + it('skips and warns when no default constructor', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + @inject
|
| + class Engine {
|
| + Engine.foo();
|
| + }
|
| + main() {}
|
| + '''
|
| + },
|
| + messages: ['warning: Engine cannot be injected because it does not '
|
| + 'have a default constructor. (web/main.dart 1 18)']);
|
| + });
|
| +
|
| + it('skips and warns on abstract types with no factory constructor', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + @inject
|
| + abstract class Engine { }
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + messages: ['warning: Engine cannot be injected because it is an '
|
| + 'abstract type with no factory constructor. '
|
| + '(web/main.dart 1 18)']);
|
| + });
|
| +
|
| + it('skips and warns on abstract types with implicit constructor', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + @inject
|
| + abstract class Engine {
|
| + Engine();
|
| + }
|
| + main() {}
|
| + '''
|
| + },
|
| + messages: ['warning: Engine cannot be injected because it is an '
|
| + 'abstract type with no factory constructor. '
|
| + '(web/main.dart 1 18)']);
|
| + });
|
| +
|
| + it('injects abstract types with factory constructors', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + @inject
|
| + abstract class Engine {
|
| + factory Engine() => new ConcreteEngine();
|
| + }
|
| +
|
| + class ConcreteEngine implements Engine {}
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + ]);
|
| + });
|
| +
|
| + it('injects this parameters', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + class Engine {
|
| + final Fuel fuel;
|
| + @inject
|
| + Engine(this.fuel);
|
| + }
|
| +
|
| + class Fuel {}
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(f(import_0.Fuel)),',
|
| + ]);
|
| + });
|
| +
|
| + it('narrows this parameters', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + class Engine {
|
| + final Fuel fuel;
|
| + @inject
|
| + Engine(JetFuel this.fuel);
|
| + }
|
| +
|
| + class Fuel {}
|
| + class JetFuel implements Fuel {}
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => '
|
| + 'new import_0.Engine(f(import_0.JetFuel)),',
|
| + ]);
|
| + });
|
| +
|
| + it('skips and warns on unresolved types', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + @inject
|
| + class Engine {
|
| + Engine(foo);
|
| + }
|
| +
|
| + @inject
|
| + class Car {
|
| + var foo;
|
| + Car(this.foo);
|
| + }
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + messages: ['warning: Engine cannot be injected because parameter '
|
| + 'type foo cannot be resolved. (web/main.dart 3 20)',
|
| + 'warning: Car cannot be injected because parameter type '
|
| + 'foo cannot be resolved. (web/main.dart 9 20)']);
|
| + });
|
| +
|
| + it('supports custom annotations', () {
|
| + injectableAnnotations.add('angular.NgInjectableService');
|
| + return generates(phases,
|
| + inputs: {
|
| + 'angular|lib/angular.dart': PACKAGE_ANGULAR,
|
| + 'a|web/main.dart': '''
|
| + import 'package:angular/angular.dart';
|
| + @NgInjectableService()
|
| + class Engine {
|
| + Engine();
|
| + }
|
| +
|
| + class Car {
|
| + @NgInjectableService()
|
| + Car();
|
| + }
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + 'import_0.Car: (f) => new import_0.Car(),',
|
| + ]).whenComplete(() {
|
| + injectableAnnotations.clear();
|
| + });
|
| + });
|
| +
|
| + it('supports default formal parameters', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + class Engine {
|
| + final Car car;
|
| +
|
| + @inject
|
| + Engine([Car this.car]);
|
| + }
|
| +
|
| + class Car {
|
| + @inject
|
| + Car();
|
| + }
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(f(import_0.Car)),',
|
| + 'import_0.Car: (f) => new import_0.Car(),',
|
| + ]);
|
| + });
|
| +
|
| + it('supports injectableTypes argument', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'di|lib/annotations.dart': PACKAGE_DI,
|
| + 'a|web/main.dart': '''
|
| + @Injectables(const[Engine])
|
| + library a;
|
| +
|
| + import 'package:di/annotations.dart';
|
| +
|
| + class Engine {
|
| + Engine();
|
| + }
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + ]);
|
| + });
|
| +
|
| + it('does not generate dart:core imports', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import 'package:inject/inject.dart';
|
| +
|
| + class Engine {
|
| + @inject
|
| + Engine(int i);
|
| + }
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(f(int)),',
|
| + ]);
|
| + });
|
| +
|
| + it('warns on private types', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| + @inject
|
| + class _Engine {
|
| + _Engine();
|
| + }
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + messages: ['warning: _Engine cannot be injected because it is a '
|
| + 'private type. (web/main.dart 1 18)']);
|
| + });
|
| +
|
| + it('warns on multiple constructors', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| +
|
| + @inject
|
| + class Engine {
|
| + Engine();
|
| +
|
| + @inject
|
| + Engine.foo();
|
| + }
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + messages: ['warning: Engine has more than one constructor '
|
| + 'annotated for injection. (web/main.dart 2 18)']);
|
| + });
|
| +
|
| + it('transforms main', () {
|
| + return tests.applyTransformers(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| +library main;
|
| +import 'package:di/auto_injector.dart';
|
| +import 'package:di/auto_injector.dart' as ai;
|
| +
|
| +main() {
|
| + var module = defaultInjector(modules: null, name: 'foo');
|
| + print(module);
|
| +
|
| + var module2 = ai.defaultInjector(modules: null, name: 'foo');
|
| + print(module2);
|
| +}''',
|
| + 'di|lib/auto_injector.dart': PACKAGE_AUTO
|
| + },
|
| + results: {
|
| + 'a|web/main.dart': '''
|
| +library main;
|
| +import 'main_static_injector.dart' as generated_static_injector;
|
| +import 'package:di/auto_injector.dart';
|
| +import 'package:di/auto_injector.dart' as ai;
|
| +
|
| +main() {
|
| + var module = generated_static_injector.createStaticInjector(modules: null, name: 'foo');
|
| + print(module);
|
| +
|
| + var module2 = generated_static_injector.createStaticInjector(modules: null, name: 'foo');
|
| + print(module2);
|
| +}'''
|
| +
|
| + });
|
| + });
|
| +
|
| + it('handles annotated dependencies', () {
|
| + return generates(phases,
|
| + inputs: {
|
| + 'a|web/main.dart': '''
|
| + import "package:inject/inject.dart";
|
| +
|
| + class Turbo {
|
| + const Turbo();
|
| + }
|
| +
|
| + @inject
|
| + class Engine {}
|
| +
|
| + @inject
|
| + class Car {
|
| + Car(@Turbo() Engine engine);
|
| + }
|
| +
|
| + main() {}
|
| + '''
|
| + },
|
| + imports: [
|
| + "import 'main.dart' as import_0;",
|
| + ],
|
| + generators: [
|
| + 'import_0.Engine: (f) => new import_0.Engine(),',
|
| + 'import_0.Car: (f) => new import_0.Car(f(import_0.Engine, import_0.Turbo)),',
|
| + ]);
|
| + });
|
| + });
|
| +}
|
| +
|
| +Future generates(List<List<Transformer>> phases,
|
| + {Map<String, String> inputs, Iterable<String> imports: const [],
|
| + Iterable<String> generators: const [],
|
| + Iterable<String> messages: const []}) {
|
| +
|
| + inputs['inject|lib/inject.dart'] = PACKAGE_INJECT;
|
| +
|
| + imports = imports.map((i) => '$i\n');
|
| + generators = generators.map((t) => ' $t\n');
|
| +
|
| + return tests.applyTransformers(phases,
|
| + inputs: inputs,
|
| + results: {
|
| + 'a|web/main_static_injector.dart': '''
|
| +$IMPORTS
|
| +${imports.join('')}$BOILER_PLATE
|
| +${generators.join('')}$FOOTER
|
| +''',
|
| + },
|
| + messages: messages);
|
| +}
|
| +
|
| +const String IMPORTS = '''
|
| +library a.web.main.generated_static_injector;
|
| +
|
| +import 'package:di/di.dart';
|
| +import 'package:di/static_injector.dart';
|
| +''';
|
| +
|
| +const String BOILER_PLATE = '''
|
| +Injector createStaticInjector({List<Module> modules, String name,
|
| + bool allowImplicitInjection: false}) =>
|
| + new StaticInjector(modules: modules, name: name,
|
| + allowImplicitInjection: allowImplicitInjection,
|
| + typeFactories: factories);
|
| +
|
| +final Map<Type, TypeFactory> factories = <Type, TypeFactory>{''';
|
| +
|
| +const String FOOTER = '''
|
| +};''';
|
| +
|
| +const String CLASS_ENGINE = '''
|
| + import 'package:inject/inject.dart';
|
| + class Engine {
|
| + @inject
|
| + Engine();
|
| + }''';
|
| +
|
| +const String PACKAGE_ANGULAR = '''
|
| +library angular;
|
| +
|
| +class NgInjectableService {
|
| + const NgInjectableService();
|
| +}
|
| +''';
|
| +
|
| +const String PACKAGE_INJECT = '''
|
| +library inject;
|
| +
|
| +class InjectAnnotation {
|
| + const InjectAnnotation._();
|
| +}
|
| +const inject = const InjectAnnotation._();
|
| +''';
|
| +
|
| +const String PACKAGE_DI = '''
|
| +library di.annotations;
|
| +
|
| +class Injectables {
|
| + final List<Type> types;
|
| + const Injectables(this.types);
|
| +}
|
| +''';
|
| +
|
| +const String PACKAGE_AUTO = '''
|
| +library di.auto_injector;
|
| +
|
| +defaultInjector({List modules, String name,
|
| + bool allowImplicitInjection: false}) => null;
|
| +''';
|
|
|