context_test.dart 8.31 KB
Newer Older
1 2 3 4
// 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.

5 6 7
import 'dart:async';

import 'package:flutter_tools/src/base/context.dart';
8 9

import '../src/common.dart';
10

11
void main() {
12
  group('AppContext', () {
13 14
    group('global getter', () {
      bool called;
15

16 17
      setUp(() {
        called = false;
18
      });
19

20 21 22
      test('returns non-null context in the root zone', () {
        expect(context, isNotNull);
      });
23

24 25 26
      test('returns root context in child of root zone if zone was manually created', () {
        final Zone rootZone = Zone.current;
        final AppContext rootContext = context;
27
        runZoned<void>(() {
28 29 30 31 32 33 34
          expect(Zone.current, isNot(rootZone));
          expect(Zone.current.parent, rootZone);
          expect(context, rootContext);
          called = true;
        });
        expect(called, isTrue);
      });
35

36
      test('returns child context after run', () async {
37
        final AppContext rootContext = context;
38
        await rootContext.run<void>(name: 'child', body: () {
39 40 41 42 43
          expect(context, isNot(rootContext));
          expect(context.name, 'child');
          called = true;
        });
        expect(called, isTrue);
44
      });
45

46
      test('returns grandchild context after nested run', () async {
47
        final AppContext rootContext = context;
48
        await rootContext.run<void>(name: 'child', body: () async {
49
          final AppContext childContext = context;
50
          await childContext.run<void>(name: 'grandchild', body: () {
51 52 53 54 55 56 57 58 59
            expect(context, isNot(rootContext));
            expect(context, isNot(childContext));
            expect(context.name, 'grandchild');
            called = true;
          });
        });
        expect(called, isTrue);
      });

60
      test('scans up zone hierarchy for first context', () async {
61
        final AppContext rootContext = context;
62
        await rootContext.run<void>(name: 'child', body: () {
63
          final AppContext childContext = context;
64
          runZoned<void>(() {
65 66 67 68 69 70 71 72
            expect(context, isNot(rootContext));
            expect(context, same(childContext));
            expect(context.name, 'child');
            called = true;
          });
        });
        expect(called, isTrue);
      });
73 74
    });

75 76
    group('operator[]', () {
      test('still finds values if async code runs after body has finished', () async {
77 78
        final Completer<void> outer = Completer<void>();
        final Completer<void> inner = Completer<void>();
79
        String value;
80
        await context.run<void>(
81
          body: () {
82
            outer.future.then<void>((_) {
83 84 85 86 87 88 89 90 91 92 93 94 95 96
              value = context[String];
              inner.complete();
            });
          },
          fallbacks: <Type, Generator>{
            String: () => 'value',
          },
        );
        expect(value, isNull);
        outer.complete();
        await inner.future;
        expect(value, 'value');
      });

97
      test('caches generated override values', () async {
98 99
        int consultationCount = 0;
        String value;
100 101
        await context.run<void>(
          body: () async {
102
            final StringBuffer buf = StringBuffer(context[String]);
103
            buf.write(context[String]);
104
            await context.run<void>(body: () {
105 106 107 108 109 110 111 112 113 114 115 116 117 118
              buf.write(context[String]);
            });
            value = buf.toString();
          },
          overrides: <Type, Generator>{
            String: () {
              consultationCount++;
              return 'v';
            },
          },
        );
        expect(value, 'vvv');
        expect(consultationCount, 1);
      });
119

120
      test('caches generated fallback values', () async {
121 122
        int consultationCount = 0;
        String value;
123 124
        await context.run(
          body: () async {
125
            final StringBuffer buf = StringBuffer(context[String]);
126
            buf.write(context[String]);
127
            await context.run<void>(body: () {
128 129 130 131 132 133 134 135 136 137 138 139 140
              buf.write(context[String]);
            });
            value = buf.toString();
          },
          fallbacks: <Type, Generator>{
            String: () {
              consultationCount++;
              return 'v';
            },
          },
        );
        expect(value, 'vvv');
        expect(consultationCount, 1);
141
      });
142

143 144
      test('returns null if generated value is null', () async {
        final String value = await context.run<String>(
145 146 147 148 149 150 151 152 153
          body: () => context[String],
          overrides: <Type, Generator>{
            String: () => null,
          },
        );
        expect(value, isNull);
      });

      test('throws if generator has dependency cycle', () async {
154
        final Future<String> value = context.run<String>(
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
          body: () async {
            return context[String];
          },
          fallbacks: <Type, Generator>{
            int: () => int.parse(context[String]),
            String: () => '${context[double]}',
            double: () => context[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');
        }
      });
172
    });
173

174 175
    group('run', () {
      test('returns the value returned by body', () async {
176 177
        expect(await context.run<int>(body: () => 123), 123);
        expect(await context.run<String>(body: () => 'value'), 'value');
178
        expect(await context.run<int>(body: () async => 456), 456);
179 180
      });

181 182
      test('passes name to child context', () async {
        await context.run<void>(name: 'child', body: () {
183
          expect(context.name, 'child');
184 185 186
        });
      });

187 188 189 190 191 192 193
      group('fallbacks', () {
        bool called;

        setUp(() {
          called = false;
        });

194 195
        test('are applied after parent context is consulted', () async {
          final String value = await context.run<String>(
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
            body: () {
              return context.run<String>(
                body: () {
                  called = true;
                  return context[String];
                },
                fallbacks: <Type, Generator>{
                  String: () => 'child',
                },
              );
            },
          );
          expect(called, isTrue);
          expect(value, 'child');
        });

212
        test('are not applied if parent context supplies value', () async {
213
          bool childConsulted = false;
214
          final String value = await context.run<String>(
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
            body: () {
              return context.run<String>(
                body: () {
                  called = true;
                  return context[String];
                },
                fallbacks: <Type, Generator>{
                  String: () {
                    childConsulted = true;
                    return 'child';
                  },
                },
              );
            },
            fallbacks: <Type, Generator>{
              String: () => 'parent',
            },
          );
          expect(called, isTrue);
          expect(value, 'parent');
          expect(childConsulted, isFalse);
        });

238 239
        test('may depend on one another', () async {
          final String value = await context.run<String>(
240 241 242 243 244 245 246 247 248
            body: () {
              return context[String];
            },
            fallbacks: <Type, Generator>{
              int: () => 123,
              String: () => '-${context[int]}-',
            },
          );
          expect(value, '-123-');
249 250
        });
      });
251

252
      group('overrides', () {
253
        test('intercept consultation of parent context', () async {
254
          bool parentConsulted = false;
255
          final String value = await context.run<String>(
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
            body: () {
              return context.run<String>(
                body: () => context[String],
                overrides: <Type, Generator>{
                  String: () => 'child',
                },
              );
            },
            fallbacks: <Type, Generator>{
              String: () {
                parentConsulted = true;
                return 'parent';
              },
            },
          );
          expect(value, 'child');
          expect(parentConsulted, isFalse);
273 274 275
        });
      });
    });
276 277
  });
}