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
33ad5bac
Unverified
Commit
33ad5bac
authored
Jun 26, 2019
by
Jonah Williams
Committed by
GitHub
Jun 26, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Attempt to enable tool coverage redux (#35074)
parent
206d43de
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
273 additions
and
136 deletions
+273
-136
.cirrus.yml
.cirrus.yml
+13
-0
test.dart
dev/bots/test.dart
+54
-0
coverage_collector.dart
packages/flutter_tools/lib/src/test/coverage_collector.dart
+26
-1
create_test.dart
packages/flutter_tools/test/commands/create_test.dart
+2
-1
daemon_mode_test.dart
...ages/flutter_tools/test/integration/daemon_mode_test.dart
+5
-0
debugger_stepping_test.dart
...lutter_tools/test/integration/debugger_stepping_test.dart
+5
-0
expression_evaluation_test.dart
...er_tools/test/integration/expression_evaluation_test.dart
+5
-0
flutter_attach_test.dart
...s/flutter_tools/test/integration/flutter_attach_test.dart
+5
-0
flutter_run_test.dart
...ages/flutter_tools/test/integration/flutter_run_test.dart
+5
-0
hot_reload_test.dart
packages/flutter_tools/test/integration/hot_reload_test.dart
+5
-0
lifetime_test.dart
packages/flutter_tools/test/integration/lifetime_test.dart
+5
-0
common.dart
packages/flutter_tools/test/src/common.dart
+1
-1
tool_coverage.dart
packages/flutter_tools/tool/tool_coverage.dart
+142
-133
No files found.
.cirrus.yml
View file @
33ad5bac
...
@@ -120,6 +120,19 @@ task:
...
@@ -120,6 +120,19 @@ task:
container
:
container
:
cpu
:
4
cpu
:
4
memory
:
12G
memory
:
12G
-
name
:
tool_coverage-linux
# Only re-run tool coverage if the package itself changed.
skip
:
"
!changesInclude('./packages/flutter_tools/**/*',
'.cirrus.yml',
'./dev/bots/test.dart')"
env
:
GCLOUD_SERVICE_ACCOUNT_KEY
:
ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
CODECOV_TOKEN
:
ENCRYPTED[7c76a7f8c9264f3b7f3fd63fcf186f93c62c4dfe43ec288861c2f506d456681032b89efe7b7a139c82156350ca2c752c]
SHARD
:
tool_coverage
test_script
:
-
dart --enable-asserts ./dev/bots/test.dart
-
bash <(curl -s https://codecov.io/bash) -c -s ./packages/flutter_tools/coverage/ -f '*.lcov.info' -F flutter_tool
container
:
cpu
:
8
memory
:
24G
-
name
:
web_tests-linux
-
name
:
web_tests-linux
# TODO(jonahwilliams): re-enabled once we've determined causes for flakiness
# TODO(jonahwilliams): re-enabled once we've determined causes for flakiness
allow_failures
:
true
allow_failures
:
true
...
...
dev/bots/test.dart
View file @
33ad5bac
...
@@ -20,6 +20,7 @@ final String flutter = path.join(flutterRoot, 'bin', Platform.isWindows ? 'flutt
...
@@ -20,6 +20,7 @@ final String flutter = path.join(flutterRoot, 'bin', Platform.isWindows ? 'flutt
final
String
dart
=
path
.
join
(
flutterRoot
,
'bin'
,
'cache'
,
'dart-sdk'
,
'bin'
,
Platform
.
isWindows
?
'dart.exe'
:
'dart'
);
final
String
dart
=
path
.
join
(
flutterRoot
,
'bin'
,
'cache'
,
'dart-sdk'
,
'bin'
,
Platform
.
isWindows
?
'dart.exe'
:
'dart'
);
final
String
pub
=
path
.
join
(
flutterRoot
,
'bin'
,
'cache'
,
'dart-sdk'
,
'bin'
,
Platform
.
isWindows
?
'pub.bat'
:
'pub'
);
final
String
pub
=
path
.
join
(
flutterRoot
,
'bin'
,
'cache'
,
'dart-sdk'
,
'bin'
,
Platform
.
isWindows
?
'pub.bat'
:
'pub'
);
final
String
pubCache
=
path
.
join
(
flutterRoot
,
'.pub-cache'
);
final
String
pubCache
=
path
.
join
(
flutterRoot
,
'.pub-cache'
);
final
String
toolRoot
=
path
.
join
(
flutterRoot
,
'packages'
,
'flutter_tools'
);
final
List
<
String
>
flutterTestArgs
=
<
String
>[];
final
List
<
String
>
flutterTestArgs
=
<
String
>[];
final
bool
useFlutterTestFormatter
=
Platform
.
environment
[
'FLUTTER_TEST_FORMATTER'
]
==
'true'
;
final
bool
useFlutterTestFormatter
=
Platform
.
environment
[
'FLUTTER_TEST_FORMATTER'
]
==
'true'
;
...
@@ -30,6 +31,7 @@ const Map<String, ShardRunner> _kShards = <String, ShardRunner>{
...
@@ -30,6 +31,7 @@ const Map<String, ShardRunner> _kShards = <String, ShardRunner>{
'tests'
:
_runTests
,
'tests'
:
_runTests
,
'web_tests'
:
_runWebTests
,
'web_tests'
:
_runWebTests
,
'tool_tests'
:
_runToolTests
,
'tool_tests'
:
_runToolTests
,
'tool_coverage'
:
_runToolCoverage
,
'build_tests'
:
_runBuildTests
,
'build_tests'
:
_runBuildTests
,
'coverage'
:
_runCoverage
,
'coverage'
:
_runCoverage
,
'integration_tests'
:
_runIntegrationTests
,
'integration_tests'
:
_runIntegrationTests
,
...
@@ -176,6 +178,58 @@ Future<bq.BigqueryApi> _getBigqueryApi() async {
...
@@ -176,6 +178,58 @@ Future<bq.BigqueryApi> _getBigqueryApi() async {
}
}
}
}
// Partition tool tests into two groups, see explanation on `_runToolCoverage`.
List
<
List
<
String
>>
_partitionToolTests
()
{
final
List
<
String
>
pending
=
<
String
>[];
final
String
toolTestDir
=
path
.
join
(
toolRoot
,
'test'
);
for
(
FileSystemEntity
entity
in
Directory
(
toolTestDir
).
listSync
(
recursive:
true
))
{
if
(
entity
is
File
&&
entity
.
path
.
endsWith
(
'_test.dart'
))
{
final
String
relativePath
=
path
.
relative
(
entity
.
path
,
from:
toolRoot
);
pending
.
add
(
relativePath
);
}
}
// Shuffle the tests to avoid giving an expensive test directory like
// integration to a single run of tests.
pending
..
shuffle
();
final
int
aboutHalf
=
pending
.
length
~/
2
;
final
List
<
String
>
groupA
=
pending
.
take
(
aboutHalf
).
toList
();
final
List
<
String
>
groupB
=
pending
.
skip
(
aboutHalf
).
toList
();
return
<
List
<
String
>>[
groupA
,
groupB
];
}
// Tools tests run with coverage enabled have much higher memory usage than
// our current CI infrastructure can support. We partition the tests into
// two sets and run them in separate invocations of dart to reduce peak memory
// usage. codecov.io automatically handles merging different coverage files
// together, so producing separate files is OK.
//
// See: https://github.com/flutter/flutter/issues/35025
Future
<
void
>
_runToolCoverage
()
async
{
final
List
<
List
<
String
>>
tests
=
_partitionToolTests
();
// Precompile tests to speed up subsequent runs.
await
runCommand
(
pub
,
<
String
>[
'run'
,
'build_runner'
,
'build'
],
workingDirectory:
toolRoot
,
);
// The name of this subshard has to match the --file path provided at
// the end of this test script in `.cirrus.yml`.
const
List
<
String
>
subshards
=
<
String
>[
'A'
,
'B'
];
for
(
int
i
=
0
;
i
<
tests
.
length
;
i
++)
{
final
List
<
String
>
testGroup
=
tests
[
i
];
await
runCommand
(
dart
,
<
String
>[
path
.
join
(
'tool'
,
'tool_coverage.dart'
),
'--'
]..
addAll
(
testGroup
),
workingDirectory:
toolRoot
,
environment:
<
String
,
String
>{
'FLUTTER_ROOT'
:
flutterRoot
,
'SUBSHARD'
:
subshards
[
i
],
}
);
}
}
Future
<
void
>
_runToolTests
()
async
{
Future
<
void
>
_runToolTests
()
async
{
final
bq
.
BigqueryApi
bigqueryApi
=
await
_getBigqueryApi
();
final
bq
.
BigqueryApi
bigqueryApi
=
await
_getBigqueryApi
();
await
_runSmokeTests
();
await
_runSmokeTests
();
...
...
packages/flutter_tools/lib/src/test/coverage_collector.dart
View file @
33ad5bac
...
@@ -41,6 +41,32 @@ class CoverageCollector extends TestWatcher {
...
@@ -41,6 +41,32 @@ class CoverageCollector extends TestWatcher {
}
}
}
}
/// Collects coverage for an isolate using the given `port`.
///
/// This should be called when the code whose coverage data is being collected
/// has been run to completion so that all coverage data has been recorded.
///
/// The returned [Future] completes when the coverage is collected.
Future
<
void
>
collectCoverageIsolate
(
Uri
observatoryUri
)
async
{
assert
(
observatoryUri
!=
null
);
printTrace
(
'collecting coverage data from
$observatoryUri
...'
);
final
Map
<
String
,
dynamic
>
data
=
await
collect
(
observatoryUri
,
(
String
libraryName
)
{
// If we have a specified coverage directory or could not find the package name, then
// accept all libraries.
return
(
coverageDirectory
!=
null
)
||
(
flutterProject
==
null
)
||
libraryName
.
contains
(
flutterProject
.
manifest
.
appName
);
});
if
(
data
==
null
)
{
throw
Exception
(
'Failed to collect coverage.'
);
}
assert
(
data
!=
null
);
print
(
'(
$observatoryUri
): collected coverage data; merging...'
);
_addHitmap
(
coverage
.
createHitmap
(
data
[
'coverage'
]));
print
(
'(
$observatoryUri
): done merging coverage data into global coverage map.'
);
}
/// Collects coverage for the given [Process] using the given `port`.
/// Collects coverage for the given [Process] using the given `port`.
///
///
/// This should be called when the code whose coverage data is being collected
/// This should be called when the code whose coverage data is being collected
...
@@ -91,7 +117,6 @@ class CoverageCollector extends TestWatcher {
...
@@ -91,7 +117,6 @@ class CoverageCollector extends TestWatcher {
coverage
.
Formatter
formatter
,
coverage
.
Formatter
formatter
,
Directory
coverageDirectory
,
Directory
coverageDirectory
,
})
async
{
})
async
{
printTrace
(
'formating coverage data'
);
if
(
_globalHitmap
==
null
)
{
if
(
_globalHitmap
==
null
)
{
return
null
;
return
null
;
}
}
...
...
packages/flutter_tools/test/commands/create_test.dart
View file @
33ad5bac
...
@@ -2,7 +2,8 @@
...
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
@Tags
(<
String
>[
'create'
])
// This test performs too poorly to run with coverage enabled.
@Tags
(<
String
>[
'create'
,
'no_coverage'
])
import
'dart:async'
;
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:convert'
;
import
'dart:typed_data'
;
import
'dart:typed_data'
;
...
...
packages/flutter_tools/test/integration/daemon_mode_test.dart
View file @
33ad5bac
...
@@ -2,6 +2,11 @@
...
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags
(<
String
>[
'no_coverage'
])
import
'dart:async'
;
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:convert'
;
...
...
packages/flutter_tools/test/integration/debugger_stepping_test.dart
View file @
33ad5bac
...
@@ -2,6 +2,11 @@
...
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags
(<
String
>[
'no_coverage'
])
import
'package:file/file.dart'
;
import
'package:file/file.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
...
...
packages/flutter_tools/test/integration/expression_evaluation_test.dart
View file @
33ad5bac
...
@@ -2,6 +2,11 @@
...
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags
(<
String
>[
'no_coverage'
])
import
'dart:async'
;
import
'dart:async'
;
import
'package:file/file.dart'
;
import
'package:file/file.dart'
;
...
...
packages/flutter_tools/test/integration/flutter_attach_test.dart
View file @
33ad5bac
...
@@ -2,6 +2,11 @@
...
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags
(<
String
>[
'no_coverage'
])
import
'package:file/file.dart'
;
import
'package:file/file.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
...
...
packages/flutter_tools/test/integration/flutter_run_test.dart
View file @
33ad5bac
...
@@ -2,6 +2,11 @@
...
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags
(<
String
>[
'no_coverage'
])
import
'package:file/file.dart'
;
import
'package:file/file.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
...
...
packages/flutter_tools/test/integration/hot_reload_test.dart
View file @
33ad5bac
...
@@ -2,6 +2,11 @@
...
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags
(<
String
>[
'no_coverage'
])
import
'dart:async'
;
import
'dart:async'
;
import
'package:file/file.dart'
;
import
'package:file/file.dart'
;
...
...
packages/flutter_tools/test/integration/lifetime_test.dart
View file @
33ad5bac
...
@@ -2,6 +2,11 @@
...
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
// Integration tests which invoke flutter instead of unit testing the code
// will not produce meaningful coverage information - we can measure coverage
// from the isolate running the test, but not from the isolate started via
// the command line process.
@Tags
(<
String
>[
'no_coverage'
])
import
'dart:async'
;
import
'dart:async'
;
import
'package:file/file.dart'
;
import
'package:file/file.dart'
;
...
...
packages/flutter_tools/test/src/common.dart
View file @
33ad5bac
...
@@ -18,7 +18,7 @@ import 'package:flutter_tools/src/commands/create.dart';
...
@@ -18,7 +18,7 @@ import 'package:flutter_tools/src/commands/create.dart';
import
'package:flutter_tools/src/runner/flutter_command.dart'
;
import
'package:flutter_tools/src/runner/flutter_command.dart'
;
import
'package:flutter_tools/src/runner/flutter_command_runner.dart'
;
import
'package:flutter_tools/src/runner/flutter_command_runner.dart'
;
export
'package:test_
api/test_api
.dart'
hide
TypeMatcher
,
isInstanceOf
;
// Defines a 'package:test' shim.
export
'package:test_
core/test_core
.dart'
hide
TypeMatcher
,
isInstanceOf
;
// Defines a 'package:test' shim.
/// Disable both web and desktop to make testing easier. For example, prevent
/// Disable both web and desktop to make testing easier. For example, prevent
/// them from showing up in the devices list if the host happens to be setup
/// them from showing up in the devices list if the host happens to be setup
...
...
packages/flutter_tools/tool/tool_coverage.dart
View file @
33ad5bac
...
@@ -3,165 +3,174 @@
...
@@ -3,165 +3,174 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:async'
;
import
'dart:
convert
'
;
import
'dart:
developer
'
;
import
'dart:io'
;
import
'dart:io'
;
import
'dart:isolate'
;
import
'package:args/args.dart'
;
import
'package:async/async.dart'
;
import
'package:coverage/coverage.dart'
;
import
'package:flutter_tools/src/context_runner.dart'
;
import
'package:flutter_tools/src/context_runner.dart'
;
import
'package:path/path.dart'
as
p
;
import
'package:pedantic/pedantic.dart'
;
import
'package:stream_channel/isolate_channel.dart'
;
import
'package:stream_channel/stream_channel.dart'
;
import
'package:test_core/src/runner/hack_register_platform.dart'
as
hack
;
// ignore: implementation_imports
import
'package:test_core/src/executable.dart'
as
test
;
// ignore: implementation_imports
import
'package:vm_service_client/vm_service_client.dart'
;
import
'package:test_api/src/backend/runtime.dart'
;
// ignore: implementation_imports
import
'package:test_api/src/backend/suite_platform.dart'
;
// ignore: implementation_imports
import
'package:test_core/src/runner/platform.dart'
;
// ignore: implementation_imports
import
'package:test_core/src/runner/runner_suite.dart'
;
// ignore: implementation_imports
import
'package:test_core/src/runner/suite.dart'
;
// ignore: implementation_imports
import
'package:test_core/src/runner/plugin/platform_helpers.dart'
;
// ignore: implementation_imports
import
'package:test_core/src/runner/environment.dart'
;
// ignore: implementation_imports
import
'package:flutter_tools/src/project.dart'
;
import
'package:flutter_tools/src/project.dart'
;
import
'package:flutter_tools/src/test/coverage_collector.dart'
;
import
'package:flutter_tools/src/test/coverage_collector.dart'
;
import
'package:pool/pool.dart'
;
import
'package:path/path.dart'
as
path
;
/// Generates an lcov report for the flutter tool unit tests.
final
ArgParser
argParser
=
ArgParser
()
..
addOption
(
'output-html'
,
defaultsTo:
'coverage/report.html'
,
help:
'The output path for the genhtml report.'
)
..
addOption
(
'output-lcov'
,
defaultsTo:
'coverage/lcov.info'
,
help:
'The output path for the lcov data.'
)
..
addOption
(
'test-directory'
,
defaultsTo:
'test/'
,
help:
'The path to the test directory.'
)
..
addOption
(
'packages'
,
defaultsTo:
'.packages'
,
help:
'The path to the .packages file.'
)
..
addOption
(
'genhtml'
,
defaultsTo:
'genhtml'
,
help:
'The genhtml executable.'
);
/// Generates an html coverage report for the flutter_tool.
///
///
/// Example invocation:
/// Example invocation:
///
///
/// dart tool/tool_coverage.dart
--packages=.packages --test-directory=test
/// dart tool/tool_coverage.dart
.
Future
<
void
>
main
(
List
<
String
>
arguments
)
async
{
Future
<
void
>
main
(
List
<
String
>
arguments
)
async
{
final
ArgResults
argResults
=
argParser
.
parse
(
arguments
);
return
runInContext
(()
async
{
await
runInContext
(()
async
{
final
VMPlatform
vmPlatform
=
VMPlatform
();
final
CoverageCollector
coverageCollector
=
CoverageCollector
(
hack
.
registerPlatformPlugin
(
flutterProject:
FlutterProject
.
current
(),
<
Runtime
>[
Runtime
.
vm
],
()
=>
vmPlatform
,
);
);
/// A temp directory to create synthetic test files in.
await
test
.
main
(<
String
>[
'-x'
,
'no_coverage'
,
'--no-color'
,
'-r'
,
'compact'
,
'-j'
,
'1'
,
...
arguments
]);
final
Directory
tempDirectory
=
Directory
.
systemTemp
.
createTempSync
(
'_flutter_coverage'
)
exit
(
exitCode
);
..
createSync
();
final
String
flutterRoot
=
File
(
Platform
.
script
.
toFilePath
()).
parent
.
parent
.
parent
.
parent
.
path
;
await
ToolCoverageRunner
(
tempDirectory
,
coverageCollector
,
flutterRoot
,
argResults
).
collectCoverage
();
});
});
}
}
class
ToolCoverageRunner
{
/// A platform that loads tests in isolates spawned within this Dart process.
ToolCoverageRunner
(
class
VMPlatform
extends
PlatformPlugin
{
this
.
tempDirectory
,
final
CoverageCollector
coverageCollector
=
CoverageCollector
(
this
.
coverageCollector
,
flutterProject:
FlutterProject
.
current
(),
this
.
flutterRoot
,
this
.
argResults
,
);
);
final
Map
<
String
,
Future
<
void
>>
_pending
=
<
String
,
Future
<
void
>>{};
final
String
precompiledPath
=
p
.
join
(
'.dart_tool'
,
'build'
,
'generated'
,
'flutter_tools'
);
final
ArgResults
argResults
;
@override
final
Pool
pool
=
Pool
(
Platform
.
numberOfProcessors
);
StreamChannel
<
void
>
loadChannel
(
String
path
,
SuitePlatform
platform
)
=>
final
Directory
tempDirectory
;
throw
UnimplementedError
();
final
CoverageCollector
coverageCollector
;
final
String
flutterRoot
;
Future
<
void
>
collectCoverage
()
async
{
final
List
<
Future
<
void
>>
pending
=
<
Future
<
void
>>[];
final
Directory
testDirectory
=
Directory
(
argResults
[
'test-directory'
]);
@override
final
List
<
FileSystemEntity
>
fileSystemEntities
=
testDirectory
.
listSync
(
recursive:
true
);
Future
<
RunnerSuite
>
load
(
String
path
,
SuitePlatform
platform
,
for
(
FileSystemEntity
fileSystemEntity
in
fileSystemEntities
)
{
SuiteConfiguration
suiteConfig
,
Object
message
)
async
{
if
(!
fileSystemEntity
.
path
.
endsWith
(
'_test.dart'
))
{
final
ReceivePort
receivePort
=
ReceivePort
();
continue
;
Isolate
isolate
;
}
try
{
pending
.
add
(
_runTest
(
fileSystemEntity
));
isolate
=
await
_spawnIsolate
(
path
,
receivePort
.
sendPort
);
}
catch
(
error
)
{
receivePort
.
close
();
rethrow
;
}
}
await
Future
.
wait
(
pending
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
// When this is completed we remove it from the map of pending so we can
// log the futures that get "stuck".
unawaited
(
completer
.
future
.
whenComplete
(()
{
_pending
.
remove
(
path
);
}));
final
ServiceProtocolInfo
info
=
await
Service
.
controlWebServer
(
enable:
true
);
final
dynamic
channel
=
IsolateChannel
<
Object
>.
connectReceive
(
receivePort
)
.
transformStream
(
StreamTransformer
<
Object
,
Object
>.
fromHandlers
(
handleDone:
(
EventSink
<
Object
>
sink
)
async
{
try
{
// this will throw if collection fails.
await
coverageCollector
.
collectCoverageIsolate
(
info
.
serverUri
);
}
finally
{
isolate
.
kill
(
priority:
Isolate
.
immediate
);
isolate
=
null
;
sink
.
close
();
completer
.
complete
();
}
},
handleError:
(
dynamic
error
,
StackTrace
stackTrace
,
EventSink
<
Object
>
sink
)
{
isolate
.
kill
(
priority:
Isolate
.
immediate
);
isolate
=
null
;
sink
.
close
();
completer
.
complete
();
}));
final
String
lcovData
=
await
coverageCollector
.
finalizeCoverage
();
VMEnvironment
environment
;
final
String
outputLcovPath
=
argResults
[
'output-lcov'
];
final
RunnerSuiteController
controller
=
deserializeSuite
(
final
String
outputHtmlPath
=
argResults
[
'output-html'
];
path
,
final
String
genHtmlExecutable
=
argResults
[
'genhtml'
];
platform
,
File
(
outputLcovPath
)
suiteConfig
,
..
createSync
(
recursive:
true
)
environment
,
..
writeAsStringSync
(
lcovData
);
channel
,
await
Process
.
run
(
genHtmlExecutable
,
<
String
>[
outputLcovPath
,
'-o'
,
outputHtmlPath
],
runInShell:
true
);
message
,
);
_pending
[
path
]
=
completer
.
future
;
return
await
controller
.
suite
;
}
}
// Creates a synthetic test file to wrap the test main in a group invocation.
/// Spawns an isolate and passes it [message].
// This will set up several fields used by the test methods on the context. Normally
///
// this would be handled automatically by the test runner, but since we're executing
/// This isolate connects an [IsolateChannel] to [message] and sends the
// the files directly with dart we need to handle it manually.
/// serialized tests over that channel.
String
_createTest
(
File
testFile
)
{
Future
<
Isolate
>
_spawnIsolate
(
String
path
,
SendPort
message
)
async
{
final
File
fakeTest
=
File
(
path
.
join
(
tempDirectory
.
path
,
testFile
.
path
))
String
testPath
=
p
.
absolute
(
p
.
join
(
precompiledPath
,
path
)
+
'.vm_test.dart'
);
..
createSync
(
recursive:
true
)
testPath
=
testPath
.
substring
(
0
,
testPath
.
length
-
'.dart'
.
length
)
+
'.vm.app.dill'
;
..
writeAsStringSync
(
'''
return
await
Isolate
.
spawnUri
(
p
.
toUri
(
testPath
),
<
String
>[],
message
,
import "package:test/test.dart";
packageConfig:
p
.
toUri
(
'.packages'
),
import "
${path.absolute(testFile.path)}
" as entrypoint;
checked:
true
,
);
void main() {
group('', entrypoint.main);
}
'''
);
return
fakeTest
.
path
;
}
}
Future
<
void
>
_runTest
(
File
testFile
)
async
{
@override
final
PoolResource
resource
=
await
pool
.
request
();
Future
<
void
>
close
()
async
{
final
String
testPath
=
_createTest
(
testFile
);
final
int
port
=
await
_findPort
();
final
Uri
coverageUri
=
Uri
.
parse
(
'http://127.0.0.1:
$port
'
);
final
Completer
<
void
>
completer
=
Completer
<
void
>();
final
String
packagesPath
=
argResults
[
'packages'
];
final
Process
testProcess
=
await
Process
.
start
(
Platform
.
resolvedExecutable
,
<
String
>[
'--packages=
$packagesPath
'
,
'--pause-isolates-on-exit'
,
'--enable-asserts'
,
'--enable-vm-service=
${coverageUri.port}
'
,
testPath
,
],
runInShell:
true
,
environment:
<
String
,
String
>{
'FLUTTER_ROOT'
:
flutterRoot
,
}).
timeout
(
const
Duration
(
seconds:
30
));
testProcess
.
stdout
.
transform
(
utf8
.
decoder
)
.
transform
(
const
LineSplitter
())
.
listen
((
String
line
)
{
print
(
line
);
if
(
line
.
contains
(
'All tests passed'
)
||
line
.
contains
(
'Some tests failed'
))
{
completer
.
complete
(
null
);
}
});
try
{
try
{
await
completer
.
future
;
await
Future
.
wait
(
_pending
.
values
).
timeout
(
const
Duration
(
seconds:
10
));
await
coverageCollector
.
collectCoverage
(
testProcess
,
coverageUri
).
timeout
(
const
Duration
(
seconds:
30
));
testProcess
?.
kill
();
}
on
TimeoutException
{
}
on
TimeoutException
{
print
(
'Failed to collect coverage for
${testFile.path}
after 30 seconds'
);
// TODO(jonahwilliams): resolve whether there are any specific tests that
}
finally
{
// get stuck or if it is a general infra issue with how we are collecting
resource
.
release
();
// coverage.
// Log tests that are "Stuck" waiuting for coverage.
print
(
'The folllowing tests timed out waiting for coverage:'
);
print
(
_pending
.
keys
.
join
(
', '
));
}
}
final
String
packagePath
=
Directory
.
current
.
path
;
final
Resolver
resolver
=
Resolver
(
packagesPath:
'.packages'
);
final
Formatter
formatter
=
LcovFormatter
(
resolver
,
reportOn:
<
String
>[
'lib'
,
],
basePath:
packagePath
);
final
String
result
=
await
coverageCollector
.
finalizeCoverage
(
formatter:
formatter
,
);
final
String
prefix
=
Platform
.
environment
[
'SUBSHARD'
]
??
''
;
final
String
outputLcovPath
=
p
.
join
(
'coverage'
,
'
$prefix
.lcov.info'
);
File
(
outputLcovPath
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
result
);
}
}
}
Future
<
int
>
_findPort
()
async
{
class
VMEnvironment
implements
Environment
{
int
port
=
0
;
VMEnvironment
(
this
.
observatoryUrl
,
this
.
_isolate
);
ServerSocket
serverSocket
;
try
{
@override
serverSocket
=
await
ServerSocket
.
bind
(
InternetAddress
.
loopbackIPv4
.
address
,
0
);
final
bool
supportsDebugging
=
false
;
port
=
serverSocket
.
port
;
}
catch
(
e
)
{
@override
// Failures are signaled by a return value of 0 from this function.
final
Uri
observatoryUrl
;
print
(
'_findPort failed:
$e
'
);
}
/// The VM service isolate object used to control this isolate.
if
(
serverSocket
!=
null
)
{
final
VMIsolateRef
_isolate
;
await
serverSocket
.
close
();
}
@override
return
port
;
Uri
get
remoteDebuggerUrl
=>
null
;
@override
Stream
<
void
>
get
onRestart
=>
StreamController
<
dynamic
>.
broadcast
().
stream
;
@override
CancelableOperation
<
void
>
displayPause
()
{
final
CancelableCompleter
<
dynamic
>
completer
=
CancelableCompleter
<
dynamic
>(
onCancel:
()
=>
_isolate
.
resume
());
completer
.
complete
(
_isolate
.
pause
().
then
((
dynamic
_
)
=>
_isolate
.
onPauseOrResume
.
firstWhere
((
VMPauseEvent
event
)
=>
event
is
VMResumeEvent
)));
return
completer
.
operation
;
}
}
}
}
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