flutter_command_test.dart 5.33 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2017 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/cache.dart';
8
import 'package:flutter_tools/src/usage.dart';
9
import 'package:flutter_tools/src/base/common.dart';
10 11
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:mockito/mockito.dart';
12
import 'package:quiver/time.dart';
13 14
import 'package:test/test.dart';

15
import '../src/context.dart';
16 17 18 19 20 21

void main() {

  group('Flutter Command', () {

    MockCache cache;
22 23 24
    MockClock clock;
    MockUsage usage;
    List<int> mockTimes;
25 26 27

    setUp(() {
      cache = new MockCache();
28 29 30 31 32 33
      clock = new MockClock();
      usage = new MockUsage();
      when(usage.isFirstRun).thenReturn(false);
      when(clock.now()).thenAnswer(
        (Invocation _) => new DateTime.fromMillisecondsSinceEpoch(mockTimes.removeAt(0))
      );
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
    });

    testUsingContext('honors shouldUpdateCache false', () async {
      final DummyFlutterCommand flutterCommand = new DummyFlutterCommand(shouldUpdateCache: false);
      await flutterCommand.run();
      verifyZeroInteractions(cache);
    },
    overrides: <Type, Generator>{
      Cache: () => cache,
    });

    testUsingContext('honors shouldUpdateCache true', () async {
      final DummyFlutterCommand flutterCommand = new DummyFlutterCommand(shouldUpdateCache: true);
      await flutterCommand.run();
      verify(cache.updateAll()).called(1);
    },
    overrides: <Type, Generator>{
      Cache: () => cache,
    });
53 54 55 56 57 58 59 60 61 62

    testUsingContext('report execution timing by default', () async {
      // Crash if called a third time which is unexpected.
      mockTimes = <int>[1000, 2000];

      final DummyFlutterCommand flutterCommand = new DummyFlutterCommand();
      await flutterCommand.run();
      verify(clock.now()).called(2);

      expect(
63
        verify(usage.sendTiming(captureAny, captureAny, captureAny, label: captureAny)).captured,
64 65 66 67 68 69 70 71 72 73 74 75
        <dynamic>['flutter', 'dummy', const Duration(milliseconds: 1000), null]
      );
    },
    overrides: <Type, Generator>{
      Clock: () => clock,
      Usage: () => usage,
    });

    testUsingContext('no timing report without usagePath', () async {
      // Crash if called a third time which is unexpected.
      mockTimes = <int>[1000, 2000];

76
      final DummyFlutterCommand flutterCommand =
77 78 79 80 81 82 83 84 85
          new DummyFlutterCommand(noUsagePath: true);
      await flutterCommand.run();
      verify(clock.now()).called(2);
      verifyNever(usage.sendTiming(captureAny, captureAny, captureAny, label: captureAny));
    },
    overrides: <Type, Generator>{
      Clock: () => clock,
      Usage: () => usage,
    });
86

87 88 89 90 91
    testUsingContext('report additional FlutterCommandResult data', () async {
      // Crash if called a third time which is unexpected.
      mockTimes = <int>[1000, 2000];

      final FlutterCommandResult commandResult = new FlutterCommandResult(
92
        ExitStatus.success,
93
        // nulls should be cleaned up.
94
        timingLabelParts: <String> ['blah1', 'blah2', null, 'blah3'],
95 96 97
        endTimeOverride: new DateTime.fromMillisecondsSinceEpoch(1500)
      );

98 99 100
      final DummyFlutterCommand flutterCommand = new DummyFlutterCommand(
        commandFunction: () async => commandResult
      );
101 102 103
      await flutterCommand.run();
      verify(clock.now()).called(2);
      expect(
104
        verify(usage.sendTiming(captureAny, captureAny, captureAny, label: captureAny)).captured,
105
        <dynamic>[
106 107
          'flutter',
          'dummy',
108
          const Duration(milliseconds: 500), // FlutterCommandResult's end time used instead.
109
          'success-blah1-blah2-blah3',
110
        ],
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
      );
    },
    overrides: <Type, Generator>{
      Clock: () => clock,
      Usage: () => usage,
    });

    testUsingContext('report failed execution timing too', () async {
      // Crash if called a third time which is unexpected.
      mockTimes = <int>[1000, 2000];

      final DummyFlutterCommand flutterCommand =
          new DummyFlutterCommand(commandFunction: () async { throwToolExit('fail'); });

      try {
        await flutterCommand.run();
        fail('Mock should make this fail');
      } on ToolExit {
        // Should have still checked time twice.
        verify(clock.now()).called(2);

        expect(
          verify(usage.sendTiming(captureAny, captureAny, captureAny, label: captureAny)).captured,
          <dynamic>['flutter', 'dummy', const Duration(milliseconds: 1000), 'fail']
        );
      }
137 138 139 140 141 142
    },
    overrides: <Type, Generator>{
      Clock: () => clock,
      Usage: () => usage,
    });

143
  });
144

145 146
}

147 148
typedef Future<FlutterCommandResult> CommandFunction();

149 150
class DummyFlutterCommand extends FlutterCommand {

151
  DummyFlutterCommand({
152 153 154
    this.shouldUpdateCache : false,
    this.noUsagePath : false,
    this.commandFunction,
155 156 157
  });

  final bool noUsagePath;
158
  final CommandFunction commandFunction;
159 160 161 162 163 164 165

  @override
  final bool shouldUpdateCache;

  @override
  String get description => 'does nothing';

166 167 168
  @override
  Future<String> get usagePath => noUsagePath ? null : super.usagePath;

169 170 171 172
  @override
  String get name => 'dummy';

  @override
173
  Future<FlutterCommandResult> runCommand() async {
174
    return commandFunction == null ? null : commandFunction();
175 176 177
  }
}

178 179
class MockCache extends Mock implements Cache {}

180
class MockUsage extends Mock implements Usage {}