context_test.dart 8.32 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 'package:test/test.dart';

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

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

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

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

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

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

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

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

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

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

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

      test('throws if generator has dependency cycle', () async {
153
        final Future<String> value = context.run<String>(
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
          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');
        }
      });
171
    });
172

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

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

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

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

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

211
        test('are not applied if parent context supplies value', () async {
212
          bool childConsulted = false;
213
          final String value = await context.run<String>(
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
            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);
        });

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

251
      group('overrides', () {
252
        test('intercept consultation of parent context', () async {
253
          bool parentConsulted = false;
254
          final String value = await context.run<String>(
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
            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);
272 273 274
        });
      });
    });
275 276
  });
}