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
Aug 14, 2022
by
Ian Hickson
Committed by
GitHub
Aug 14, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Cleaner test.dart output. (#109206)
parent
d50c5b1b
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
}}'
;
}
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'
...
...
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
)'
);
}
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'
));
...
...
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
;
}
...
...
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