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
Show 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
];
enum
_InitialResult
{
crashed
,
timedOut
,
connected
}
enum
_TestResult
{
crashed
,
harnessBailed
,
completed
}
typedef
Future
<
Null
>
_Finalizer
();
void
installHook
(
)
{
class
FlutterPlatform
extends
PlatformPlugin
{
hack
.
registerPlatformPlugin
(<
TestPlatform
>[
TestPlatform
.
vm
],
()
=>
new
FlutterPlatform
());
FlutterPlatform
({
this
.
shellPath
})
{
}
assert
(
shellPath
!=
null
);
}
class
_ServerInfo
{
final
String
shellPath
;
final
String
url
;
final
String
shutdownUrl
;
final
Future
<
WebSocket
>
socket
;
final
HttpServer
server
;
_ServerInfo
(
this
.
server
,
this
.
url
,
this
.
shutdownUrl
,
this
.
socket
);
// Each time loadChannel() is called, we spin up a local WebSocket server,
}
// then spin up the engine in a subprocess. We pass the engine a Dart file
// that connects to our WebSocket server, then we proxy JSON messages from
// the test harness to the engine and back again. If at any time the engine
// crashes, we inject an error into that stream. When the process closes,
// we clean everything up.
@override
StreamChannel
<
dynamic
>
loadChannel
(
String
testPath
,
TestPlatform
platform
)
{
final
StreamChannelController
<
dynamic
>
controller
=
new
StreamChannelController
<
dynamic
>(
allowForeignErrors:
false
);
_startTest
(
testPath
,
controller
.
local
);
return
controller
.
foreign
;
}
Future
<
Null
>
_startTest
(
String
testPath
,
StreamChannel
<
dynamic
>
controller
)
async
{
printTrace
(
'starting test:
$testPath
'
);
Future
<
_ServerInfo
>
_startServer
()
async
{
final
List
<
_Finalizer
>
finalizers
=
<
_Finalizer
>[];
bool
subprocessActive
=
false
;
bool
controllerSinkClosed
=
false
;
try
{
controller
.
sink
.
done
.
then
((
_
)
{
controllerSinkClosed
=
true
;
});
// Prepare our WebSocket server to talk to the engine subproces.
HttpServer
server
=
await
HttpServer
.
bind
(
_kHost
,
0
);
HttpServer
server
=
await
HttpServer
.
bind
(
_kHost
,
0
);
Completer
<
WebSocket
>
socket
=
new
Completer
<
WebSocket
>();
finalizers
.
add
(()
async
{
await
server
.
close
(
force:
true
);
});
Completer
<
WebSocket
>
webSocket
=
new
Completer
<
WebSocket
>();
server
.
listen
((
HttpRequest
request
)
{
server
.
listen
((
HttpRequest
request
)
{
if
(
request
.
uri
.
path
==
_kRunnerPath
)
webSocket
.
complete
(
WebSocketTransformer
.
upgrade
(
request
));
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
})
{
// Prepare a temporary directory to store the Dart file that will talk to us.
assert
(
shellPath
!=
null
||
_kSkyShell
!=
null
);
// Please provide the path to the shell in the SKY_SHELL environment variable.
Directory
temporaryDirectory
=
Directory
.
systemTemp
.
createTempSync
(
'dart_test_listener'
);
String
executable
=
shellPath
??
_kSkyShell
;
finalizers
.
add
(()
async
{
temporaryDirectory
.
deleteSync
(
recursive:
true
);
});
List
<
String
>
arguments
=
<
String
>[];
if
(
observatoryPort
!=
null
)
{
// Prepare the Dart file that will talk to us and start the test.
arguments
.
add
(
'--observatory-port=
$observatoryPort
'
);
File
listenerFile
=
new
File
(
'
${temporaryDirectory.path}
/listener.dart'
);
}
else
{
listenerFile
.
createSync
();
arguments
.
add
(
'--disable-observatory'
);
listenerFile
.
writeAsStringSync
(
_generateTestMain
(
testUrl:
path
.
toUri
(
path
.
absolute
(
testPath
)).
toString
(),
encodedWebsocketUrl:
Uri
.
encodeComponent
(
"ws://
${_kHost.address}
:
${server.port}
"
),
));
// 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
();
}
}
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
)
{
// Start the engine subprocess.
for
(
Stream
<
List
<
int
>>
stream
in
Process
process
=
await
_startProcess
(
<
Stream
<
List
<
int
>>>[
process
.
stderr
,
process
.
stdout
])
{
shellPath
,
stream
.
transform
(
UTF8
.
decoder
)
listenerFile
.
path
,
.
transform
(
const
LineSplitter
())
packages:
PackageMap
.
globalPackagesPath
,
.
listen
((
String
line
)
{
observatoryPort:
observatoryPort
,
if
(
line
!=
null
)
);
print
(
'Shell:
$line
'
);
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
));
}
}
}
});
File
_cachedFontConfig
;
// Pipe stdout and stderr from the subprocess to our printStatus console.
_pipeStandardStreamsToConsole
(
process
);
/// Returns a Fontconfig config file that limits font fallback to directories
// At this point, three things can happen next:
/// specified in [fontDirectories]
.
// The engine could crash, in which case process.exitCode will complete
.
File
get
_fontConfigFile
{
// The engine could connect to us, in which case webSocket.future will complete.
if
(
_cachedFontConfig
!=
null
)
return
_cachedFontConfig
;
// The local test harness could get bored of us.
Directory
fontsDir
=
Directory
.
systemTemp
.
createTempSync
(
'flutter_fonts'
);
_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
;
}),
]);
StringBuffer
sb
=
new
StringBuffer
();
switch
(
initialResult
)
{
sb
.
writeln
(
'<fontconfig>'
);
case
_InitialResult
.
crashed
:
for
(
String
fontDir
in
fontDirectories
)
{
int
exitCode
=
await
process
.
exitCode
;
sb
.
writeln
(
' <dir>
$fontDir
</dir>'
);
String
message
=
_getErrorMessage
(
_getExitCodeMessage
(
exitCode
,
'before connecting to test harness'
),
testPath
,
shellPath
);
}
controller
.
sink
.
addError
(
new
Exception
(
message
));
sb
.
writeln
(
' <cachedir>/var/cache/fontconfig</cachedir>'
);
controller
.
sink
.
close
();
sb
.
writeln
(
'</fontconfig>'
);
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
;
_cachedFontConfig
=
new
File
(
'
${fontsDir.path}
/fonts.conf'
);
Completer
<
Null
>
harnessDone
=
new
Completer
<
Null
>(
);
_cachedFontConfig
.
createSync
();
StreamSubscription
<
dynamic
>
harnessToTest
=
controller
.
stream
.
listen
(
_cachedFontConfig
.
writeAsStringSync
(
sb
.
toString
());
(
dynamic
event
)
{
testSocket
.
add
(
JSON
.
encode
(
event
));
},
return
_cachedFontConfig
;
onDone:
()
{
harnessDone
.
complete
();
},
}
);
class
FlutterPlatform
extends
PlatformPlugin
{
Completer
<
Null
>
testDone
=
new
Completer
<
Null
>();
@override
StreamSubscription
<
dynamic
>
testToHarness
=
testSocket
.
listen
(
StreamChannel
<
dynamic
>
loadChannel
(
String
mainPath
,
TestPlatform
platform
)
{
(
dynamic
event
)
{
return
StreamChannelCompleter
.
fromFuture
(
_startTest
(
mainPath
));
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
;
}
}
Future
<
StreamChannel
<
dynamic
>>
_startTest
(
String
mainPath
)
async
{
CoverageCollector
.
instance
.
collectCoverage
(
_ServerInfo
info
=
await
_startServer
();
host:
_kHost
.
address
,
Directory
tempDir
=
Directory
.
systemTemp
.
createTempSync
(
port:
observatoryPort
,
'dart_test_listener'
);
processToKill:
process
,
// This kills the subprocess whether coverage is enabled or not.
File
listenerFile
=
new
File
(
'
${tempDir.path}
/listener.dart'
);
);
listenerFile
.
createSync
();
subprocessActive
=
false
;
listenerFile
.
writeAsStringSync
(
'''
}
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
'
);
}
String
_generateTestMain
({
String
testUrl
,
String
encodedWebsocketUrl
,
})
{
return
'''
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));
});
});
}
}
'''
);
'''
;
int
observatoryPort
;
if
(
CoverageCollector
.
instance
.
enabled
)
{
observatoryPort
=
CoverageCollector
.
instance
.
observatoryPort
??
new
math
.
Random
().
nextInt
(
30000
)
+
2000
;
await
CoverageCollector
.
instance
.
finishPendingJobs
();
}
}
Process
process
=
await
_startProcess
(
File
_cachedFontConfig
;
listenerFile
.
path
,
packages:
PackageMap
.
globalPackagesPath
,
observatoryPort:
observatoryPort
);
_attachStandardStreams
(
process
);
/// Returns a Fontconfig config file that limits font fallback to the
/// artifact cache directory.
File
get
_fontConfigFile
{
if
(
_cachedFontConfig
!=
null
)
return
_cachedFontConfig
;
void
finalize
()
{
StringBuffer
sb
=
new
StringBuffer
();
if
(
process
!=
null
)
{
sb
.
writeln
(
'<fontconfig>'
);
Process
processToKill
=
process
;
sb
.
writeln
(
' <dir>
${cache.getCacheArtifacts().path}
</dir>'
);
process
=
null
;
sb
.
writeln
(
' <cachedir>/var/cache/fontconfig</cachedir>'
);
CoverageCollector
.
instance
.
collectCoverage
(
sb
.
writeln
(
'</fontconfig>'
);
host:
_kHost
.
address
,
port:
observatoryPort
,
Directory
fontsDir
=
Directory
.
systemTemp
.
createTempSync
(
'flutter_fonts'
);
processToKill:
processToKill
_cachedFontConfig
=
new
File
(
'
${fontsDir.path}
/fonts.conf'
);
);
_cachedFontConfig
.
createSync
();
_cachedFontConfig
.
writeAsStringSync
(
sb
.
toString
());
return
_cachedFontConfig
;
}
}
if
(
tempDir
!=
null
)
{
Directory
dirToDelete
=
tempDir
;
tempDir
=
null
;
Future
<
Process
>
_startProcess
(
String
executable
,
String
testPath
,
{
String
packages
,
int
observatoryPort
})
{
dirToDelete
.
deleteSync
(
recursive:
true
);
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
.
exitCode
.
then
((
_
)
{
void
_pipeStandardStreamsToConsole
(
Process
process
)
{
WebSocket
.
connect
(
info
.
shutdownUrl
);
for
(
Stream
<
List
<
int
>>
stream
in
<
Stream
<
List
<
int
>>>[
process
.
stderr
,
process
.
stdout
])
{
stream
.
transform
(
UTF8
.
decoder
)
.
transform
(
const
LineSplitter
())
.
listen
((
String
line
)
{
if
(
line
!=
null
)
printStatus
(
'Shell:
$line
'
);
});
});
}
}
try
{
String
_getErrorMessage
(
String
what
,
String
testPath
,
String
shellPath
)
{
WebSocket
socket
=
await
info
.
socket
;
return
'
$what
\n
Test:
$testPath
\n
Shell:
$shellPath
\n\n
'
;
StreamChannel
<
dynamic
>
channel
=
new
StreamChannel
<
dynamic
>(
socket
.
map
(
JSON
.
decode
),
socket
);
return
channel
.
transformStream
(
new
StreamTransformer
<
dynamic
,
dynamic
>.
fromHandlers
(
handleDone:
(
EventSink
<
dynamic
>
sink
)
{
finalize
();
sink
.
close
();
}
)
).
transformSink
(
new
StreamSinkTransformer
<
dynamic
,
String
>.
fromHandlers
(
handleData:
(
dynamic
data
,
StreamSink
<
String
>
sink
)
{
sink
.
add
(
JSON
.
encode
(
data
));
},
handleDone:
(
EventSink
<
String
>
sink
)
{
finalize
();
sink
.
close
();
}
}
));
}
catch
(
e
)
{
String
_getExitCodeMessage
(
int
exitCode
,
String
when
)
{
finalize
();
switch
(
exitCode
)
{
rethrow
;
case
0
:
return
'Shell subprocess ended cleanly
$when
. Did main() call exit()?'
;
case
-
0x0f
:
// ProcessSignal.SIGTERM
return
'Shell subprocess crashed with SIGTERM (
$exitCode
)
$when
.'
;
case
-
0x0b
:
// ProcessSignal.SIGSEGV
return
'Shell subprocess crashed with segmentation fault
$when
.'
;
case
-
0x06
:
// ProcessSignal.SIGABRT
return
'Shell subprocess crashed with SIGABRT (
$exitCode
)
$when
.'
;
default
:
return
'Shell subprocess crashed with unexpected exit code
$exitCode
$when
.'
;
}
}
}
}
}
}
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