- Add VariableSpec class with magnitude field validation - Add Formula class supporting multiple input/output variables - Support d4rt_code as string or object with code field - Add comprehensive tests for parsing and serialization - Fix broken test import in pruebas_d4rt_test.dart Follows README.md format requirements exactly
398 lines
12 KiB
Dart
398 lines
12 KiB
Dart
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
// ignore_for_file: only_throw_errors
|
|
|
|
import 'dart:async';
|
|
import 'dart:math';
|
|
|
|
import 'package:coverage/src/util.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
const _failCount = 5;
|
|
const _delay = Duration(milliseconds: 10);
|
|
|
|
void main() {
|
|
test('retry', () async {
|
|
var count = 0;
|
|
final stopwatch = Stopwatch()..start();
|
|
|
|
Future failCountTimes() async {
|
|
expect(stopwatch.elapsed, greaterThanOrEqualTo(_delay * count));
|
|
|
|
while (count < _failCount) {
|
|
count++;
|
|
throw 'not yet!';
|
|
}
|
|
return 42;
|
|
}
|
|
|
|
final value = await retry(failCountTimes, _delay) as int;
|
|
|
|
expect(value, 42);
|
|
expect(count, _failCount);
|
|
expect(stopwatch.elapsed, greaterThanOrEqualTo(_delay * count));
|
|
});
|
|
|
|
group('retry with timeout', () {
|
|
test('if it finishes', () async {
|
|
var count = 0;
|
|
final stopwatch = Stopwatch()..start();
|
|
|
|
Future failCountTimes() async {
|
|
expect(stopwatch.elapsed, greaterThanOrEqualTo(_delay * count));
|
|
|
|
while (count < _failCount) {
|
|
count++;
|
|
throw 'not yet!';
|
|
}
|
|
return 42;
|
|
}
|
|
|
|
final safeTimoutDuration = _delay * _failCount * 10;
|
|
final value = await retry(
|
|
failCountTimes,
|
|
_delay,
|
|
timeout: safeTimoutDuration,
|
|
) as int;
|
|
|
|
expect(value, 42);
|
|
expect(count, _failCount);
|
|
expect(stopwatch.elapsed, greaterThanOrEqualTo(_delay * count));
|
|
});
|
|
|
|
test('if it does not finish', () async {
|
|
var count = 0;
|
|
final stopwatch = Stopwatch()..start();
|
|
|
|
var caught = false;
|
|
var countAfterError = 0;
|
|
|
|
Future failCountTimes() async {
|
|
if (caught) {
|
|
countAfterError++;
|
|
}
|
|
expect(stopwatch.elapsed, greaterThanOrEqualTo(_delay * count));
|
|
|
|
count++;
|
|
throw 'never';
|
|
}
|
|
|
|
final unsafeTimeoutDuration = _delay * (_failCount / 2);
|
|
|
|
try {
|
|
await retry(failCountTimes, _delay, timeout: unsafeTimeoutDuration);
|
|
// ignore: avoid_catching_errors
|
|
} on StateError catch (e) {
|
|
expect(e.message, 'Failed to complete within 25ms');
|
|
caught = true;
|
|
|
|
expect(countAfterError, 0,
|
|
reason: 'Execution should stop after a timeout');
|
|
|
|
await Future<dynamic>.delayed(_delay * 3);
|
|
|
|
expect(countAfterError, 0, reason: 'Even after a delay');
|
|
}
|
|
|
|
expect(caught, isTrue);
|
|
});
|
|
});
|
|
|
|
group('extractVMServiceUri', () {
|
|
test('returns null when not found', () {
|
|
expect(extractVMServiceUri('foo bar baz'), isNull);
|
|
});
|
|
|
|
test('returns null for an incorrectly formatted URI', () {
|
|
const msg = 'Observatory listening on :://';
|
|
expect(extractVMServiceUri(msg), null);
|
|
});
|
|
|
|
test('returns URI at end of string', () {
|
|
const msg = 'Observatory listening on http://foo.bar:9999/';
|
|
expect(extractVMServiceUri(msg), Uri.parse('http://foo.bar:9999/'));
|
|
});
|
|
|
|
test('returns URI with auth token at end of string', () {
|
|
const msg = 'Observatory listening on http://foo.bar:9999/cG90YXRv/';
|
|
expect(
|
|
extractVMServiceUri(msg), Uri.parse('http://foo.bar:9999/cG90YXRv/'));
|
|
});
|
|
|
|
test('return URI embedded within string', () {
|
|
const msg = '1985-10-26 Observatory listening on http://foo.bar:9999/ **';
|
|
expect(extractVMServiceUri(msg), Uri.parse('http://foo.bar:9999/'));
|
|
});
|
|
|
|
test('return URI with auth token embedded within string', () {
|
|
const msg =
|
|
'1985-10-26 Observatory listening on http://foo.bar:9999/cG90YXRv/ **';
|
|
expect(
|
|
extractVMServiceUri(msg), Uri.parse('http://foo.bar:9999/cG90YXRv/'));
|
|
});
|
|
|
|
test('handles new Dart VM service message format', () {
|
|
const msg =
|
|
'The Dart VM service is listening on http://foo.bar:9999/cG90YXRv/';
|
|
expect(
|
|
extractVMServiceUri(msg), Uri.parse('http://foo.bar:9999/cG90YXRv/'));
|
|
});
|
|
});
|
|
|
|
group('getIgnoredLines', () {
|
|
const invalidSources = [
|
|
'''final str = ''; // coverage:ignore-start
|
|
final str = '';
|
|
final str = ''; // coverage:ignore-start
|
|
''',
|
|
'''final str = ''; // coverage:ignore-start
|
|
final str = '';
|
|
final str = ''; // coverage:ignore-start
|
|
final str = ''; // coverage:ignore-end
|
|
final str = '';
|
|
final str = ''; // coverage:ignore-end
|
|
''',
|
|
'''final str = ''; // coverage:ignore-start
|
|
final str = '';
|
|
final str = ''; // coverage:ignore-end
|
|
final str = '';
|
|
final str = ''; // coverage:ignore-end
|
|
''',
|
|
'''final str = ''; // coverage:ignore-end
|
|
final str = '';
|
|
final str = ''; // coverage:ignore-start
|
|
final str = '';
|
|
final str = ''; // coverage:ignore-end
|
|
''',
|
|
'''final str = ''; // coverage:ignore-end
|
|
final str = '';
|
|
final str = ''; // coverage:ignore-end
|
|
''',
|
|
'''final str = ''; // coverage:ignore-end
|
|
final str = '';
|
|
final str = ''; // coverage:ignore-start
|
|
''',
|
|
'''final str = ''; // coverage:ignore-end
|
|
''',
|
|
'''final str = ''; // coverage:ignore-start
|
|
''',
|
|
];
|
|
|
|
test('throws FormatException when the annotations are not balanced', () {
|
|
void runTest(int index, String errMsg) {
|
|
final content = invalidSources[index].split('\n');
|
|
expect(
|
|
() => getIgnoredLines('content-$index.dart', content),
|
|
throwsA(
|
|
allOf(
|
|
isFormatException,
|
|
predicate((FormatException e) => e.message == errMsg),
|
|
),
|
|
),
|
|
reason: 'expected FormatException with message "$errMsg"',
|
|
);
|
|
}
|
|
|
|
runTest(
|
|
0,
|
|
'coverage:ignore-start found at content-0.dart:'
|
|
'3 before previous coverage:ignore-start ended',
|
|
);
|
|
runTest(
|
|
1,
|
|
'coverage:ignore-start found at content-1.dart:'
|
|
'3 before previous coverage:ignore-start ended',
|
|
);
|
|
runTest(
|
|
2,
|
|
'unmatched coverage:ignore-end found at content-2.dart:5',
|
|
);
|
|
runTest(
|
|
3,
|
|
'unmatched coverage:ignore-end found at content-3.dart:1',
|
|
);
|
|
runTest(
|
|
4,
|
|
'unmatched coverage:ignore-end found at content-4.dart:1',
|
|
);
|
|
runTest(
|
|
5,
|
|
'unmatched coverage:ignore-end found at content-5.dart:1',
|
|
);
|
|
runTest(
|
|
6,
|
|
'unmatched coverage:ignore-end found at content-6.dart:1',
|
|
);
|
|
runTest(
|
|
7,
|
|
'coverage:ignore-start found at content-7.dart:'
|
|
'1 has no matching coverage:ignore-end',
|
|
);
|
|
});
|
|
|
|
test(
|
|
'returns null when the annotations are not '
|
|
'balanced but the whole file is ignored', () {
|
|
for (final content in invalidSources) {
|
|
final lines = content.split('\n');
|
|
lines.add(' // coverage:ignore-file');
|
|
expect(getIgnoredLines('', lines), null);
|
|
}
|
|
});
|
|
|
|
test('returns null when the whole file is ignored', () {
|
|
final lines = '''final str = ''; // coverage:ignore-start
|
|
final str = ''; // coverage:ignore-end
|
|
final str = ''; // coverage:ignore-file
|
|
'''
|
|
.split('\n');
|
|
|
|
expect(getIgnoredLines('', lines), null);
|
|
});
|
|
|
|
test('return the correct range of lines ignored', () {
|
|
final lines = '''
|
|
final str = ''; // coverage:ignore-start
|
|
final str = ''; // coverage:ignore-line
|
|
final str = ''; // coverage:ignore-end
|
|
final str = ''; // coverage:ignore-start
|
|
final str = ''; // coverage:ignore-line
|
|
final str = ''; // coverage:ignore-end
|
|
'''
|
|
.split('\n');
|
|
|
|
expect(getIgnoredLines('', lines), [
|
|
[1, 3],
|
|
[4, 6],
|
|
]);
|
|
});
|
|
|
|
test('return the correct list of lines ignored', () {
|
|
final lines = '''
|
|
final str = ''; // coverage:ignore-line
|
|
final str = ''; // coverage:ignore-line
|
|
final str = ''; // coverage:ignore-line
|
|
'''
|
|
.split('\n');
|
|
|
|
expect(getIgnoredLines('', lines), [
|
|
[1, 1],
|
|
[2, 2],
|
|
[3, 3],
|
|
]);
|
|
});
|
|
|
|
test('ignore comments have no effect inside string literals', () {
|
|
final lines = '''
|
|
final str = '// coverage:ignore-file';
|
|
final str = '// coverage:ignore-line';
|
|
final str = ''; // coverage:ignore-line
|
|
final str = '// coverage:ignore-start';
|
|
final str = '// coverage:ignore-end';
|
|
'''
|
|
.split('\n');
|
|
|
|
expect(getIgnoredLines('', lines), [
|
|
[3, 3],
|
|
]);
|
|
});
|
|
|
|
test('allow white-space after ignore comments', () {
|
|
// Using multiple strings, rather than splitting a multi-line string,
|
|
// because many code editors remove trailing white-space.
|
|
final lines = [
|
|
"final str = ''; // coverage:ignore-start ",
|
|
"final str = ''; // coverage:ignore-line\t",
|
|
"final str = ''; // coverage:ignore-end \t \t ",
|
|
"final str = ''; // coverage:ignore-line \t ",
|
|
"final str = ''; // coverage:ignore-start \t ",
|
|
"final str = ''; // coverage:ignore-end \t \t ",
|
|
];
|
|
|
|
expect(getIgnoredLines('', lines), [
|
|
[1, 3],
|
|
[4, 4],
|
|
[5, 6],
|
|
]);
|
|
});
|
|
|
|
test('allow omitting space after //', () {
|
|
final lines = [
|
|
"final str = ''; //coverage:ignore-start",
|
|
"final str = ''; //coverage:ignore-line",
|
|
"final str = ''; //coverage:ignore-end",
|
|
"final str = ''; //coverage:ignore-line",
|
|
"final str = ''; //coverage:ignore-start",
|
|
"final str = ''; //coverage:ignore-end",
|
|
];
|
|
|
|
expect(getIgnoredLines('', lines), [
|
|
[1, 3],
|
|
[4, 4],
|
|
[5, 6],
|
|
]);
|
|
});
|
|
|
|
test('allow text after ignore comments', () {
|
|
final lines = [
|
|
"final str = ''; // coverage:ignore-start due to XYZ",
|
|
"final str = ''; // coverage:ignore-line",
|
|
"final str = ''; // coverage:ignore-end due to XYZ",
|
|
"final str = ''; // coverage:ignore-line due to 123",
|
|
"final str = ''; // coverage:ignore-start",
|
|
"final str = ''; // coverage:ignore-end it is done",
|
|
];
|
|
|
|
expect(getIgnoredLines('', lines), [
|
|
[1, 3],
|
|
[4, 4],
|
|
[5, 6],
|
|
]);
|
|
});
|
|
});
|
|
|
|
test('getAllWorkspaceNames', () {
|
|
// Uses the workspace_names directory:
|
|
// workspace_names
|
|
// └── pkgs
|
|
// ├── foo
|
|
// │ └── foo_example // Not part of foo's workspace.
|
|
// └── bar
|
|
// └── bar_example // Part of bar's workspace.
|
|
expect(
|
|
getAllWorkspaceNames('test/workspace_names'),
|
|
unorderedEquals([
|
|
'workspace_names',
|
|
'foo',
|
|
'bar',
|
|
'bar_example',
|
|
]));
|
|
});
|
|
|
|
test('IgnoredLinesContains', () {
|
|
(List<List<int>>, int) createRandomRanges(int len) {
|
|
final ranges = <List<int>>[];
|
|
var line = 0;
|
|
final rand = Random();
|
|
while (ranges.length < len) {
|
|
final start = line += 1 + rand.nextInt(5);
|
|
final end = line += rand.nextInt(5);
|
|
ranges.add([start, end]);
|
|
}
|
|
return (ranges, line);
|
|
}
|
|
|
|
bool naiveIgnoredContains(List<List<int>> ranges, int line) =>
|
|
ranges.any((range) => range[0] <= line && range[1] >= line);
|
|
|
|
for (final len in [0, 1, 2, 3, 10, 100, 1000]) {
|
|
final (ranges, end) = createRandomRanges(len);
|
|
for (var line = 0; line < end + 3; ++line) {
|
|
expect(
|
|
ranges.ignoredContains(line), naiveIgnoredContains(ranges, line));
|
|
}
|
|
}
|
|
});
|
|
}
|