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
971ca4b8
Commit
971ca4b8
authored
Dec 15, 2016
by
Ian Hickson
Committed by
GitHub
Dec 15, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Check exit code for test subprocess (#7269)
parent
502592e5
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
310 additions
and
158 deletions
+310
-158
README.md
dev/automated_tests/test_smoke_test/README.md
+1
-1
crash1_test.dart
dev/automated_tests/test_smoke_test/crash1_test.dart
+16
-0
crash2_test.dart
dev/automated_tests/test_smoke_test/crash2_test.dart
+12
-0
fail_test.dart
dev/automated_tests/test_smoke_test/fail_test.dart
+1
-1
missing_import_test.broken_dart
...ted_tests/test_smoke_test/missing_import_test.broken_dart
+10
-0
pass_test.dart
dev/automated_tests/test_smoke_test/pass_test.dart
+1
-1
syntax_error_test.broken_dart
...mated_tests/test_smoke_test/syntax_error_test.broken_dart
+9
-0
test.sh
dev/bots/test.sh
+4
-0
test.dart
packages/flutter_tools/lib/src/commands/test.dart
+7
-7
coverage_collector.dart
packages/flutter_tools/lib/src/test/coverage_collector.dart
+3
-3
flutter_platform.dart
packages/flutter_tools/lib/src/test/flutter_platform.dart
+246
-145
No files found.
dev/automated_tests/test_smoke_test/README.md
View file @
971ca4b8
This directory is used by //
/flutter/travi
s/test.sh to verify that
This directory is used by //
flutter/dev/bot
s/test.sh to verify that
`flutter test`
actually correctly fails when a test fails.
`flutter test`
actually correctly fails when a test fails.
dev/automated_tests/test_smoke_test/crash1_test.dart
0 → 100644
View file @
971ca4b8
// 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
'dart:io'
as
system
;
import
'package:flutter_test/flutter_test.dart'
;
// this is a test to make sure our tests consider engine crashes to be failures
// see //flutter/dev/bots/test.sh
void
main
(
)
{
test
(
'test smoke test -- this test should fail'
,
()
async
{
system
.
Process
.
killPid
(
system
.
pid
,
system
.
ProcessSignal
.
SIGSEGV
);
});
}
\ No newline at end of file
dev/automated_tests/test_smoke_test/crash2_test.dart
0 → 100644
View file @
971ca4b8
// 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
'dart:io'
as
system
;
// this is a test to make sure our tests consider engine crashes to be failures
// see //flutter/dev/bots/test.sh
void
main
(
)
{
system
.
Process
.
killPid
(
system
.
pid
,
system
.
ProcessSignal
.
SIGSEGV
);
}
\ No newline at end of file
dev/automated_tests/test_smoke_test/fail_test.dart
View file @
971ca4b8
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
// this is a test to make sure our tests actually catch failures
// this is a test to make sure our tests actually catch failures
// see //
/flutter/travi
s/test.sh
// see //
flutter/dev/bot
s/test.sh
void
main
(
)
{
void
main
(
)
{
test
(
'test smoke test -- this test SHOULD FAIL'
,
()
async
{
test
(
'test smoke test -- this test SHOULD FAIL'
,
()
async
{
...
...
dev/automated_tests/test_smoke_test/missing_import_test.broken_dart
0 → 100644
View file @
971ca4b8
// 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.
// this is a test to make sure our tests consider syntax errors to be failures
// see //flutter/dev/bots/test.sh
void main() {
fail(); // inspired by https://github.com/flutter/flutter/issues/2698
}
dev/automated_tests/test_smoke_test/pass_test.dart
View file @
971ca4b8
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
// this is a test to make sure our tests actually catch failures
// this is a test to make sure our tests actually catch failures
// see //
/flutter/travi
s/test.sh
// see //
flutter/dev/bot
s/test.sh
void
main
(
)
{
void
main
(
)
{
test
(
'test smoke test -- this test should pass'
,
()
async
{
test
(
'test smoke test -- this test should pass'
,
()
async
{
...
...
dev/automated_tests/test_smoke_test/syntax_error_test.broken_dart
0 → 100644
View file @
971ca4b8
// 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.
// this is a test to make sure our tests consider syntax errors to be failures
// see //flutter/dev/bots/test.sh
The challenge: demand satisfaction
If they apologize, no need for further action.
dev/bots/test.sh
View file @
971ca4b8
...
@@ -22,6 +22,10 @@ flutter analyze --flutter-repo
...
@@ -22,6 +22,10 @@ flutter analyze --flutter-repo
# verify that the tests actually return failure on failure and success on success
# verify that the tests actually return failure on failure and success on success
(
cd
dev/automated_tests
;
!
flutter
test
test_smoke_test/fail_test.dart
>
/dev/null
)
(
cd
dev/automated_tests
;
!
flutter
test
test_smoke_test/fail_test.dart
>
/dev/null
)
(
cd
dev/automated_tests
;
flutter
test
test_smoke_test/pass_test.dart
>
/dev/null
)
(
cd
dev/automated_tests
;
flutter
test
test_smoke_test/pass_test.dart
>
/dev/null
)
(
cd
dev/automated_tests
;
!
flutter
test
test_smoke_test/crash1_test.dart
>
/dev/null
)
(
cd
dev/automated_tests
;
!
flutter
test
test_smoke_test/crash2_test.dart
>
/dev/null
)
(
cd
dev/automated_tests
;
!
flutter
test
test_smoke_test/syntax_error_test.broken_dart
>
/dev/null
)
(
cd
dev/automated_tests
;
!
flutter
test
test_smoke_test/missing_import_test.broken_dart
>
/dev/null
)
(
cd
packages/flutter_driver
;
!
flutter drive
--use-existing-app
-t
test_driver/failure.dart
>
/dev/null 2>&1
)
(
cd
packages/flutter_driver
;
!
flutter drive
--use-existing-app
-t
test_driver/failure.dart
>
/dev/null 2>&1
)
COVERAGE_FLAG
=
COVERAGE_FLAG
=
...
...
packages/flutter_tools/lib/src/commands/test.dart
View file @
971ca4b8
...
@@ -6,7 +6,7 @@ import 'dart:async';
...
@@ -6,7 +6,7 @@ import 'dart:async';
import
'dart:io'
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
import
'package:path/path.dart'
as
path
;
import
'package:test/src/executable.dart'
as
executable
;
// ignore: implementation_imports
import
'package:test/src/executable.dart'
as
test
;
// ignore: implementation_imports
import
'../base/common.dart'
;
import
'../base/common.dart'
;
import
'../base/logger.dart'
;
import
'../base/logger.dart'
;
...
@@ -77,9 +77,9 @@ class TestCommand extends FlutterCommand {
...
@@ -77,9 +77,9 @@ class TestCommand extends FlutterCommand {
Directory
.
current
=
testDirectory
;
Directory
.
current
=
testDirectory
;
}
}
printTrace
(
'running test package with arguments:
$testArgs
'
);
printTrace
(
'running test package with arguments:
$testArgs
'
);
await
executable
.
main
(
testArgs
);
await
test
.
main
(
testArgs
);
// test.main() sets dart:io's exitCode global.
printTrace
(
'test package returned with exit code
$exitCode
'
);
printTrace
(
'test package returned with exit code
$exitCode
'
);
return
exitCode
;
return
exitCode
;
}
finally
{
}
finally
{
Directory
.
current
=
currentDirectory
;
Directory
.
current
=
currentDirectory
;
...
@@ -164,10 +164,10 @@ class TestCommand extends FlutterCommand {
...
@@ -164,10 +164,10 @@ class TestCommand extends FlutterCommand {
if
(
argResults
[
'coverage'
])
if
(
argResults
[
'coverage'
])
testArgs
.
insert
(
0
,
'--concurrency=1'
);
testArgs
.
insert
(
0
,
'--concurrency=1'
);
loader
.
installHook
()
;
final
String
shellPath
=
tools
.
getHostToolPath
(
HostTool
.
SkyShell
)
??
Platform
.
environment
[
'SKY_SHELL'
]
;
loader
.
shellPath
=
tools
.
getHostToolPath
(
HostTool
.
SkyShell
);
if
(!
FileSystemEntity
.
isFileSync
(
shellPath
))
if
(!
FileSystemEntity
.
isFileSync
(
loader
.
shellPath
))
throwToolExit
(
'Cannot find Flutter shell at
$shellPath
'
);
throwToolExit
(
'Cannot find Flutter shell at
${loader.shellPath}
'
);
loader
.
installHook
(
shellPath:
shellPath
);
Cache
.
releaseLockEarly
();
Cache
.
releaseLockEarly
();
...
...
packages/flutter_tools/lib/src/test/coverage_collector.dart
View file @
971ca4b8
...
@@ -20,14 +20,14 @@ class CoverageCollector {
...
@@ -20,14 +20,14 @@ class CoverageCollector {
void
collectCoverage
({
void
collectCoverage
({
String
host
,
String
host
,
int
port
,
int
port
,
Process
processToKill
Process
processToKill
,
})
{
})
{
if
(
enabled
)
{
if
(
enabled
)
{
assert
(
_jobs
!=
null
);
assert
(
_jobs
!=
null
);
_jobs
.
add
(
_startJob
(
_jobs
.
add
(
_startJob
(
host:
host
,
host:
host
,
port:
port
,
port:
port
,
processToKill:
processToKill
processToKill:
processToKill
,
));
));
}
else
{
}
else
{
processToKill
.
kill
();
processToKill
.
kill
();
...
@@ -37,7 +37,7 @@ class CoverageCollector {
...
@@ -37,7 +37,7 @@ class CoverageCollector {
Future
<
Null
>
_startJob
({
Future
<
Null
>
_startJob
({
String
host
,
String
host
,
int
port
,
int
port
,
Process
processToKill
Process
processToKill
,
})
async
{
})
async
{
int
pid
=
processToKill
.
pid
;
int
pid
=
processToKill
.
pid
;
printTrace
(
'collecting coverage data from pid
$pid
on port
$port
'
);
printTrace
(
'collecting coverage data from pid
$pid
on port
$port
'
);
...
...
packages/flutter_tools/lib/src/test/flutter_platform.dart
View file @
971ca4b8
...
@@ -7,7 +7,6 @@ import 'dart:convert';
...
@@ -7,7 +7,6 @@ import 'dart:convert';
import
'dart:io'
;
import
'dart:io'
;
import
'dart:math'
as
math
;
import
'dart:math'
as
math
;
import
'package:async/async.dart'
;
import
'package:path/path.dart'
as
path
;
import
'package:path/path.dart'
as
path
;
import
'package:stream_channel/stream_channel.dart'
;
import
'package:stream_channel/stream_channel.dart'
;
...
@@ -20,113 +19,198 @@ import '../dart/package_map.dart';
...
@@ -20,113 +19,198 @@ import '../dart/package_map.dart';
import
'../globals.dart'
;
import
'../globals.dart'
;
import
'coverage_collector.dart'
;
import
'coverage_collector.dart'
;
final
String
_kSkyShell
=
Platform
.
environment
[
'SKY_SHELL'
]
;
const
Duration
_kTestStartupTimeout
=
const
Duration
(
seconds:
5
)
;
final
InternetAddress
_kHost
=
InternetAddress
.
LOOPBACK_IP_V4
;
final
InternetAddress
_kHost
=
InternetAddress
.
LOOPBACK_IP_V4
;
const
String
_kRunnerPath
=
'/runner'
;
const
String
_kShutdownPath
=
'/shutdown'
;
String
shellPath
;
void
installHook
(
{
String
shellPath
})
{
hack
.
registerPlatformPlugin
(<
TestPlatform
>[
TestPlatform
.
vm
],
()
=>
new
FlutterPlatform
(
shellPath:
shellPath
));
List
<
String
>
fontDirectories
=
<
String
>[
cache
.
getCacheArtifacts
().
path
];
void
installHook
(
)
{
hack
.
registerPlatformPlugin
(<
TestPlatform
>[
TestPlatform
.
vm
],
()
=>
new
FlutterPlatform
());
}
}
class
_ServerInfo
{
enum
_InitialResult
{
crashed
,
timedOut
,
connected
}
final
String
url
;
enum
_TestResult
{
crashed
,
harnessBailed
,
completed
}
final
String
shutdownUrl
;
typedef
Future
<
Null
>
_Finalizer
();
final
Future
<
WebSocket
>
socket
;
final
HttpServer
server
;
_ServerInfo
(
this
.
server
,
this
.
url
,
this
.
shutdownUrl
,
this
.
socket
);
class
FlutterPlatform
extends
PlatformPlugin
{
}
FlutterPlatform
({
this
.
shellPath
})
{
assert
(
shellPath
!=
null
);
}
Future
<
_ServerInfo
>
_startServer
()
async
{
final
String
shellPath
;
HttpServer
server
=
await
HttpServer
.
bind
(
_kHost
,
0
);
Completer
<
WebSocket
>
socket
=
new
Completer
<
WebSocket
>();
server
.
listen
((
HttpRequest
request
)
{
if
(
request
.
uri
.
path
==
_kRunnerPath
)
socket
.
complete
(
WebSocketTransformer
.
upgrade
(
request
));
else
if
(!
socket
.
isCompleted
&&
request
.
uri
.
path
==
_kShutdownPath
)
socket
.
completeError
(
'Failed to start test'
);
});
return
new
_ServerInfo
(
server
,
'ws://
${_kHost.address}
:
${server.port}$_kRunnerPath
'
,
'ws://
${_kHost.address}
:
${server.port}$_kShutdownPath
'
,
socket
.
future
);
}
Future
<
Process
>
_startProcess
(
String
mainPath
,
{
String
packages
,
int
observatoryPort
})
{
// Each time loadChannel() is called, we spin up a local WebSocket server,
assert
(
shellPath
!=
null
||
_kSkyShell
!=
null
);
// Please provide the path to the shell in the SKY_SHELL environment variable.
// then spin up the engine in a subprocess. We pass the engine a Dart file
String
executable
=
shellPath
??
_kSkyShell
;
// that connects to our WebSocket server, then we proxy JSON messages from
List
<
String
>
arguments
=
<
String
>[];
// the test harness to the engine and back again. If at any time the engine
if
(
observatoryPort
!=
null
)
{
// crashes, we inject an error into that stream. When the process closes,
arguments
.
add
(
'--observatory-port=
$observatoryPort
'
);
// we clean everything up.
}
else
{
arguments
.
add
(
'--disable-observatory'
);
@override
StreamChannel
<
dynamic
>
loadChannel
(
String
testPath
,
TestPlatform
platform
)
{
final
StreamChannelController
<
dynamic
>
controller
=
new
StreamChannelController
<
dynamic
>(
allowForeignErrors:
false
);
_startTest
(
testPath
,
controller
.
local
);
return
controller
.
foreign
;
}
}
arguments
.
addAll
(<
String
>[
'--enable-dart-profiling'
,
'--non-interactive'
,
'--enable-checked-mode'
,
'--packages=
$packages
'
,
mainPath
]);
printTrace
(
'
$executable
${arguments.join(' ')}
'
);
Map
<
String
,
String
>
environment
=
<
String
,
String
>{
'FLUTTER_TEST'
:
'true'
,
'FONTCONFIG_FILE'
:
_fontConfigFile
.
path
,
};
return
processManager
.
start
(
executable
,
arguments
,
environment:
environment
);
}
void
_attachStandardStreams
(
Process
process
)
{
Future
<
Null
>
_startTest
(
String
testPath
,
StreamChannel
<
dynamic
>
controller
)
async
{
for
(
Stream
<
List
<
int
>>
stream
in
printTrace
(
'starting test:
$testPath
'
);
<
Stream
<
List
<
int
>>>[
process
.
stderr
,
process
.
stdout
])
{
stream
.
transform
(
UTF8
.
decoder
)
final
List
<
_Finalizer
>
finalizers
=
<
_Finalizer
>[];
.
transform
(
const
LineSplitter
())
bool
subprocessActive
=
false
;
.
listen
((
String
line
)
{
bool
controllerSinkClosed
=
false
;
if
(
line
!=
null
)
try
{
print
(
'Shell:
$line
'
);
controller
.
sink
.
done
.
then
((
_
)
{
controllerSinkClosed
=
true
;
});
// Prepare our WebSocket server to talk to the engine subproces.
HttpServer
server
=
await
HttpServer
.
bind
(
_kHost
,
0
);
finalizers
.
add
(()
async
{
await
server
.
close
(
force:
true
);
});
Completer
<
WebSocket
>
webSocket
=
new
Completer
<
WebSocket
>();
server
.
listen
((
HttpRequest
request
)
{
webSocket
.
complete
(
WebSocketTransformer
.
upgrade
(
request
));
});
});
}
}
File
_cachedFontConfig
;
// Prepare a temporary directory to store the Dart file that will talk to us.
Directory
temporaryDirectory
=
Directory
.
systemTemp
.
createTempSync
(
'dart_test_listener'
);
finalizers
.
add
(()
async
{
temporaryDirectory
.
deleteSync
(
recursive:
true
);
});
/// Returns a Fontconfig config file that limits font fallback to directories
// Prepare the Dart file that will talk to us and start the test.
/// specified in [fontDirectories].
File
listenerFile
=
new
File
(
'
${temporaryDirectory.path}
/listener.dart'
);
File
get
_fontConfigFile
{
listenerFile
.
createSync
();
if
(
_cachedFontConfig
!=
null
)
return
_cachedFontConfig
;
listenerFile
.
writeAsStringSync
(
_generateTestMain
(
testUrl:
path
.
toUri
(
path
.
absolute
(
testPath
)).
toString
(),
encodedWebsocketUrl:
Uri
.
encodeComponent
(
"ws://
${_kHost.address}
:
${server.port}
"
),
));
Directory
fontsDir
=
Directory
.
systemTemp
.
createTempSync
(
'flutter_fonts'
);
// If we are collecting coverage data, then set that up now.
int
observatoryPort
;
if
(
CoverageCollector
.
instance
.
enabled
)
{
// TODO(ianh): the random number on the next line is a landmine that will eventually
// cause a hard-to-find bug...
observatoryPort
=
CoverageCollector
.
instance
.
observatoryPort
??
new
math
.
Random
().
nextInt
(
30000
)
+
2000
;
await
CoverageCollector
.
instance
.
finishPendingJobs
();
}
StringBuffer
sb
=
new
StringBuffer
();
// Start the engine subprocess.
sb
.
writeln
(
'<fontconfig>'
);
Process
process
=
await
_startProcess
(
for
(
String
fontDir
in
fontDirectories
)
{
shellPath
,
sb
.
writeln
(
' <dir>
$fontDir
</dir>'
);
listenerFile
.
path
,
}
packages:
PackageMap
.
globalPackagesPath
,
sb
.
writeln
(
' <cachedir>/var/cache/fontconfig</cachedir>'
);
observatoryPort:
observatoryPort
,
sb
.
writeln
(
'</fontconfig>'
);
);
subprocessActive
=
true
;
finalizers
.
add
(()
async
{
if
(
subprocessActive
)
process
.
kill
();
int
exitCode
=
await
process
.
exitCode
;
subprocessActive
=
false
;
if
(!
controllerSinkClosed
&&
exitCode
!=
0
)
{
String
message
=
_getErrorMessage
(
_getExitCodeMessage
(
exitCode
,
'after tests finished'
),
testPath
,
shellPath
);
controller
.
sink
.
addError
(
new
Exception
(
message
));
}
});
_cachedFontConfig
=
new
File
(
'
${fontsDir.path}
/fonts.conf'
);
// Pipe stdout and stderr from the subprocess to our printStatus console.
_cachedFontConfig
.
createSync
();
_pipeStandardStreamsToConsole
(
process
);
_cachedFontConfig
.
writeAsStringSync
(
sb
.
toString
());
return
_cachedFontConfig
;
}
class
FlutterPlatform
extends
PlatformPlugin
{
// At this point, three things can happen next:
@override
// The engine could crash, in which case process.exitCode will complete.
StreamChannel
<
dynamic
>
loadChannel
(
String
mainPath
,
TestPlatform
platform
)
{
// The engine could connect to us, in which case webSocket.future will complete.
return
StreamChannelCompleter
.
fromFuture
(
_startTest
(
mainPath
));
// The local test harness could get bored of us.
_InitialResult
initialResult
=
await
Future
.
any
(<
Future
<
_InitialResult
>>[
process
.
exitCode
.
then
<
_InitialResult
>((
int
exitCode
)
{
return
_InitialResult
.
crashed
;
}),
new
Future
<
_InitialResult
>.
delayed
(
_kTestStartupTimeout
,
()
{
return
_InitialResult
.
timedOut
;
}),
webSocket
.
future
.
then
<
_InitialResult
>((
WebSocket
webSocket
)
{
return
_InitialResult
.
connected
;
}),
]);
switch
(
initialResult
)
{
case
_InitialResult
.
crashed
:
int
exitCode
=
await
process
.
exitCode
;
String
message
=
_getErrorMessage
(
_getExitCodeMessage
(
exitCode
,
'before connecting to test harness'
),
testPath
,
shellPath
);
controller
.
sink
.
addError
(
new
Exception
(
message
));
controller
.
sink
.
close
();
await
controller
.
sink
.
done
;
break
;
case
_InitialResult
.
timedOut
:
String
message
=
_getErrorMessage
(
'Test never connected to test harness.'
,
testPath
,
shellPath
);
controller
.
sink
.
addError
(
new
Exception
(
message
));
controller
.
sink
.
close
();
await
controller
.
sink
.
done
;
break
;
case
_InitialResult
.
connected
:
WebSocket
testSocket
=
await
webSocket
.
future
;
Completer
<
Null
>
harnessDone
=
new
Completer
<
Null
>();
StreamSubscription
<
dynamic
>
harnessToTest
=
controller
.
stream
.
listen
(
(
dynamic
event
)
{
testSocket
.
add
(
JSON
.
encode
(
event
));
},
onDone:
()
{
harnessDone
.
complete
();
},
);
Completer
<
Null
>
testDone
=
new
Completer
<
Null
>();
StreamSubscription
<
dynamic
>
testToHarness
=
testSocket
.
listen
(
(
dynamic
event
)
{
assert
(
event
is
String
);
// we shouldn't ever get binary messages
controller
.
sink
.
add
(
JSON
.
decode
(
event
));
},
onDone:
()
{
testDone
.
complete
();
},
);
_TestResult
testResult
=
await
Future
.
any
(<
Future
<
_TestResult
>>[
process
.
exitCode
.
then
<
_TestResult
>((
int
exitCode
)
{
return
_TestResult
.
crashed
;
}),
testDone
.
future
.
then
<
_TestResult
>((
Null
_
)
{
return
_TestResult
.
completed
;
}),
harnessDone
.
future
.
then
<
_TestResult
>((
Null
_
)
{
return
_TestResult
.
harnessBailed
;
}),
]);
harnessToTest
.
cancel
();
testToHarness
.
cancel
();
assert
(!
controllerSinkClosed
);
switch
(
testResult
)
{
case
_TestResult
.
crashed
:
int
exitCode
=
await
process
.
exitCode
;
subprocessActive
=
false
;
String
message
=
_getErrorMessage
(
_getExitCodeMessage
(
exitCode
,
'before test harness closed its WebSocket'
),
testPath
,
shellPath
);
controller
.
sink
.
addError
(
new
Exception
(
message
));
controller
.
sink
.
close
();
await
controller
.
sink
.
done
;
break
;
case
_TestResult
.
completed
:
break
;
case
_TestResult
.
harnessBailed
:
break
;
}
break
;
}
CoverageCollector
.
instance
.
collectCoverage
(
host:
_kHost
.
address
,
port:
observatoryPort
,
processToKill:
process
,
// This kills the subprocess whether coverage is enabled or not.
);
subprocessActive
=
false
;
}
catch
(
e
,
stack
)
{
if
(!
controllerSinkClosed
)
{
controller
.
sink
.
addError
(
e
,
stack
);
}
else
{
printError
(
'unhandled error during test:
\n
$e
\n
$stack
'
);
}
}
finally
{
for
(
_Finalizer
finalizer
in
finalizers
)
await
finalizer
();
if
(!
controllerSinkClosed
)
{
controller
.
sink
.
close
();
await
controller
.
sink
.
done
;
}
}
assert
(!
subprocessActive
);
assert
(
controllerSinkClosed
);
printTrace
(
'ending test:
$testPath
'
);
}
}
Future
<
StreamChannel
<
dynamic
>>
_startTest
(
String
mainPath
)
async
{
String
_generateTestMain
({
_ServerInfo
info
=
await
_startServer
();
String
testUrl
,
Directory
tempDir
=
Directory
.
systemTemp
.
createTempSync
(
String
encodedWebsocketUrl
,
'dart_test_listener'
);
})
{
File
listenerFile
=
new
File
(
'
${tempDir.path}
/listener.dart'
);
return
'''
listenerFile
.
createSync
();
listenerFile
.
writeAsStringSync
(
'''
import '
dart:
convert
';
import '
dart:
convert
';
import '
dart:
io
';
import '
dart:
io
';
...
@@ -134,10 +218,10 @@ import 'package:stream_channel/stream_channel.dart';
...
@@ -134,10 +218,10 @@ import 'package:stream_channel/stream_channel.dart';
import '
package:
test
/
src
/
runner
/
plugin
/
remote_platform_helpers
.
dart
';
import '
package:
test
/
src
/
runner
/
plugin
/
remote_platform_helpers
.
dart
';
import '
package:
test
/
src
/
runner
/
vm
/
catch_isolate_errors
.
dart
';
import '
package:
test
/
src
/
runner
/
vm
/
catch_isolate_errors
.
dart
';
import '
$
{
path
.
toUri
(
path
.
absolute
(
mainPath
))}
' as test;
import '
$
testUrl
' as test;
void main() {
void main() {
String server = Uri.decodeComponent('
$
{
Uri
.
encodeComponent
(
info
.
url
)}
');
String server = Uri.decodeComponent('
$
encodedWebsocketUrl
');
StreamChannel channel = serializeSuite(() {
StreamChannel channel = serializeSuite(() {
catchIsolateErrors();
catchIsolateErrors();
return test.main;
return test.main;
...
@@ -147,65 +231,82 @@ void main() {
...
@@ -147,65 +231,82 @@ void main() {
socket.addStream(channel.stream.map(JSON.encode));
socket.addStream(channel.stream.map(JSON.encode));
});
});
}
}
'''
);
'''
;
}
File
_cachedFontConfig
;
/// Returns a Fontconfig config file that limits font fallback to the
/// artifact cache directory.
File
get
_fontConfigFile
{
if
(
_cachedFontConfig
!=
null
)
return
_cachedFontConfig
;
int
observatoryPort
;
StringBuffer
sb
=
new
StringBuffer
();
if
(
CoverageCollector
.
instance
.
enabled
)
{
sb
.
writeln
(
'<fontconfig>'
);
observatoryPort
=
CoverageCollector
.
instance
.
observatoryPort
??
new
math
.
Random
().
nextInt
(
30000
)
+
2000
;
sb
.
writeln
(
' <dir>
${cache.getCacheArtifacts().path}
</dir>'
);
await
CoverageCollector
.
instance
.
finishPendingJobs
();
sb
.
writeln
(
' <cachedir>/var/cache/fontconfig</cachedir>'
);
sb
.
writeln
(
'</fontconfig>'
);
Directory
fontsDir
=
Directory
.
systemTemp
.
createTempSync
(
'flutter_fonts'
);
_cachedFontConfig
=
new
File
(
'
${fontsDir.path}
/fonts.conf'
);
_cachedFontConfig
.
createSync
();
_cachedFontConfig
.
writeAsStringSync
(
sb
.
toString
());
return
_cachedFontConfig
;
}
Future
<
Process
>
_startProcess
(
String
executable
,
String
testPath
,
{
String
packages
,
int
observatoryPort
})
{
assert
(
executable
!=
null
);
// Please provide the path to the shell in the SKY_SHELL environment variable.
List
<
String
>
arguments
=
<
String
>[];
if
(
observatoryPort
!=
null
)
{
arguments
.
add
(
'--observatory-port=
$observatoryPort
'
);
}
else
{
arguments
.
add
(
'--disable-observatory'
);
}
}
arguments
.
addAll
(<
String
>[
'--enable-dart-profiling'
,
'--non-interactive'
,
'--enable-checked-mode'
,
'--packages=
$packages
'
,
testPath
,
]);
printTrace
(
'
$executable
${arguments.join(' ')}
'
);
Map
<
String
,
String
>
environment
=
<
String
,
String
>{
'FLUTTER_TEST'
:
'true'
,
'FONTCONFIG_FILE'
:
_fontConfigFile
.
path
,
};
return
processManager
.
start
(
executable
,
arguments
,
environment:
environment
);
}
Process
process
=
await
_startProcess
(
void
_pipeStandardStreamsToConsole
(
Process
process
)
{
listenerFile
.
path
,
for
(
Stream
<
List
<
int
>>
stream
in
packages:
PackageMap
.
globalPackagesPath
,
<
Stream
<
List
<
int
>>>[
process
.
stderr
,
process
.
stdout
])
{
observatoryPort:
observatoryPort
stream
.
transform
(
UTF8
.
decoder
)
);
.
transform
(
const
LineSplitter
())
.
listen
((
String
line
)
{
_attachStandardStreams
(
process
);
if
(
line
!=
null
)
printStatus
(
'Shell:
$line
'
);
void
finalize
()
{
});
if
(
process
!=
null
)
{
Process
processToKill
=
process
;
process
=
null
;
CoverageCollector
.
instance
.
collectCoverage
(
host:
_kHost
.
address
,
port:
observatoryPort
,
processToKill:
processToKill
);
}
if
(
tempDir
!=
null
)
{
Directory
dirToDelete
=
tempDir
;
tempDir
=
null
;
dirToDelete
.
deleteSync
(
recursive:
true
);
}
}
}
}
process
.
exitCode
.
then
((
_
)
{
String
_getErrorMessage
(
String
what
,
String
testPath
,
String
shellPath
)
{
WebSocket
.
connect
(
info
.
shutdownUrl
)
;
return
'
$what
\n
Test:
$testPath
\n
Shell:
$shellPath
\n\n
'
;
});
}
try
{
String
_getExitCodeMessage
(
int
exitCode
,
String
when
)
{
WebSocket
socket
=
await
info
.
socket
;
switch
(
exitCode
)
{
StreamChannel
<
dynamic
>
channel
=
new
StreamChannel
<
dynamic
>(
socket
.
map
(
JSON
.
decode
),
socket
);
case
0
:
return
channel
.
transformStream
(
return
'Shell subprocess ended cleanly
$when
. Did main() call exit()?'
;
new
StreamTransformer
<
dynamic
,
dynamic
>.
fromHandlers
(
case
-
0x0f
:
// ProcessSignal.SIGTERM
handleDone:
(
EventSink
<
dynamic
>
sink
)
{
return
'Shell subprocess crashed with SIGTERM (
$exitCode
)
$when
.'
;
finalize
();
case
-
0x0b
:
// ProcessSignal.SIGSEGV
sink
.
close
();
return
'Shell subprocess crashed with segmentation fault
$when
.'
;
}
case
-
0x06
:
// ProcessSignal.SIGABRT
)
return
'Shell subprocess crashed with SIGABRT (
$exitCode
)
$when
.'
;
).
transformSink
(
new
StreamSinkTransformer
<
dynamic
,
String
>.
fromHandlers
(
default
:
handleData:
(
dynamic
data
,
StreamSink
<
String
>
sink
)
{
return
'Shell subprocess crashed with unexpected exit code
$exitCode
$when
.'
;
sink
.
add
(
JSON
.
encode
(
data
));
},
handleDone:
(
EventSink
<
String
>
sink
)
{
finalize
();
sink
.
close
();
}
));
}
catch
(
e
)
{
finalize
();
rethrow
;
}
}
}
}
}
}
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