test scenario_app on CI (flutter/engine#10065)
This commit is contained in:
@@ -16,10 +16,11 @@ task:
|
||||
FLUTTER_ENGINE: "/tmp/clean_engine/src"
|
||||
FRAMEWORK_PATH: "/tmp/master_framework"
|
||||
PATH: "$FLUTTER_ENGINE/third_party/dart/tools/sdks/dart-sdk/bin:$DEPOT_TOOLS:$PATH"
|
||||
USE_ANDROID: "False"
|
||||
setup_script: |
|
||||
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git $DEPOT_TOOLS
|
||||
mkdir -p $ENGINE_PATH/src
|
||||
echo 'solutions = [{"managed": False,"name": "src/flutter","url": "git@github.com:flutter/engine.git","deps_file": "DEPS", "custom_vars": {"download_android_deps" : False, "download_windows_deps" : False,},},]' > $ENGINE_PATH/.gclient
|
||||
echo 'solutions = [{"managed": False,"name": "src/flutter","url": "git@github.com:flutter/engine.git","deps_file": "DEPS", "custom_vars": {"download_android_deps" : ' $USE_ANDROID ', "download_windows_deps" : False,},},]' > $ENGINE_PATH/.gclient
|
||||
cd $ENGINE_PATH/src
|
||||
rm -rf flutter
|
||||
rm -rf out
|
||||
@@ -62,10 +63,9 @@ task:
|
||||
cd $ENGINE_PATH/src
|
||||
./flutter/testing/run_tests.sh host_release
|
||||
- name: build_and_test_android_unopt_debug
|
||||
get_android_sdk_script: |
|
||||
echo 'solutions = [{"managed": False,"name": "src/flutter","url": "git@github.com:flutter/engine.git","deps_file": "DEPS", "custom_vars": {"download_windows_deps" : False,},},]' > $ENGINE_PATH/.gclient
|
||||
cd $ENGINE_PATH/src
|
||||
gclient sync
|
||||
env:
|
||||
USE_ANDROID: "True"
|
||||
ANDROID_HOME: $ENGINE_PATH/src/third_party/android_tools/sdk
|
||||
lint_host_script: |
|
||||
cd $ENGINE_PATH/src/flutter/tools/android_lint
|
||||
$ENGINE_PATH/src/third_party/dart/tools/sdks/dart-sdk/bin/pub get
|
||||
@@ -77,6 +77,27 @@ task:
|
||||
mkdir javadoc_tmp
|
||||
./flutter/tools/gen_javadoc.py --out-dir javadoc_tmp
|
||||
test_android_script: cd $ENGINE_PATH/src && python ./flutter/testing/run_tests.py --type=java
|
||||
- name: build_and_test_android_profile_app
|
||||
env:
|
||||
USE_ANDROID: "True"
|
||||
ANDROID_HOME: $ENGINE_PATH/src/third_party/android_tools/sdk
|
||||
GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[3be31b3547bea4e70cff1d46f9a11ad8c6b42c1982a3964d81e437dee2035f674f12e130bde231352421d8de2029c55f]
|
||||
compile_host_script: |
|
||||
cd $ENGINE_PATH/src
|
||||
./flutter/tools/gn --runtime-mode=profile --no-lto
|
||||
autoninja -C out/host_profile
|
||||
compile_android_script: |
|
||||
cd $ENGINE_PATH/src
|
||||
./flutter/tools/gn --android --runtime-mode=profile --no-lto --android-cpu=arm64
|
||||
ninja -C out/android_profile_arm64
|
||||
compile_app_script: |
|
||||
cd $ENGINE_PATH/src/flutter/testing/scenario_app
|
||||
./compile_android_aot.sh "$ENGINE_PATH/src/out/host_profile" "$ENGINE_PATH/src/out/android_profile_arm64/clang_x64"
|
||||
cd android
|
||||
./gradlew assembleDebug
|
||||
firebase_test_script: |
|
||||
cd $ENGINE_PATH/src
|
||||
./flutter/ci/firebase_testlab.sh "$ENGINE_PATH/src/flutter/testing/scenario_app/android/app/build/outputs/apk/debug/app-debug.apk"
|
||||
- name: format_and_dart_test
|
||||
format_script: |
|
||||
cd $ENGINE_PATH/src/flutter
|
||||
|
||||
@@ -4,7 +4,7 @@ ENV DEPOT_TOOLS_PATH $HOME/depot_tools
|
||||
ENV ENGINE_PATH $HOME/engine
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y git wget curl unzip python lsb-release sudo
|
||||
RUN apt-get install -y git wget curl unzip python lsb-release sudo apt-transport-https
|
||||
|
||||
RUN git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $DEPOT_TOOLS_PATH
|
||||
ENV PATH $PATH:$DEPOT_TOOLS_PATH
|
||||
@@ -18,3 +18,14 @@ WORKDIR $ENGINE_PATH/src
|
||||
RUN ./build/install-build-deps.sh --no-prompt
|
||||
RUN ./build/install-build-deps-android.sh --no-prompt
|
||||
RUN ./flutter/build/install-build-deps-linux-desktop.sh
|
||||
|
||||
# Add repo for gcloud sdk and install it
|
||||
RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | \
|
||||
tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
|
||||
|
||||
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | \
|
||||
apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
|
||||
|
||||
RUN apt-get update && apt-get install -y google-cloud-sdk && \
|
||||
gcloud config set core/disable_usage_reporting true && \
|
||||
gcloud config set component_manager/disable_update_check true
|
||||
|
||||
37
engine/src/flutter/ci/firebase_testlab.sh
Executable file
37
engine/src/flutter/ci/firebase_testlab.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
GIT_REVISION=$(git rev-parse HEAD)
|
||||
|
||||
if [[ ! -f $1 ]]; then
|
||||
echo "File $1 not found."
|
||||
exit -1
|
||||
fi
|
||||
|
||||
# New contributors will not have permissions to run this test - they won't be
|
||||
# able to access the service account information. We should just mark the test
|
||||
# as passed - it will run fine on post submit, where it will still catch
|
||||
# failures.
|
||||
# We can also still make sure that building a release app bundle still works.
|
||||
if [[ $GCLOUD_FIREBASE_TESTLAB_KEY == ENCRYPTED* ]]; then
|
||||
echo "This user does not have permission to run this test."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo $GCLOUD_FIREBASE_TESTLAB_KEY > ${HOME}/gcloud-service-key.json
|
||||
gcloud auth activate-service-account --key-file=${HOME}/gcloud-service-key.json
|
||||
gcloud --quiet config set project flutter-infra
|
||||
|
||||
# Run the test.
|
||||
# game-loop tests are meant for OpenGL apps.
|
||||
# This type of test will give the application a handle to a file, and
|
||||
# we'll write the timeline JSON to that file.
|
||||
# See https://firebase.google.com/docs/test-lab/android/game-loop
|
||||
gcloud firebase test android run \
|
||||
--type game-loop \
|
||||
--app $1 \
|
||||
--timeout 2m \
|
||||
--results-bucket=gs://flutter_firebase_testlab \
|
||||
--results-dir=engine_scenario_test/$GIT_REVISION/$CIRRUS_BUILD_ID
|
||||
|
||||
@@ -27,6 +27,7 @@ dependencies {
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
implementation 'com.android.support:design:28.0.0'
|
||||
implementation 'android.arch.lifecycle:common-java8:1.1.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="dev.flutter.scenarios">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:allowBackup="true"
|
||||
@@ -17,6 +16,11 @@
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.intent.action.TEST_LOOP"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="application/javascript"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
||||
@@ -1,12 +1,94 @@
|
||||
package dev.flutter.scenarios;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import io.flutter.app.FlutterActivity;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import io.flutter.Log;
|
||||
import io.flutter.embedding.android.FlutterActivity;
|
||||
import io.flutter.embedding.android.FlutterFragment;
|
||||
import io.flutter.embedding.android.FlutterView;
|
||||
import io.flutter.embedding.engine.FlutterShellArgs;
|
||||
import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
|
||||
import io.flutter.plugin.common.BasicMessageChannel;
|
||||
import io.flutter.plugin.common.BinaryCodec;
|
||||
|
||||
public class MainActivity extends FlutterActivity implements OnFirstFrameRenderedListener {
|
||||
final static String TAG = "Scenarios";
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final Intent launchIntent = getIntent();
|
||||
if ("com.google.intent.action.TEST_LOOP".equals(launchIntent.getAction())) {
|
||||
if(Build.VERSION.SDK_INT > 22){
|
||||
requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
|
||||
}
|
||||
// Run for one minute, get the timeline data, write it, and finish.
|
||||
final Uri logFileUri = launchIntent.getData();
|
||||
new Handler().postDelayed(() -> writeTimelineData(logFileUri), 20000);
|
||||
}
|
||||
}
|
||||
|
||||
public void onFirstFrameRendered() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
reportFullyDrawn();
|
||||
}
|
||||
}
|
||||
|
||||
private FlutterShellArgs getFlutterShellArgs() {
|
||||
FlutterShellArgs args = FlutterShellArgs.fromIntent(getIntent());
|
||||
args.add(FlutterShellArgs.ARG_TRACE_STARTUP);
|
||||
args.add(FlutterShellArgs.ARG_ENABLE_DART_PROFILING);
|
||||
args.add(FlutterShellArgs.ARG_VERBOSE_LOGGING);
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected FlutterFragment createFlutterFragment() {
|
||||
return new FlutterFragment.Builder()
|
||||
.dartEntrypoint(getDartEntrypoint())
|
||||
.initialRoute(getInitialRoute())
|
||||
.appBundlePath(getAppBundlePath())
|
||||
.flutterShellArgs(getFlutterShellArgs())
|
||||
.renderMode(FlutterView.RenderMode.surface)
|
||||
.transparencyMode(FlutterView.TransparencyMode.opaque)
|
||||
.shouldAttachEngineToActivity(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void writeTimelineData(Uri logFile) {
|
||||
if (logFile == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (getFlutterEngine() == null) {
|
||||
Log.e(TAG, "Could not write timeline data - no engine.");
|
||||
return;
|
||||
}
|
||||
final BasicMessageChannel<ByteBuffer> channel = new BasicMessageChannel<>(
|
||||
getFlutterEngine().getDartExecutor(), "write_timeline", BinaryCodec.INSTANCE);
|
||||
channel.send(null, (ByteBuffer reply) -> {
|
||||
try {
|
||||
final FileDescriptor fd = getContentResolver()
|
||||
.openAssetFileDescriptor(logFile, "w").getFileDescriptor();
|
||||
final FileOutputStream outputStream = new FileOutputStream(fd);
|
||||
outputStream.write(reply.array());
|
||||
outputStream.close();
|
||||
} catch (IOException ex) {
|
||||
Log.e(TAG, "Could not write timeline file: " + ex.toString());
|
||||
}
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ echo "Compiling ELF Shared Library..."
|
||||
"$DEVICE_TOOLS/gen_snapshot" --deterministic --snapshot_kind=app-aot-elf --elf="$OUTDIR/libapp.so" --strip "$OUTDIR/app.dill"
|
||||
|
||||
mkdir -p "android/app/src/main/jniLibs/arm64-v8a"
|
||||
mkdir -p "android/app/libs"
|
||||
cp "$OUTDIR/libapp.so" "android/app/src/main/jniLibs/arm64-v8a/"
|
||||
cp "$DEVICE_TOOLS/../flutter.jar" "android/app/libs/"
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:developer' as developer;
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
@@ -27,7 +30,7 @@ void main() {
|
||||
window.sendPlatformMessage('scenario_status', data, null);
|
||||
}
|
||||
|
||||
void _handlePlatformMessage(String name, ByteData data, PlatformMessageResponseCallback callback) {
|
||||
Future<void> _handlePlatformMessage(String name, ByteData data, PlatformMessageResponseCallback callback) async {
|
||||
if (name == 'set_scenario' && data != null) {
|
||||
final String scenarioName = utf8.decode(data.buffer.asUint8List());
|
||||
final Scenario candidateScenario = _scenarios[scenarioName];
|
||||
@@ -40,9 +43,41 @@ void _handlePlatformMessage(String name, ByteData data, PlatformMessageResponseC
|
||||
data.setUint8(0, candidateScenario == null ? 0 : 1);
|
||||
callback(data);
|
||||
}
|
||||
} else if (name == 'write_timeline') {
|
||||
final String timelineData = await _getTimelineData();
|
||||
callback(Uint8List.fromList(utf8.encode(timelineData)).buffer.asByteData());
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _getTimelineData() async {
|
||||
final String isolateId = developer.Service.getIsolateID(Isolate.current);
|
||||
final developer.ServiceProtocolInfo info = await developer.Service.getInfo();
|
||||
final Uri cpuProfileTimelineUri = info.serverUri.resolve(
|
||||
'_getCpuProfileTimeline?tags=None&isolateId=$isolateId',
|
||||
);
|
||||
final Uri vmServiceTimelineUri = info.serverUri.resolve('getVMTimeline');
|
||||
final Map<String, dynamic> cpuTimelineJson = await _getJson(cpuProfileTimelineUri);
|
||||
final Map<String, dynamic> vmServiceTimelineJson = await _getJson(vmServiceTimelineUri);
|
||||
final Map<String, dynamic> cpuResult = cpuTimelineJson['result'].cast<String, dynamic>();
|
||||
final Map<String, dynamic> vmServiceResult = vmServiceTimelineJson['result'].cast<String, dynamic>();
|
||||
|
||||
return json.encode(<String, dynamic>{
|
||||
'stackFrames': cpuResult['stackFrames'],
|
||||
'traceEvents': <dynamic>[...cpuResult['traceEvents'], ...vmServiceResult['traceEvents']],
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> _getJson(Uri uri) async {
|
||||
final HttpClient client = HttpClient();
|
||||
final HttpClientRequest request = await client.getUrl(uri);
|
||||
final HttpClientResponse response = await request.close();
|
||||
if (response.statusCode > 299) {
|
||||
return null;
|
||||
}
|
||||
final String data = await utf8.decodeStream(response);
|
||||
return json.decode(data);
|
||||
}
|
||||
|
||||
void _onBeginFrame(Duration duration) {
|
||||
_currentScenario.onBeginFrame(duration);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user