blob: 4b86a0b12a90e0f5c43c7330787071803e16f4ce [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.dumprendertree;
import com.android.dumprendertree.forwarder.AdbUtils;
import com.android.dumprendertree.forwarder.ForwardServer;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.os.Process;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LoadTestsAutoTest extends ActivityInstrumentationTestCase2<TestShellActivity> {
private final static String LOGTAG = "LoadTest";
private final static String LOAD_TEST_RESULT =
Environment.getExternalStorageDirectory() + "/load_test_result.txt";
private final static int MAX_GC_WAIT_SEC = 10;
private final static int LOCAL_PORT = 17171;
private boolean mFinished;
static final String LOAD_TEST_RUNNER_FILES[] = {
"run_page_cycler.py"
};
private ForwardServer mForwardServer;
public LoadTestsAutoTest() {
super(TestShellActivity.class);
}
// This function writes the result of the layout test to
// Am status so that it can be picked up from a script.
public void passOrFailCallback(String file, boolean result) {
Instrumentation inst = getInstrumentation();
Bundle bundle = new Bundle();
bundle.putBoolean(file, result);
inst.sendStatus(0, bundle);
}
private String setUpForwarding(String forwardInfo, String suite, String iteration) throws IOException {
// read forwarding information first
Pattern forwardPattern = Pattern.compile("(.*):(\\d+)/(.*)/");
Matcher matcher = forwardPattern.matcher(forwardInfo);
if (!matcher.matches()) {
throw new RuntimeException("Invalid forward information");
}
String host = matcher.group(1);
int port = Integer.parseInt(matcher.group(2));
mForwardServer = new ForwardServer(LOCAL_PORT, AdbUtils.resolve(host), port);
mForwardServer.start();
return String.format("http://127.0.0.1:%d/%s/%s/start.html?auto=1&iterations=%s",
LOCAL_PORT, matcher.group(3), suite, iteration);
}
// Invokes running of layout tests
// and waits till it has finished running.
public void runPageCyclerTest() throws IOException {
LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation();
if (runner.mPageCyclerSuite != null) {
// start forwarder to use page cycler suites hosted on external web server
if (runner.mPageCyclerForwardHost == null) {
throw new RuntimeException("no forwarder information provided");
}
runner.mTestPath = setUpForwarding(runner.mPageCyclerForwardHost,
runner.mPageCyclerSuite, runner.mPageCyclerIteration);
Log.d(LOGTAG, "using path: " + runner.mTestPath);
}
if (runner.mTestPath == null) {
throw new RuntimeException("No test specified");
}
final TestShellActivity activity = (TestShellActivity) getActivity();
Log.v(LOGTAG, "About to run tests, calling gc first...");
freeMem();
// Run tests
runTestAndWaitUntilDone(activity, runner.mTestPath, runner.mTimeoutInMillis);
getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
activity.clearCache();
}
});
if (mForwardServer != null) {
mForwardServer.stop();
mForwardServer = null;
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
dumpMemoryInfo();
// Kill activity
activity.finish();
}
private void freeMem() {
Log.v(LOGTAG, "freeMem: calling gc...");
final CountDownLatch latch = new CountDownLatch(1);
@SuppressWarnings("unused")
Object dummy = new Object() {
// this object instance is used to track gc
@Override
protected void finalize() throws Throwable {
latch.countDown();
super.finalize();
}
};
dummy = null;
System.gc();
try {
if (!latch.await(MAX_GC_WAIT_SEC, TimeUnit.SECONDS)) {
Log.w(LOGTAG, "gc did not happen in 10s");
}
} catch (InterruptedException e) {
//ignore
}
}
private void printRow(PrintStream ps, String format, Object...objs) {
ps.println(String.format(format, objs));
}
private void dumpMemoryInfo() {
try {
freeMem();
Log.v(LOGTAG, "Dumping memory information.");
FileOutputStream out = new FileOutputStream(LOAD_TEST_RESULT, true);
PrintStream ps = new PrintStream(out);
ps.print("\n\n\n");
ps.println("** MEMINFO in pid " + Process.myPid()
+ " [com.android.dumprendertree] **");
String formatString = "%17s %8s %8s %8s %8s";
long nativeMax = Debug.getNativeHeapSize() / 1024;
long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
Runtime runtime = Runtime.getRuntime();
long dalvikMax = runtime.totalMemory() / 1024;
long dalvikFree = runtime.freeMemory() / 1024;
long dalvikAllocated = dalvikMax - dalvikFree;
Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
final int nativeShared = memInfo.nativeSharedDirty;
final int dalvikShared = memInfo.dalvikSharedDirty;
final int otherShared = memInfo.otherSharedDirty;
final int nativePrivate = memInfo.nativePrivateDirty;
final int dalvikPrivate = memInfo.dalvikPrivateDirty;
final int otherPrivate = memInfo.otherPrivateDirty;
printRow(ps, formatString, "", "native", "dalvik", "other", "total");
printRow(ps, formatString, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax);
printRow(ps, formatString, "allocated:", nativeAllocated, dalvikAllocated, "N/A",
nativeAllocated + dalvikAllocated);
printRow(ps, formatString, "free:", nativeFree, dalvikFree, "N/A",
nativeFree + dalvikFree);
printRow(ps, formatString, "(Pss):", memInfo.nativePss, memInfo.dalvikPss,
memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss);
printRow(ps, formatString, "(shared dirty):", nativeShared, dalvikShared, otherShared,
nativeShared + dalvikShared + otherShared);
printRow(ps, formatString, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate,
nativePrivate + dalvikPrivate + otherPrivate);
ps.print("\n\n\n");
ps.flush();
ps.close();
out.flush();
out.close();
} catch (IOException e) {
Log.e(LOGTAG, e.getMessage());
}
}
// A convenient method to be called by another activity.
private void runTestAndWaitUntilDone(TestShellActivity activity, String url, int timeout) {
activity.setCallback(new TestShellCallback() {
@Override
public void finished() {
synchronized (LoadTestsAutoTest.this) {
mFinished = true;
LoadTestsAutoTest.this.notifyAll();
}
}
@Override
public void timedOut(String url) {
}
@Override
public void dumpResult(String webViewDump) {
String lines[] = webViewDump.split("\\r?\\n");
for (String line : lines) {
line = line.trim();
// parse for a line like this:
// totals: 9620.00 11947.00 10099.75 380.38
// and return the 3rd number, which is mean
if (line.startsWith("totals:")) {
line = line.substring(7).trim(); // strip "totals:"
String[] numbers = line.split("\\s+");
if (numbers.length == 4) {
Bundle b = new Bundle();
b.putString("mean", numbers[2]);
getInstrumentation().sendStatus(Activity.RESULT_FIRST_USER, b);
}
}
}
}
});
mFinished = false;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClass(activity, TestShellActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(TestShellActivity.TEST_URL, url);
intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout);
intent.putExtra(TestShellActivity.RESULT_FILE, LOAD_TEST_RESULT);
activity.startActivity(intent);
// Wait until done.
synchronized (this) {
while(!mFinished) {
try {
this.wait();
} catch (InterruptedException e) { }
}
}
}
public void copyRunnerAssetsToCache() {
try {
Context targetContext = getInstrumentation().getTargetContext();
File cacheDir = targetContext.getCacheDir();
for( int i=0; i< LOAD_TEST_RUNNER_FILES.length; i++) {
InputStream in = targetContext.getAssets().open(
LOAD_TEST_RUNNER_FILES[i]);
OutputStream out = new FileOutputStream(
new File(cacheDir, LOAD_TEST_RUNNER_FILES[i]));
byte[] buf = new byte[2048];
int len;
while ((len = in.read(buf)) >= 0 ) {
out.write(buf, 0, len);
}
out.close();
in.close();
}
}catch (IOException e) {
e.printStackTrace();
}
}
}