Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
97901da1
Unverified
Commit
97901da1
authored
2 years ago
by
Ian Hickson
Committed by
GitHub
2 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Cleaner test.dart output. (#109206)
parent
d50c5b1b
stable
No related merge requests found
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
573 additions
and
742 deletions
+573
-742
analyze.dart
dev/bots/analyze.dart
+54
-64
flutter_compact_formatter.dart
dev/bots/flutter_compact_formatter.dart
+0
-270
run_command.dart
dev/bots/run_command.dart
+50
-59
service_worker_test.dart
dev/bots/service_worker_test.dart
+12
-12
test.dart
dev/bots/test.dart
+217
-216
analyze_test.dart
dev/bots/test/analyze_test.dart
+52
-80
test_test.dart
dev/bots/test/test_test.dart
+22
-9
utils.dart
dev/bots/utils.dart
+166
-32
No files found.
dev/bots/analyze.dart
View file @
97901da1
This diff is collapsed.
Click to expand it.
dev/bots/flutter_compact_formatter.dart
deleted
100644 → 0
View file @
d50c5b1b
// 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:convert'
;
import
'dart:io'
;
final
Stopwatch
_stopwatch
=
Stopwatch
();
/// A wrapper around package:test's JSON reporter.
///
/// This class behaves similarly to the compact reporter, but suppresses all
/// output except for progress until the end of testing. In other words, errors,
/// [print] calls, and skipped test messages will not be printed during the run
/// of the suite.
///
/// It also processes the JSON data into a collection of [TestResult]s for any
/// other post processing needs, e.g. sending data to analytics.
class
FlutterCompactFormatter
{
FlutterCompactFormatter
()
{
_stopwatch
.
start
();
}
/// Whether to use color escape codes in writing to stdout.
final
bool
useColor
=
stdout
.
supportsAnsiEscapes
;
/// The terminal escape for green text, or the empty string if this is Windows
/// or not outputting to a terminal.
String
get
_green
=>
useColor
?
'
\
u001b[32m'
:
''
;
/// The terminal escape for red text, or the empty string if this is Windows
/// or not outputting to a terminal.
String
get
_red
=>
useColor
?
'
\
u001b[31m'
:
''
;
/// The terminal escape for yellow text, or the empty string if this is
/// Windows or not outputting to a terminal.
String
get
_yellow
=>
useColor
?
'
\
u001b[33m'
:
''
;
/// The terminal escape for gray text, or the empty string if this is
/// Windows or not outputting to a terminal.
String
get
_gray
=>
useColor
?
'
\
u001b[1;30m'
:
''
;
/// The terminal escape for bold text, or the empty string if this is
/// Windows or not outputting to a terminal.
String
get
_bold
=>
useColor
?
'
\
u001b[1m'
:
''
;
/// The terminal escape for removing test coloring, or the empty string if
/// this is Windows or not outputting to a terminal.
String
get
_noColor
=>
useColor
?
'
\
u001b[0m'
:
''
;
/// The terminal escape for clearing the line, or a carriage return if
/// this is Windows or not outputting to a terminal.
String
get
_clearLine
=>
useColor
?
'
\
x1b[2K
\r
'
:
'
\r
'
;
final
Map
<
int
,
TestResult
>
_tests
=
<
int
,
TestResult
>{};
/// The test results from this run.
Iterable
<
TestResult
>
get
tests
=>
_tests
.
values
;
/// The number of tests that were started.
int
started
=
0
;
/// The number of test failures.
int
failures
=
0
;
/// The number of skipped tests.
int
skips
=
0
;
/// The number of successful tests.
int
successes
=
0
;
/// Process a single line of JSON output from the JSON test reporter.
///
/// Callers are responsible for splitting multiple lines before calling this
/// method.
TestResult
?
processRawOutput
(
String
raw
)
{
assert
(
raw
!=
null
);
// We might be getting messages from Flutter Tool about updating/building.
if
(!
raw
.
startsWith
(
'{'
))
{
print
(
raw
);
return
null
;
}
final
Map
<
String
,
dynamic
>
decoded
=
json
.
decode
(
raw
)
as
Map
<
String
,
dynamic
>;
final
TestResult
?
originalResult
=
_tests
[
decoded
[
'testID'
]];
switch
(
decoded
[
'type'
]
as
String
)
{
case
'done'
:
stdout
.
write
(
_clearLine
);
stdout
.
write
(
'
$_bold${_stopwatch.elapsed}$_noColor
'
);
stdout
.
writeln
(
'
$_green
+
$successes
$_yellow
~
$skips
$_red
-
$failures
:
$_bold$_gray
Done.
$_noColor
'
);
break
;
case
'testStart'
:
final
Map
<
String
,
dynamic
>
testData
=
decoded
[
'test'
]
as
Map
<
String
,
dynamic
>;
if
(
testData
[
'url'
]
==
null
)
{
started
+=
1
;
stdout
.
write
(
_clearLine
);
stdout
.
write
(
'
$_bold${_stopwatch.elapsed}$_noColor
'
);
stdout
.
write
(
'
$_green
+
$successes
$_yellow
~
$skips
$_red
-
$failures
:
$_gray${testData['name']}$_noColor
'
);
break
;
}
_tests
[
testData
[
'id'
]
as
int
]
=
TestResult
(
id:
testData
[
'id'
]
as
int
,
name:
testData
[
'name'
]
as
String
,
line:
testData
[
'root_line'
]
as
int
?
??
testData
[
'line'
]
as
int
,
column:
testData
[
'root_column'
]
as
int
?
??
testData
[
'column'
]
as
int
,
path:
testData
[
'root_url'
]
as
String
?
??
testData
[
'url'
]
as
String
,
startTime:
decoded
[
'time'
]
as
int
,
);
break
;
case
'testDone'
:
if
(
originalResult
==
null
)
{
break
;
}
originalResult
.
endTime
=
decoded
[
'time'
]
as
int
;
if
(
decoded
[
'skipped'
]
==
true
)
{
skips
+=
1
;
originalResult
.
status
=
TestStatus
.
skipped
;
}
else
{
if
(
decoded
[
'result'
]
==
'success'
)
{
originalResult
.
status
=
TestStatus
.
succeeded
;
successes
+=
1
;
}
else
{
originalResult
.
status
=
TestStatus
.
failed
;
failures
+=
1
;
}
}
break
;
case
'error'
:
final
String
error
=
decoded
[
'error'
]
as
String
;
final
String
stackTrace
=
decoded
[
'stackTrace'
]
as
String
;
if
(
originalResult
!=
null
)
{
originalResult
.
errorMessage
=
error
;
originalResult
.
stackTrace
=
stackTrace
;
}
else
{
if
(
error
!=
null
)
{
stderr
.
writeln
(
error
);
}
if
(
stackTrace
!=
null
)
{
stderr
.
writeln
(
stackTrace
);
}
}
break
;
case
'print'
:
if
(
originalResult
!=
null
)
{
originalResult
.
messages
.
add
(
decoded
[
'message'
]
as
String
);
}
break
;
case
'group'
:
case
'allSuites'
:
case
'start'
:
case
'suite'
:
default
:
break
;
}
return
originalResult
;
}
/// Print summary of test results.
void
finish
()
{
final
List
<
String
>
skipped
=
<
String
>[];
final
List
<
String
>
failed
=
<
String
>[];
for
(
final
TestResult
result
in
_tests
.
values
)
{
switch
(
result
.
status
)
{
case
TestStatus
.
started
:
failed
.
add
(
'
${_red}
Unexpectedly failed to complete a test!'
);
failed
.
add
(
result
.
toString
()
+
_noColor
);
break
;
case
TestStatus
.
skipped
:
skipped
.
add
(
'
${_yellow}
Skipped
${result.name}
(
${result.pathLineColumn}
).
$_noColor
'
);
break
;
case
TestStatus
.
failed
:
failed
.
addAll
(<
String
>[
'
$_bold${_red}
Failed
${result.name}
(
${result.pathLineColumn}
):'
,
result
.
errorMessage
!,
_noColor
+
_red
,
result
.
stackTrace
!,
]);
failed
.
addAll
(
result
.
messages
);
failed
.
add
(
_noColor
);
break
;
case
TestStatus
.
succeeded
:
break
;
}
}
skipped
.
forEach
(
print
);
failed
.
forEach
(
print
);
if
(
failed
.
isEmpty
)
{
print
(
'
${_green}
Completed,
$successes
test(s) passing (
$skips
skipped).
$_noColor
'
);
}
else
{
print
(
'
$_gray$failures
test(s) failed.
$_noColor
'
);
}
}
}
/// The state of a test received from the JSON reporter.
enum
TestStatus
{
/// Test execution has started.
started
,
/// Test completed successfully.
succeeded
,
/// Test failed.
failed
,
/// Test was skipped.
skipped
,
}
/// The detailed status of a test run.
class
TestResult
{
TestResult
({
required
this
.
id
,
required
this
.
name
,
required
this
.
line
,
required
this
.
column
,
required
this
.
path
,
required
this
.
startTime
,
this
.
status
=
TestStatus
.
started
,
})
:
assert
(
id
!=
null
),
assert
(
name
!=
null
),
assert
(
line
!=
null
),
assert
(
column
!=
null
),
assert
(
path
!=
null
),
assert
(
startTime
!=
null
),
assert
(
status
!=
null
),
messages
=
<
String
>[];
/// The state of the test.
TestStatus
status
;
/// The internal ID of the test used by the JSON reporter.
final
int
id
;
/// The name of the test, specified via the `test` method.
final
String
name
;
/// The line number from the original file.
final
int
line
;
/// The column from the original file.
final
int
column
;
/// The path of the original test file.
final
String
path
;
/// A friendly print out of the [path], [line], and [column] of the test.
String
get
pathLineColumn
=>
'
$path
:
$line
:
$column
'
;
/// The start time of the test, in milliseconds relative to suite startup.
final
int
startTime
;
/// The stdout of the test.
final
List
<
String
>
messages
;
/// The error message from the test, from an `expect`, an [Exception] or
/// [Error].
String
?
errorMessage
;
/// The stacktrace from a test failure.
String
?
stackTrace
;
/// The time, in milliseconds relative to suite startup, that the test ended.
int
?
endTime
;
/// The total time, in milliseconds, that the test took.
int
get
totalTime
=>
(
endTime
??
_stopwatch
.
elapsedMilliseconds
)
-
startTime
;
@override
String
toString
()
=>
'{
$runtimeType
: {
$id
,
$name
,
${totalTime}
ms,
$pathLineColumn
}}'
;
}
This diff is collapsed.
Click to expand it.
dev/bots/run_command.dart
View file @
97901da1
...
...
@@ -50,23 +50,9 @@ class Command {
/// The raw process that was launched for this command.
final
io
.
Process
process
;
final
Stopwatch
_time
;
final
Future
<
List
<
List
<
int
>>>?
_savedStdout
;
final
Future
<
List
<
List
<
int
>>>?
_savedStderr
;
/// Evaluates when the [process] exits.
///
/// Returns the result of running the command.
Future
<
CommandResult
>
get
onExit
async
{
final
int
exitCode
=
await
process
.
exitCode
;
_time
.
stop
();
// Saved output is null when OutputMode.print is used.
final
String
?
flattenedStdout
=
_savedStdout
!=
null
?
_flattenToString
((
await
_savedStdout
)!)
:
null
;
final
String
?
flattenedStderr
=
_savedStderr
!=
null
?
_flattenToString
((
await
_savedStderr
)!)
:
null
;
return
CommandResult
.
_
(
exitCode
,
_time
.
elapsed
,
flattenedStdout
,
flattenedStderr
);
}
final
Future
<
String
>
_savedStdout
;
final
Future
<
String
>
_savedStderr
;
}
/// The result of running a command using [startCommand] and [runCommand];
...
...
@@ -105,46 +91,50 @@ Future<Command> startCommand(String executable, List<String> arguments, {
})
async
{
final
String
commandDescription
=
'
${path.relative(executable, from: workingDirectory)}
${arguments.join(' ')}
'
;
final
String
relativeWorkingDir
=
path
.
relative
(
workingDirectory
??
io
.
Directory
.
current
.
path
);
print
Progress
(
'RUNNING'
,
relativeWorkingDir
,
commandDescription
);
print
(
'RUNNING: cd
$cyan$relativeWorkingDir$reset
;
$green$commandDescription$reset
'
);
final
Stopwatch
time
=
Stopwatch
()..
start
();
final
io
.
Process
process
=
await
io
.
Process
.
start
(
executable
,
arguments
,
workingDirectory:
workingDirectory
,
environment:
environment
,
);
Future
<
List
<
List
<
int
>>>
savedStdout
=
Future
<
List
<
List
<
int
>>>.
value
(<
List
<
int
>>[]);
Future
<
List
<
List
<
int
>>>
savedStderr
=
Future
<
List
<
List
<
int
>>>.
value
(<
List
<
int
>>[]);
final
Stream
<
List
<
int
>>
stdoutSource
=
process
.
stdout
.
transform
<
String
>(
const
Utf8Decoder
())
.
transform
(
const
LineSplitter
())
.
where
((
String
line
)
=>
removeLine
==
null
||
!
removeLine
(
line
))
.
map
((
String
line
)
{
final
String
formattedLine
=
'
$line
\n
'
;
if
(
outputListener
!=
null
)
{
outputListener
(
formattedLine
,
process
);
}
return
formattedLine
;
})
.
transform
(
const
Utf8Encoder
());
switch
(
outputMode
)
{
case
OutputMode
.
print
:
stdoutSource
.
listen
((
List
<
int
>
output
)
{
io
.
stdout
.
add
(
output
);
savedStdout
.
then
((
List
<
List
<
int
>>
list
)
=>
list
.
add
(
output
));
});
process
.
stderr
.
listen
((
List
<
int
>
output
)
{
io
.
stdout
.
add
(
output
);
savedStdout
.
then
((
List
<
List
<
int
>>
list
)
=>
list
.
add
(
output
));
});
break
;
case
OutputMode
.
capture
:
savedStdout
=
stdoutSource
.
toList
();
savedStderr
=
process
.
stderr
.
toList
();
break
;
}
return
Command
.
_
(
process
,
time
,
savedStdout
,
savedStderr
);
return
Command
.
_
(
process
,
time
,
process
.
stdout
.
transform
<
String
>(
const
Utf8Decoder
())
.
transform
(
const
LineSplitter
())
.
where
((
String
line
)
=>
removeLine
==
null
||
!
removeLine
(
line
))
.
map
<
String
>((
String
line
)
{
final
String
formattedLine
=
'
$line
\n
'
;
if
(
outputListener
!=
null
)
{
outputListener
(
formattedLine
,
process
);
}
switch
(
outputMode
)
{
case
OutputMode
.
print
:
print
(
line
);
break
;
case
OutputMode
.
capture
:
break
;
}
return
line
;
})
.
join
(
'
\n
'
),
process
.
stderr
.
transform
<
String
>(
const
Utf8Decoder
())
.
transform
(
const
LineSplitter
())
.
map
<
String
>((
String
line
)
{
switch
(
outputMode
)
{
case
OutputMode
.
print
:
print
(
line
);
break
;
case
OutputMode
.
capture
:
break
;
}
return
line
;
})
.
join
(
'
\n
'
),
);
}
/// Runs the `executable` and waits until the process exits.
...
...
@@ -182,7 +172,12 @@ Future<CommandResult> runCommand(String executable, List<String> arguments, {
outputListener:
outputListener
,
);
final
CommandResult
result
=
await
command
.
onExit
;
final
CommandResult
result
=
CommandResult
.
_
(
await
command
.
process
.
exitCode
,
command
.
_time
.
elapsed
,
await
command
.
_savedStdout
,
await
command
.
_savedStderr
,
);
if
((
result
.
exitCode
==
0
)
==
expectNonZeroExit
||
(
expectedExitCode
!=
null
&&
result
.
exitCode
!=
expectedExitCode
))
{
// Print the output when we get unexpected results (unless output was
...
...
@@ -191,28 +186,24 @@ Future<CommandResult> runCommand(String executable, List<String> arguments, {
case
OutputMode
.
print
:
break
;
case
OutputMode
.
capture
:
io
.
stdout
.
writeln
(
result
.
flattenedStdout
);
io
.
stdout
.
writeln
(
result
.
flattenedStderr
);
print
(
result
.
flattenedStdout
);
print
(
result
.
flattenedStderr
);
break
;
}
foundError
(<
String
>[
if
(
failureMessage
!=
null
)
failureMessage
else
'
$
{bold}
ERROR:
${red}
Last command exited with
${result.exitCode}
(expected:
${expectNonZeroExit ? (expectedExitCode ?? 'non-zero') : 'zero'}
)
.
$reset
'
,
'
$
bold${red}
Command exited with exit code
${result.exitCode}
but expected:
${expectNonZeroExit ? (expectedExitCode ?? 'non-zero') : 'zero'}
exit code
.
$reset
'
,
'
${bold}
Command:
$green$commandDescription$reset
'
,
'
${bold}
Relative working directory:
$cyan$relativeWorkingDir$reset
'
,
]);
}
else
{
print
(
'
$clock
ELAPSED TIME:
${prettyPrintDuration(result.elapsedTime)}
for
$green$commandDescription$reset
in
$cyan$relativeWorkingDir$reset
'
);
print
(
'ELAPSED TIME:
${prettyPrintDuration(result.elapsedTime)}
for
$green$commandDescription$reset
in
$cyan$relativeWorkingDir$reset
'
);
}
return
result
;
}
/// Flattens a nested list of UTF-8 code units into a single string.
String
_flattenToString
(
List
<
List
<
int
>>
chunks
)
=>
utf8
.
decode
(
chunks
.
expand
<
int
>((
List
<
int
>
ints
)
=>
ints
).
toList
());
/// Specifies what to do with the command output from [runCommand] and [startCommand].
enum
OutputMode
{
/// Forwards standard output and standard error streams to the test process'
...
...
This diff is collapsed.
Click to expand it.
dev/bots/service_worker_test.dart
View file @
97901da1
...
...
@@ -163,8 +163,8 @@ Future<void> runWebServiceWorkerTest({
Future
<
void
>
startAppServer
({
required
String
cacheControl
,
})
async
{
final
int
serverPort
=
await
findAvailablePort
();
final
int
browserDebugPort
=
await
findAvailablePort
();
final
int
serverPort
=
await
findAvailablePort
AndPossiblyCauseFlakyTests
();
final
int
browserDebugPort
=
await
findAvailablePort
AndPossiblyCauseFlakyTests
();
server
=
await
AppServer
.
start
(
headless:
headless
,
cacheControl:
cacheControl
,
...
...
@@ -201,7 +201,7 @@ Future<void> runWebServiceWorkerTest({
final
bool
shouldExpectFlutterJs
=
testType
!=
ServiceWorkerTestType
.
withoutFlutterJs
;
print
(
'BEGIN runWebServiceWorkerTest(headless:
$headless
, testType:
$testType
)
\n
'
);
print
(
'BEGIN runWebServiceWorkerTest(headless:
$headless
, testType:
$testType
)'
);
try
{
/////
...
...
@@ -417,7 +417,7 @@ Future<void> runWebServiceWorkerTest({
await
server
?.
stop
();
}
print
(
'END runWebServiceWorkerTest(headless:
$headless
, testType:
$testType
)
\n
'
);
print
(
'END runWebServiceWorkerTest(headless:
$headless
, testType:
$testType
)'
);
}
Future
<
void
>
runWebServiceWorkerTestWithCachingResources
({
...
...
@@ -435,8 +435,8 @@ Future<void> runWebServiceWorkerTestWithCachingResources({
Future
<
void
>
startAppServer
({
required
String
cacheControl
,
})
async
{
final
int
serverPort
=
await
findAvailablePort
();
final
int
browserDebugPort
=
await
findAvailablePort
();
final
int
serverPort
=
await
findAvailablePort
AndPossiblyCauseFlakyTests
();
final
int
browserDebugPort
=
await
findAvailablePort
AndPossiblyCauseFlakyTests
();
server
=
await
AppServer
.
start
(
headless:
headless
,
cacheControl:
cacheControl
,
...
...
@@ -472,7 +472,7 @@ Future<void> runWebServiceWorkerTestWithCachingResources({
final
bool
shouldExpectFlutterJs
=
testType
!=
ServiceWorkerTestType
.
withoutFlutterJs
;
print
(
'BEGIN runWebServiceWorkerTestWithCachingResources(headless:
$headless
, testType:
$testType
)
\n
'
);
print
(
'BEGIN runWebServiceWorkerTestWithCachingResources(headless:
$headless
, testType:
$testType
)'
);
try
{
//////////////////////////////////////////////////////
...
...
@@ -576,7 +576,7 @@ Future<void> runWebServiceWorkerTestWithCachingResources({
await
server
?.
stop
();
}
print
(
'END runWebServiceWorkerTestWithCachingResources(headless:
$headless
, testType:
$testType
)
\n
'
);
print
(
'END runWebServiceWorkerTestWithCachingResources(headless:
$headless
, testType:
$testType
)'
);
}
Future
<
void
>
runWebServiceWorkerTestWithBlockedServiceWorkers
({
...
...
@@ -593,8 +593,8 @@ Future<void> runWebServiceWorkerTestWithBlockedServiceWorkers({
Future
<
void
>
startAppServer
({
required
String
cacheControl
,
})
async
{
final
int
serverPort
=
await
findAvailablePort
();
final
int
browserDebugPort
=
await
findAvailablePort
();
final
int
serverPort
=
await
findAvailablePort
AndPossiblyCauseFlakyTests
();
final
int
browserDebugPort
=
await
findAvailablePort
AndPossiblyCauseFlakyTests
();
server
=
await
AppServer
.
start
(
headless:
headless
,
cacheControl:
cacheControl
,
...
...
@@ -628,7 +628,7 @@ Future<void> runWebServiceWorkerTestWithBlockedServiceWorkers({
workingDirectory:
_testAppWebDirectory
,
);
print
(
'BEGIN runWebServiceWorkerTestWithBlockedServiceWorkers(headless:
$headless
)
\n
'
);
print
(
'BEGIN runWebServiceWorkerTestWithBlockedServiceWorkers(headless:
$headless
)'
);
try
{
await
_rebuildApp
(
version:
1
,
testType:
ServiceWorkerTestType
.
blockedServiceWorkers
,
target:
_targetWithBlockedServiceWorkers
);
...
...
@@ -662,5 +662,5 @@ Future<void> runWebServiceWorkerTestWithBlockedServiceWorkers({
);
await
server
?.
stop
();
}
print
(
'END runWebServiceWorkerTestWithBlockedServiceWorkers(headless:
$headless
)
\n
'
);
print
(
'END runWebServiceWorkerTestWithBlockedServiceWorkers(headless:
$headless
)'
);
}
This diff is collapsed.
Click to expand it.
dev/bots/test.dart
View file @
97901da1
This diff is collapsed.
Click to expand it.
dev/bots/test/analyze_test.dart
View file @
97901da1
This diff is collapsed.
Click to expand it.
dev/bots/test/test_test.dart
View file @
97901da1
...
...
@@ -17,7 +17,17 @@ import 'common.dart';
/// will include the process result's stdio in the failure message.
void
expectExitCode
(
ProcessResult
result
,
int
expectedExitCode
)
{
if
(
result
.
exitCode
!=
expectedExitCode
)
{
fail
(
'Failure due to exit code
${result.exitCode}
\n
STDOUT:
\n
${result.stdout}
\n
STDERR:
\n
${result.stderr}
'
);
fail
(
'Process
${result.pid}
exitted with the wrong exit code.
\n
'
'
\n
'
'EXPECTED: exit code
$expectedExitCode
\n
'
'ACTUAL: exit code
${result.exitCode}
\n
'
'
\n
'
'STDOUT:
\n
'
'
${result.stdout}
\n
'
'STDERR:
\n
'
'
${result.stderr}
'
);
}
}
...
...
@@ -96,10 +106,13 @@ void main() {
group
(
'test.dart script'
,
()
{
const
ProcessManager
processManager
=
LocalProcessManager
();
Future
<
ProcessResult
>
runScript
(
[
Map
<
String
,
String
>?
environment
,
List
<
String
>
otherArgs
=
const
<
String
>[]])
async
{
Future
<
ProcessResult
>
runScript
([
Map
<
String
,
String
>?
environment
,
List
<
String
>
otherArgs
=
const
<
String
>[],
])
async
{
final
String
dart
=
path
.
absolute
(
path
.
join
(
'..'
,
'..'
,
'bin'
,
'cache'
,
'dart-sdk'
,
'bin'
,
'dart'
));
path
.
join
(
'..'
,
'..'
,
'bin'
,
'cache'
,
'dart-sdk'
,
'bin'
,
'dart'
),
);
final
ProcessResult
scriptProcess
=
processManager
.
runSync
(<
String
>[
dart
,
'test.dart'
,
...
...
@@ -112,21 +125,21 @@ void main() {
// When updating this test, try to pick shard numbers that ensure we're checking
// that unequal test distributions don't miss tests.
ProcessResult
result
=
await
runScript
(
<
String
,
String
>{
'SHARD'
:
'test_harness_tests'
,
'SUBSHARD'
:
'1_3'
},
<
String
,
String
>{
'SHARD'
:
kTestHarnessShardName
,
'SUBSHARD'
:
'1_3'
},
);
expectExitCode
(
result
,
0
);
expect
(
result
.
stdout
,
contains
(
'Selecting subshard 1 of 3 (
range
1-3 of 8)'
));
expect
(
result
.
stdout
,
contains
(
'Selecting subshard 1 of 3 (
tests
1-3 of 8)'
));
result
=
await
runScript
(
<
String
,
String
>{
'SHARD'
:
'test_harness_tests'
,
'SUBSHARD'
:
'3_3'
},
<
String
,
String
>{
'SHARD'
:
kTestHarnessShardName
,
'SUBSHARD'
:
'3_3'
},
);
expectExitCode
(
result
,
0
);
expect
(
result
.
stdout
,
contains
(
'Selecting subshard 3 of 3 (
range
7-8 of 8)'
));
expect
(
result
.
stdout
,
contains
(
'Selecting subshard 3 of 3 (
tests
7-8 of 8)'
));
});
test
(
'exits with code 1 when SUBSHARD index greater than total'
,
()
async
{
final
ProcessResult
result
=
await
runScript
(
<
String
,
String
>{
'SHARD'
:
'test_harness_tests'
,
'SUBSHARD'
:
'100_99'
},
<
String
,
String
>{
'SHARD'
:
kTestHarnessShardName
,
'SUBSHARD'
:
'100_99'
},
);
expectExitCode
(
result
,
1
);
expect
(
result
.
stdout
,
contains
(
'Invalid subshard name'
));
...
...
This diff is collapsed.
Click to expand it.
dev/bots/utils.dart
View file @
97901da1
...
...
@@ -2,28 +2,82 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:
core'
as
core_internals
show
print
;
import
'dart:
async'
;
import
'dart:core'
hide
print
;
import
'dart:io'
as
system
show
exit
;
import
'dart:io'
hide
exit
;
import
'dart:math'
as
math
;
import
'package:meta/meta.dart'
;
const
Duration
_quietTimeout
=
Duration
(
minutes:
10
);
// how long the output should be hidden between calls to printProgress before just being verbose
final
bool
hasColor
=
stdout
.
supportsAnsiEscapes
;
final
String
bold
=
hasColor
?
'
\
x1B[1m'
:
''
;
// used for shard titles
final
String
red
=
hasColor
?
'
\
x1B[31m'
:
''
;
// used for errors
final
String
green
=
hasColor
?
'
\
x1B[32m'
:
''
;
// used for section titles, commands
final
String
yellow
=
hasColor
?
'
\
x1B[33m'
:
''
;
// used for skips
final
String
cyan
=
hasColor
?
'
\
x1B[36m'
:
''
;
// used for paths
final
String
reverse
=
hasColor
?
'
\
x1B[7m'
:
''
;
// used for clocks
final
String
bold
=
hasColor
?
'
\
x1B[1m'
:
''
;
// shard titles
final
String
red
=
hasColor
?
'
\
x1B[31m'
:
''
;
// errors
final
String
green
=
hasColor
?
'
\
x1B[32m'
:
''
;
// section titles, commands
final
String
yellow
=
hasColor
?
'
\
x1B[33m'
:
''
;
// indications that a test was skipped (usually renders orange or brown)
final
String
cyan
=
hasColor
?
'
\
x1B[36m'
:
''
;
// paths
final
String
reverse
=
hasColor
?
'
\
x1B[7m'
:
''
;
// clocks
final
String
gray
=
hasColor
?
'
\
x1B[30m'
:
''
;
// subtle decorative items (usually renders as dark gray)
final
String
white
=
hasColor
?
'
\
x1B[37m'
:
''
;
// last log line (usually renders as light gray)
final
String
reset
=
hasColor
?
'
\
x1B[0m'
:
''
;
final
String
redLine
=
'
$red
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
$reset
'
;
typedef
PrintCallback
=
void
Function
(
Object
line
);
const
int
kESC
=
0x1B
;
const
int
kOpenSquareBracket
=
0x5B
;
const
int
kCSIParameterRangeStart
=
0x30
;
const
int
kCSIParameterRangeEnd
=
0x3F
;
const
int
kCSIIntermediateRangeStart
=
0x20
;
const
int
kCSIIntermediateRangeEnd
=
0x2F
;
const
int
kCSIFinalRangeStart
=
0x40
;
const
int
kCSIFinalRangeEnd
=
0x7E
;
String
get
redLine
{
if
(
hasColor
)
{
return
'
$red${'━' * stdout.terminalColumns}$reset
'
;
}
return
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'
;
}
String
get
clock
{
final
DateTime
now
=
DateTime
.
now
();
return
'
$reverse
▌'
'
${now.hour.toString().padLeft(2, "0")}
:'
'
${now.minute.toString().padLeft(2, "0")}
:'
'
${now.second.toString().padLeft(2, "0")}
'
'▐
$reset
'
;
}
String
prettyPrintDuration
(
Duration
duration
)
{
String
result
=
''
;
final
int
minutes
=
duration
.
inMinutes
;
if
(
minutes
>
0
)
{
result
+=
'
${minutes}
min '
;
}
final
int
seconds
=
duration
.
inSeconds
-
minutes
*
60
;
final
int
milliseconds
=
duration
.
inMilliseconds
-
(
seconds
*
1000
+
minutes
*
60
*
1000
);
result
+=
'
$seconds
.
${milliseconds.toString().padLeft(3, "0")}
s'
;
return
result
;
}
typedef
PrintCallback
=
void
Function
(
Object
?
line
);
typedef
VoidCallback
=
void
Function
();
// Allow print() to be overridden, for tests.
PrintCallback
print
=
core_internals
.
print
;
//
// Files that import this library should not import `print` from dart:core
// and should not use dart:io's `stdout` or `stderr`.
//
// By default this hides log lines between `printProgress` calls unless a
// timeout expires or anything calls `foundError`.
//
// Also used to implement `--verbose` in test.dart.
PrintCallback
print
=
_printQuietly
;
// Called by foundError and used to implement `--abort-on-error` in test.dart.
VoidCallback
?
onError
;
bool
get
hasError
=>
_hasError
;
bool
_hasError
=
false
;
...
...
@@ -31,22 +85,44 @@ bool _hasError = false;
Iterable
<
String
>
get
errorMessages
=>
_errorMessages
;
List
<
String
>
_errorMessages
=
<
String
>[];
final
List
<
String
>
_pendingLogs
=
<
String
>[];
Timer
?
_hideTimer
;
// When this is null, the output is verbose.
void
foundError
(
List
<
String
>
messages
)
{
assert
(
messages
.
isNotEmpty
);
print
(
redLine
);
messages
.
forEach
(
print
);
print
(
redLine
);
// Make the error message easy to notice in the logs by
// wrapping it in a red box.
final
int
width
=
math
.
max
(
15
,
(
hasColor
?
stdout
.
terminalColumns
:
80
)
-
1
);
print
(
'
$red
╔═╡
${bold}
ERROR
$reset$red
╞═
${"═" * (width - 9)}
'
);
for
(
final
String
message
in
messages
.
expand
((
String
line
)
=>
line
.
split
(
'
\n
'
)))
{
print
(
'
$red
║
$reset
$message
'
);
}
print
(
'
$red
╚
${"═" * width}
'
);
// Normally, "print" actually prints to the log. To make the errors visible,
// and to include useful context, print the entire log up to this point, and
// clear it. Subsequent messages will continue to not be logged until there is
// another error.
_pendingLogs
.
forEach
(
_printLoudly
);
_pendingLogs
.
clear
();
_errorMessages
.
addAll
(
messages
);
_hasError
=
true
;
if
(
onError
!=
null
)
{
onError
!();
}
}
@visibleForTesting
void
resetErrorStatus
(
)
{
_hasError
=
false
;
_errorMessages
.
clear
();
_pendingLogs
.
clear
();
_hideTimer
?.
cancel
();
_hideTimer
=
null
;
}
Never
reportErrorsAndExit
(
)
{
_hideTimer
?.
cancel
();
_hideTimer
=
null
;
print
(
redLine
);
print
(
'For your convenience, the error messages reported above are repeated here:'
);
_errorMessages
.
forEach
(
print
);
...
...
@@ -54,35 +130,93 @@ Never reportErrorsAndExit() {
system
.
exit
(
1
);
}
String
get
clock
{
final
DateTime
now
=
DateTime
.
now
();
return
'
$reverse
▌'
'
${now.hour.toString().padLeft(2, "0")}
:'
'
${now.minute.toString().padLeft(2, "0")}
:'
'
${now.second.toString().padLeft(2, "0")}
'
'▐
$reset
'
;
void
printProgress
(
String
message
)
{
_pendingLogs
.
clear
();
_hideTimer
?.
cancel
();
_hideTimer
=
null
;
print
(
'
$clock
$message
$reset
'
);
if
(
hasColor
)
{
// This sets up a timer to switch to verbose mode when the tests take too long,
// so that if a test hangs we can see the logs.
// (This is only supported with a color terminal. When the terminal doesn't
// support colors, the scripts just print everything verbosely, that way in
// CI there's nothing hidden.)
_hideTimer
=
Timer
(
_quietTimeout
,
()
{
_hideTimer
=
null
;
_pendingLogs
.
forEach
(
_printLoudly
);
_pendingLogs
.
clear
();
});
}
}
String
prettyPrintDuration
(
Duration
duration
)
{
String
result
=
''
;
final
int
minutes
=
duration
.
inMinutes
;
if
(
minutes
>
0
)
{
result
+=
'
${minutes}
min '
;
final
Pattern
_lineBreak
=
RegExp
(
r'[\r\n]'
);
void
_printQuietly
(
Object
?
message
)
{
// The point of this function is to avoid printing its output unless the timer
// has gone off in which case the function assumes verbose mode is active and
// prints everything. To show that progress is still happening though, rather
// than showing nothing at all, it instead shows the last line of output and
// keeps overwriting it. To do this in color mode, carefully measures the line
// of text ignoring color codes, which is what the parser below does.
if
(
_hideTimer
!=
null
)
{
_pendingLogs
.
add
(
message
.
toString
());
String
line
=
'
$message
'
.
trimRight
();
final
int
start
=
line
.
lastIndexOf
(
_lineBreak
)
+
1
;
int
index
=
start
;
int
length
=
0
;
while
(
index
<
line
.
length
&&
length
<
stdout
.
terminalColumns
)
{
if
(
line
.
codeUnitAt
(
index
)
==
kESC
)
{
// 0x1B
index
+=
1
;
if
(
index
<
line
.
length
&&
line
.
codeUnitAt
(
index
)
==
kOpenSquareBracket
)
{
// 0x5B, [
// That was the start of a CSI sequence.
index
+=
1
;
while
(
index
<
line
.
length
&&
line
.
codeUnitAt
(
index
)
>=
kCSIParameterRangeStart
&&
line
.
codeUnitAt
(
index
)
<=
kCSIParameterRangeEnd
)
{
// 0x30..0x3F
index
+=
1
;
// ...parameter bytes...
}
while
(
index
<
line
.
length
&&
line
.
codeUnitAt
(
index
)
>=
kCSIIntermediateRangeStart
&&
line
.
codeUnitAt
(
index
)
<=
kCSIIntermediateRangeEnd
)
{
// 0x20..0x2F
index
+=
1
;
// ...intermediate bytes...
}
if
(
index
<
line
.
length
&&
line
.
codeUnitAt
(
index
)
>=
kCSIFinalRangeStart
&&
line
.
codeUnitAt
(
index
)
<=
kCSIFinalRangeEnd
)
{
// 0x40..0x7E
index
+=
1
;
// ...final byte.
}
}
}
else
{
index
+=
1
;
length
+=
1
;
}
}
line
=
line
.
substring
(
start
,
index
);
if
(
line
.
isNotEmpty
)
{
stdout
.
write
(
'
\r
\
x1B[2K
$white$line$reset
'
);
}
}
else
{
_printLoudly
(
'
$message
'
);
}
final
int
seconds
=
duration
.
inSeconds
-
minutes
*
60
;
final
int
milliseconds
=
duration
.
inMilliseconds
-
(
seconds
*
1000
+
minutes
*
60
*
1000
);
result
+=
'
$seconds
.
${milliseconds.toString().padLeft(3, "0")}
s'
;
return
result
;
}
void
printProgress
(
String
action
,
String
workingDir
,
String
command
)
{
print
(
'
$clock
$action
: cd
$cyan$workingDir$reset
;
$green$command$reset
'
);
void
_printLoudly
(
String
message
)
{
if
(
hasColor
)
{
// Overwrite the last line written by _printQuietly.
stdout
.
writeln
(
'
\r
\
x1B[2K
$reset${message.trimRight()}
'
);
}
else
{
stdout
.
writeln
(
message
);
}
}
// THE FOLLOWING CODE IS A VIOLATION OF OUR STYLE GUIDE
// BECAUSE IT INTRODUCES A VERY FLAKY RACE CONDITION
// https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#never-check-if-a-port-is-available-before-using-it-never-add-timeouts-and-other-race-conditions
// DO NOT USE THE FOLLOWING FUNCTIONS
// DO NOT WRITE CODE LIKE THE FOLLOWING FUNCTIONS
// https://github.com/flutter/flutter/issues/109474
int
_portCounter
=
8080
;
/// Finds the next available local port.
Future
<
int
>
findAvailablePort
()
async
{
Future
<
int
>
findAvailablePort
AndPossiblyCauseFlakyTests
()
async
{
while
(!
await
_isPortAvailable
(
_portCounter
))
{
_portCounter
+=
1
;
}
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment