| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| |
| import 'package:flutter_tools/src/base/context.dart'; |
| |
| import '../src/common.dart'; |
| |
| void main() { |
| group('AppContext', () { |
| group('global getter', () { |
| bool called; |
| |
| setUp(() { |
| called = false; |
| }); |
| |
| test('returns non-null context in the root zone', () { |
| expect(context, isNotNull); |
| }); |
| |
| test('returns root context in child of root zone if zone was manually created', () { |
| final Zone rootZone = Zone.current; |
| final AppContext rootContext = context; |
| runZoned<void>(() { |
| expect(Zone.current, isNot(rootZone)); |
| expect(Zone.current.parent, rootZone); |
| expect(context, rootContext); |
| called = true; |
| }); |
| expect(called, isTrue); |
| }); |
| |
| test('returns child context after run', () async { |
| final AppContext rootContext = context; |
| await rootContext.run<void>(name: 'child', body: () { |
| expect(context, isNot(rootContext)); |
| expect(context.name, 'child'); |
| called = true; |
| }); |
| expect(called, isTrue); |
| }); |
| |
| test('returns grandchild context after nested run', () async { |
| final AppContext rootContext = context; |
| await rootContext.run<void>(name: 'child', body: () async { |
| final AppContext childContext = context; |
| await childContext.run<void>(name: 'grandchild', body: () { |
| expect(context, isNot(rootContext)); |
| expect(context, isNot(childContext)); |
| expect(context.name, 'grandchild'); |
| called = true; |
| }); |
| }); |
| expect(called, isTrue); |
| }); |
| |
| test('scans up zone hierarchy for first context', () async { |
| final AppContext rootContext = context; |
| await rootContext.run<void>(name: 'child', body: () { |
| final AppContext childContext = context; |
| runZoned<void>(() { |
| expect(context, isNot(rootContext)); |
| expect(context, same(childContext)); |
| expect(context.name, 'child'); |
| called = true; |
| }); |
| }); |
| expect(called, isTrue); |
| }); |
| }); |
| |
| group('operator[]', () { |
| test('still finds values if async code runs after body has finished', () async { |
| final Completer<void> outer = Completer<void>(); |
| final Completer<void> inner = Completer<void>(); |
| String value; |
| await context.run<void>( |
| body: () { |
| outer.future.then<void>((_) { |
| value = context.get<String>(); |
| inner.complete(); |
| }); |
| }, |
| fallbacks: <Type, Generator>{ |
| String: () => 'value', |
| }, |
| ); |
| expect(value, isNull); |
| outer.complete(); |
| await inner.future; |
| expect(value, 'value'); |
| }); |
| |
| test('caches generated override values', () async { |
| int consultationCount = 0; |
| String value; |
| await context.run<void>( |
| body: () async { |
| final StringBuffer buf = StringBuffer(context.get<String>()); |
| buf.write(context.get<String>()); |
| await context.run<void>(body: () { |
| buf.write(context.get<String>()); |
| }); |
| value = buf.toString(); |
| }, |
| overrides: <Type, Generator>{ |
| String: () { |
| consultationCount++; |
| return 'v'; |
| }, |
| }, |
| ); |
| expect(value, 'vvv'); |
| expect(consultationCount, 1); |
| }); |
| |
| test('caches generated fallback values', () async { |
| int consultationCount = 0; |
| String value; |
| await context.run( |
| body: () async { |
| final StringBuffer buf = StringBuffer(context.get<String>()); |
| buf.write(context.get<String>()); |
| await context.run<void>(body: () { |
| buf.write(context.get<String>()); |
| }); |
| value = buf.toString(); |
| }, |
| fallbacks: <Type, Generator>{ |
| String: () { |
| consultationCount++; |
| return 'v'; |
| }, |
| }, |
| ); |
| expect(value, 'vvv'); |
| expect(consultationCount, 1); |
| }); |
| |
| test('returns null if generated value is null', () async { |
| final String value = await context.run<String>( |
| body: () => context.get<String>(), |
| overrides: <Type, Generator>{ |
| String: () => null, |
| }, |
| ); |
| expect(value, isNull); |
| }); |
| |
| test('throws if generator has dependency cycle', () async { |
| final Future<String> value = context.run<String>( |
| body: () async { |
| return context.get<String>(); |
| }, |
| fallbacks: <Type, Generator>{ |
| int: () => int.parse(context.get<String>()), |
| String: () => '${context.get<double>()}', |
| double: () => context.get<int>() * 1.0, |
| }, |
| ); |
| try { |
| await value; |
| fail('ContextDependencyCycleException expected but not thrown.'); |
| } on ContextDependencyCycleException catch (e) { |
| expect(e.cycle, <Type>[String, double, int]); |
| expect(e.toString(), 'Dependency cycle detected: String -> double -> int'); |
| } |
| }); |
| }); |
| |
| group('run', () { |
| test('returns the value returned by body', () async { |
| expect(await context.run<int>(body: () => 123), 123); |
| expect(await context.run<String>(body: () => 'value'), 'value'); |
| expect(await context.run<int>(body: () async => 456), 456); |
| }); |
| |
| test('passes name to child context', () async { |
| await context.run<void>(name: 'child', body: () { |
| expect(context.name, 'child'); |
| }); |
| }); |
| |
| group('fallbacks', () { |
| bool called; |
| |
| setUp(() { |
| called = false; |
| }); |
| |
| test('are applied after parent context is consulted', () async { |
| final String value = await context.run<String>( |
| body: () { |
| return context.run<String>( |
| body: () { |
| called = true; |
| return context.get<String>(); |
| }, |
| fallbacks: <Type, Generator>{ |
| String: () => 'child', |
| }, |
| ); |
| }, |
| ); |
| expect(called, isTrue); |
| expect(value, 'child'); |
| }); |
| |
| test('are not applied if parent context supplies value', () async { |
| bool childConsulted = false; |
| final String value = await context.run<String>( |
| body: () { |
| return context.run<String>( |
| body: () { |
| called = true; |
| return context.get<String>(); |
| }, |
| fallbacks: <Type, Generator>{ |
| String: () { |
| childConsulted = true; |
| return 'child'; |
| }, |
| }, |
| ); |
| }, |
| fallbacks: <Type, Generator>{ |
| String: () => 'parent', |
| }, |
| ); |
| expect(called, isTrue); |
| expect(value, 'parent'); |
| expect(childConsulted, isFalse); |
| }); |
| |
| test('may depend on one another', () async { |
| final String value = await context.run<String>( |
| body: () { |
| return context.get<String>(); |
| }, |
| fallbacks: <Type, Generator>{ |
| int: () => 123, |
| String: () => '-${context.get<int>()}-', |
| }, |
| ); |
| expect(value, '-123-'); |
| }); |
| }); |
| |
| group('overrides', () { |
| test('intercept consultation of parent context', () async { |
| bool parentConsulted = false; |
| final String value = await context.run<String>( |
| body: () { |
| return context.run<String>( |
| body: () => context.get<String>(), |
| overrides: <Type, Generator>{ |
| String: () => 'child', |
| }, |
| ); |
| }, |
| fallbacks: <Type, Generator>{ |
| String: () { |
| parentConsulted = true; |
| return 'parent'; |
| }, |
| }, |
| ); |
| expect(value, 'child'); |
| expect(parentConsulted, isFalse); |
| }); |
| }); |
| }); |
| }); |
| } |