build_test_task.dart 3.89 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2014 The Flutter 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:io';

import 'package:args/args.dart';

9
import '../framework/devices.dart';
10 11 12 13 14 15 16 17 18
import '../framework/task_result.dart';
import '../framework/utils.dart';

/// [Task] for defining build-test separation.
///
/// Using this [Task] allows DeviceLab capacity to only be spent on the [test].
abstract class BuildTestTask {
  BuildTestTask(this.args, {this.workingDirectory, this.runFlutterClean = true,}) {
    final ArgResults argResults = argParser.parse(args);
19
    applicationBinaryPath = argResults[kApplicationBinaryPathOption] as String?;
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
    buildOnly = argResults[kBuildOnlyFlag] as bool;
    testOnly = argResults[kTestOnlyFlag] as bool;
  }

  static const String kApplicationBinaryPathOption = 'application-binary-path';
  static const String kBuildOnlyFlag = 'build';
  static const String kTestOnlyFlag = 'test';

  final ArgParser argParser = ArgParser()
    ..addOption(kApplicationBinaryPathOption)
    ..addFlag(kBuildOnlyFlag)
    ..addFlag(kTestOnlyFlag);

  /// Args passed from the test runner via "--task-arg".
  final List<String> args;

  /// If true, skip [test].
  bool buildOnly = false;

  /// If true, skip [build].
  bool testOnly = false;

  /// Whether to run `flutter clean` before building the application under test.
  final bool runFlutterClean;

  /// Path to a built application to use in [test].
  ///
  /// If not given, will default to child's expected location.
48
  String? applicationBinaryPath;
49 50

  /// Where the test artifacts are stored, such as performance results.
51
  final Directory? workingDirectory;
52 53 54 55 56 57 58 59 60

  /// Run Flutter build to create [applicationBinaryPath].
  Future<void> build() async {
    await inDirectory<void>(workingDirectory, () async {
      if (runFlutterClean) {
        section('FLUTTER CLEAN');
        await flutter('clean');
      }
      section('BUILDING APPLICATION');
61
      await flutter('build', options: getBuildArgs(deviceOperatingSystem));
62 63 64 65 66 67 68 69 70 71 72 73
    });

  }

  /// Run Flutter drive test from [getTestArgs] against the application under test on the device.
  ///
  /// This assumes that [applicationBinaryPath] exists.
  Future<TaskResult> test() async {
    final Device device = await devices.workingDevice;
    await device.unlock();
    await inDirectory<void>(workingDirectory, () async {
      section('DRIVE START');
74
      await flutter('drive', options: getTestArgs(deviceOperatingSystem, device.deviceId));
75 76 77 78 79 80
    });

    return parseTaskResult();
  }

  /// Args passed to flutter build to build the application under test.
81
  List<String> getBuildArgs(DeviceOperatingSystem deviceOperatingSystem) => throw UnimplementedError('getBuildArgs is not implemented');
82 83

  /// Args passed to flutter drive to test the built application.
84
  List<String> getTestArgs(DeviceOperatingSystem deviceOperatingSystem, String deviceId) => throw UnimplementedError('getTestArgs is not implemented');
85 86 87 88 89 90 91 92

  /// Logic to construct [TaskResult] from this test's results.
  Future<TaskResult> parseTaskResult() => throw UnimplementedError('parseTaskResult is not implemented');

  /// Path to the built application under test.
  ///
  /// Tasks can override to support default values. Otherwise, it will default
  /// to needing to be passed as an argument in the test runner.
93
  String? getApplicationBinaryPath() => applicationBinaryPath;
94 95 96 97 98 99 100 101 102 103 104 105 106 107

  /// Run this task.
  ///
  /// Throws [Exception] when unnecessary arguments are passed.
  Future<TaskResult> call() async {
    if (buildOnly && testOnly) {
      throw Exception('Both build and test should not be passed. Pass only one.');
    }

    if (buildOnly && applicationBinaryPath != null) {
      throw Exception('Application binary path is only used for tests');
    }

    if (!testOnly) {
108
      await build();
109 110 111 112 113 114 115 116 117
    }

    if (buildOnly) {
      return TaskResult.buildOnly();
    }

    return test();
  }
}