Improve test coverage for animation.dart (#4718)
We now have 100% coverage of animation.dart and animation_controller.dart. Also, add some basic tools for working with lcov files. These tools need much more polish.
This commit is contained in:
parent
74761265bc
commit
56039c0e64
@ -142,6 +142,23 @@ You can do this online, and it only takes a minute.
|
|||||||
If you've never submitted code before, you must add your (or your
|
If you've never submitted code before, you must add your (or your
|
||||||
organization's) name and contact info to the [AUTHORS](AUTHORS) file.
|
organization's) name and contact info to the [AUTHORS](AUTHORS) file.
|
||||||
|
|
||||||
|
Tools for tracking an improving test coverage
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
We strive for a high degree of test coverage for the Flutter framework. We use
|
||||||
|
Coveralls to [track our test coverage](https://coveralls.io/github/flutter/flutter?branch=master).
|
||||||
|
You can download our current coverage data from cloud storage and visualize it
|
||||||
|
in Atom as follows:
|
||||||
|
|
||||||
|
* `mkdir packages/flutter/coverage`
|
||||||
|
* Download the latest `lcov.info` file produced by Travis using
|
||||||
|
`curl https://storage.googleapis.com/flutter_infra/flutter/coverage/lcov.info -o packages/flutter/coverage/lcov.info`
|
||||||
|
* Install the [lcov-info](https://atom.io/packages/lcov-info) package for Atom.
|
||||||
|
* Open a file in `packages/flutter/lib` in Atom and type `Ctrl+Alt+C`.
|
||||||
|
|
||||||
|
See [issue 4719](https://github.com/flutter/flutter/issues/4719) for ideas about
|
||||||
|
how to improve this workflow.
|
||||||
|
|
||||||
Working on the engine and the framework at the same time
|
Working on the engine and the framework at the same time
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
64
dev/tools/coverage.dart
Normal file
64
dev/tools/coverage.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Downloads and merges line coverage data files for package:flutter.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:args/args.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
const String kBaseLcov = 'packages/flutter/coverage/lcov.base.info';
|
||||||
|
const String kTargetLcov = 'packages/flutter/coverage/lcov.info';
|
||||||
|
const String kSourceLcov = 'packages/flutter/coverage/lcov.source.info';
|
||||||
|
|
||||||
|
Future<int> main(List<String> args) async {
|
||||||
|
if (path.basename(Directory.current.path) == 'tools')
|
||||||
|
Directory.current = Directory.current.parent.parent;
|
||||||
|
|
||||||
|
ProcessResult result = Process.runSync('which', <String>['lcov']);
|
||||||
|
if (result.exitCode != 0) {
|
||||||
|
print('Cannot find lcov. Consider running "apt-get install lcov".\n');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FileSystemEntity.isFileSync(kBaseLcov)) {
|
||||||
|
print(
|
||||||
|
'Cannot find "$kBaseLcov". Consider downloading it from from cloud storage.\n'
|
||||||
|
'https://storage.googleapis.com/flutter_infra/flutter/coverage/lcov.info\n'
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArgParser argParser = new ArgParser();
|
||||||
|
argParser.addFlag('merge', negatable: false);
|
||||||
|
ArgResults results = argParser.parse(args);
|
||||||
|
|
||||||
|
if (FileSystemEntity.isFileSync(kTargetLcov)) {
|
||||||
|
if (results['merge']) {
|
||||||
|
new File(kTargetLcov).renameSync(kSourceLcov);
|
||||||
|
} else {
|
||||||
|
print('"$kTargetLcov" already exists. Did you want to --merge?\n');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results['merge']) {
|
||||||
|
if (!FileSystemEntity.isFileSync(kSourceLcov)) {
|
||||||
|
print('Cannot merge because "$kSourceLcov" does not exist.\n');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessResult result = Process.runSync('lcov', <String>[
|
||||||
|
'--add-tracefile', kBaseLcov,
|
||||||
|
'--add-tracefile', kSourceLcov,
|
||||||
|
'--output-file', kTargetLcov,
|
||||||
|
]);
|
||||||
|
return result.exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
print('No operation requested. Did you want to --merge?\n');
|
||||||
|
return 0;
|
||||||
|
}
|
1
packages/flutter/.gitignore
vendored
1
packages/flutter/.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
.pub/
|
.pub/
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
doc/api/
|
doc/api/
|
||||||
|
coverage/
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/animation.dart';
|
import 'package:flutter/animation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:test/test.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
setUp(() {
|
setUp(() {
|
||||||
@ -171,4 +171,22 @@ void main() {
|
|||||||
expect(controller.lastElapsedDuration, equals(const Duration(milliseconds: 20)));
|
expect(controller.lastElapsedDuration, equals(const Duration(milliseconds: 20)));
|
||||||
controller.stop();
|
controller.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('toString control test', () {
|
||||||
|
AnimationController controller = new AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 100)
|
||||||
|
);
|
||||||
|
expect(controller.toString(), isOneLineDescription);
|
||||||
|
controller.forward();
|
||||||
|
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 20));
|
||||||
|
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 30));
|
||||||
|
expect(controller.toString(), isOneLineDescription);
|
||||||
|
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 120));
|
||||||
|
expect(controller.toString(), isOneLineDescription);
|
||||||
|
controller.reverse();
|
||||||
|
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 20));
|
||||||
|
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 30));
|
||||||
|
expect(controller.toString(), isOneLineDescription);
|
||||||
|
controller.stop();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
28
packages/flutter/test/animation/animations_test.dart
Normal file
28
packages/flutter/test/animation/animations_test.dart
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// 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 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter/animation.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
setUp(() {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
WidgetsBinding.instance.resetEpoch();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toString control test', () {
|
||||||
|
expect(kAlwaysCompleteAnimation.toString(), isOneLineDescription);
|
||||||
|
expect(kAlwaysDismissedAnimation.toString(), isOneLineDescription);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toString control test', () {
|
||||||
|
ProxyAnimation animation = new ProxyAnimation();
|
||||||
|
expect(animation.value, 0.0);
|
||||||
|
expect(animation.status, AnimationStatus.dismissed);
|
||||||
|
expect(animation.toString(), isOneLineDescription);
|
||||||
|
animation.parent = kAlwaysDismissedAnimation;
|
||||||
|
expect(animation.toString(), isOneLineDescription);
|
||||||
|
});
|
||||||
|
}
|
@ -51,6 +51,12 @@ const Matcher isInCard = const _IsInCard();
|
|||||||
/// [Card] widget ancestors.
|
/// [Card] widget ancestors.
|
||||||
const Matcher isNotInCard = const _IsNotInCard();
|
const Matcher isNotInCard = const _IsNotInCard();
|
||||||
|
|
||||||
|
/// Asserts that a string is a plausible one-line description of an object.
|
||||||
|
///
|
||||||
|
/// Specifically, this matcher checks that the string does not contains newline
|
||||||
|
/// characters and does not have leading or trailing whitespace.
|
||||||
|
const Matcher isOneLineDescription = const _IsOneLineDescription();
|
||||||
|
|
||||||
class _FindsWidgetMatcher extends Matcher {
|
class _FindsWidgetMatcher extends Matcher {
|
||||||
const _FindsWidgetMatcher(this.min, this.max);
|
const _FindsWidgetMatcher(this.min, this.max);
|
||||||
|
|
||||||
@ -182,3 +188,17 @@ class _IsNotInCard extends Matcher {
|
|||||||
@override
|
@override
|
||||||
Description describe(Description description) => description.add('not in card');
|
Description describe(Description description) => description.add('not in card');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _IsOneLineDescription extends Matcher {
|
||||||
|
const _IsOneLineDescription();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool matches(String description, Map<dynamic, dynamic> matchState) {
|
||||||
|
return description.isNotEmpty &&
|
||||||
|
!description.contains('\n') &&
|
||||||
|
description.trim() == description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Description describe(Description description) => description.add('one line description');
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user