├── dist ├── demo.jpg ├── adb │ ├── Mac │ │ └── adb │ ├── Linux │ │ └── adb │ └── Windows │ │ ├── adb.exe │ │ ├── AdbWinApi.dll │ │ ├── fastboot.exe │ │ └── AdbWinUsbApi.dll ├── example.apk ├── logs │ ├── index.html │ ├── c6103ba0-5c61-4a18-abd9-ec3acbaf23cd │ │ ├── traces_log.txt │ │ ├── properties.txt │ │ └── monkey_log.txt │ ├── f451e7dc-66c0-4f8e-b7fb-d96402134267 │ │ ├── traces_log.txt │ │ ├── properties.txt │ │ └── monkey_log.txt │ ├── c6103ba0-5c61-4a18-abd9-ec3acbaf23cd.zip │ ├── f451e7dc-66c0-4f8e-b7fb-d96402134267.zip │ └── index_utf8.html ├── chkbugreport.jar ├── monkey-adapter-runner.jar ├── monkey-adapter-analyzer.jar ├── gen_report.sh └── run_monkey_mac.sh ├── src ├── AndroidMonkeyResultAnalyzer │ ├── .settings │ │ ├── org.eclipse.core.runtime.prefs │ │ ├── org.eclipse.core.resources.prefs │ │ └── org.eclipse.jdt.core.prefs │ ├── lib │ │ ├── org.json.jar │ │ └── commons-cli-1.2.jar │ └── src │ │ └── com │ │ └── github │ │ └── monkey │ │ └── analyzer │ │ ├── model │ │ ├── AbnormalityType.java │ │ ├── NativeCrashAbnormality.java │ │ ├── AbnormalityFactory.java │ │ ├── AnrOrCrashAbnormality.java │ │ ├── InvalidAbnormality.java │ │ ├── Abnormality.java │ │ └── AbnormalityProperties.java │ │ ├── analyze │ │ ├── IAnalyzer.java │ │ ├── CrashOrAnrAnalyzer.java │ │ ├── NativeCrashAnalyzer.java │ │ ├── Constants.java │ │ ├── AnalyzerInvoker.java │ │ ├── AnalyzerConfiguration.java │ │ ├── Analyzer.java │ │ └── AnalyzerClient.java │ │ ├── statistics │ │ ├── Tuple.java │ │ ├── CrashAbnormalityAdapter.java │ │ └── AbnormalitiesAnalyzerWrapper.java │ │ ├── report │ │ ├── JSONReportProvider.java │ │ ├── JSONReport2HtmlReport.java │ │ └── Abnormalities2JSONReport.java │ │ ├── Main.java │ │ └── CLIParser.java └── AndroidMonkeyRunningAdapter │ ├── .settings │ ├── org.eclipse.core.resources.prefs │ └── org.eclipse.jdt.core.prefs │ ├── adb │ ├── Linux │ │ └── adb │ ├── Mac │ │ └── adb │ └── Windows │ │ ├── adb.exe │ │ ├── AdbWinApi.dll │ │ └── AdbWinUsbApi.dll │ ├── libs │ ├── ddmlib.jar │ ├── ddmuilib.jar │ └── commons-cli-1.2.jar │ └── src │ └── com │ └── github │ └── monkey │ └── runner │ ├── scheduler │ ├── MonkeyTestDeviceFactory.java │ ├── Console.java │ ├── MonkeyTestSeriesFactory.java │ ├── MonkeyTestSeries.java │ ├── MonkeyTestDevice.java │ └── MonkeyTest.java │ ├── helper │ ├── SharedProperties.java │ ├── PropertiesHelper.java │ ├── ZipHelper.java │ ├── CommandHelper.java │ ├── DeviceConnectHelper.java │ └── LocationHelper.java │ ├── CLIParser.java │ └── Main.java ├── .gitignore ├── README.md └── LICENSE /dist/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/demo.jpg -------------------------------------------------------------------------------- /dist/adb/Mac/adb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/adb/Mac/adb -------------------------------------------------------------------------------- /dist/example.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/example.apk -------------------------------------------------------------------------------- /dist/adb/Linux/adb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/adb/Linux/adb -------------------------------------------------------------------------------- /dist/logs/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/logs/index.html -------------------------------------------------------------------------------- /dist/chkbugreport.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/chkbugreport.jar -------------------------------------------------------------------------------- /dist/adb/Windows/adb.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/adb/Windows/adb.exe -------------------------------------------------------------------------------- /dist/logs/c6103ba0-5c61-4a18-abd9-ec3acbaf23cd/traces_log.txt: -------------------------------------------------------------------------------- 1 | /data/anr/traces.txt: No such file or directory 2 | 3 | -------------------------------------------------------------------------------- /dist/logs/f451e7dc-66c0-4f8e-b7fb-d96402134267/traces_log.txt: -------------------------------------------------------------------------------- 1 | /data/anr/traces.txt: No such file or directory 2 | 3 | -------------------------------------------------------------------------------- /dist/adb/Windows/AdbWinApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/adb/Windows/AdbWinApi.dll -------------------------------------------------------------------------------- /dist/adb/Windows/fastboot.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/adb/Windows/fastboot.exe -------------------------------------------------------------------------------- /dist/monkey-adapter-runner.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/monkey-adapter-runner.jar -------------------------------------------------------------------------------- /dist/monkey-adapter-analyzer.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/monkey-adapter-analyzer.jar -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/.settings/org.eclipse.core.runtime.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | line.separator=\r\n 3 | -------------------------------------------------------------------------------- /dist/adb/Windows/AdbWinUsbApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/adb/Windows/AdbWinUsbApi.dll -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/adb/Linux/adb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyRunningAdapter/adb/Linux/adb -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/adb/Mac/adb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyRunningAdapter/adb/Mac/adb -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/libs/ddmlib.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyRunningAdapter/libs/ddmlib.jar -------------------------------------------------------------------------------- /dist/logs/c6103ba0-5c61-4a18-abd9-ec3acbaf23cd.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/logs/c6103ba0-5c61-4a18-abd9-ec3acbaf23cd.zip -------------------------------------------------------------------------------- /dist/logs/f451e7dc-66c0-4f8e-b7fb-d96402134267.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/dist/logs/f451e7dc-66c0-4f8e-b7fb-d96402134267.zip -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/lib/org.json.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyResultAnalyzer/lib/org.json.jar -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/libs/ddmuilib.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyRunningAdapter/libs/ddmuilib.jar -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/adb/Windows/adb.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyRunningAdapter/adb/Windows/adb.exe -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/lib/commons-cli-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyResultAnalyzer/lib/commons-cli-1.2.jar -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/adb/Windows/AdbWinApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyRunningAdapter/adb/Windows/AdbWinApi.dll -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/libs/commons-cli-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyRunningAdapter/libs/commons-cli-1.2.jar -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/adb/Windows/AdbWinUsbApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apack1001/Android-Monkey-Adapter/HEAD/src/AndroidMonkeyRunningAdapter/adb/Windows/AdbWinUsbApi.dll -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Eclipse project files 19 | .classpath 20 | .project 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Intellij project files 26 | *.iml 27 | *.ipr 28 | *.iws 29 | .idea/ 30 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/model/AbnormalityType.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.model; 2 | 3 | /** 4 | * this class define an enumeration of abnormality type.
5 | * If a new kind of abnormality is registered, one should add a new type 6 | * 7 | * @author Alex Chen (apack1001@gmail.com) 8 | * 9 | */ 10 | public enum AbnormalityType { 11 | /** 12 | * Unknown abnormality type 13 | */ 14 | UNKNOWN, 15 | /** 16 | * Monkey Crash 17 | */ 18 | CRASH, 19 | /** 20 | * Monkey ANR 21 | */ 22 | ANR, 23 | /** 24 | * Native Crash 25 | */ 26 | NATIVE, 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.6 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.6 12 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/analyze/IAnalyzer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.github.monkey.analyzer.analyze; 5 | 6 | import com.github.monkey.analyzer.model.Abnormality; 7 | 8 | /** 9 | * define Filter Operations 10 | * @author Alex Chen (apack1001@gmail.com) 11 | * 12 | */ 13 | public interface IAnalyzer { 14 | /** 15 | * perform filter 16 | * @param path abnormal file path 17 | * @return Model or Bean of Abnormality 18 | */ 19 | public Abnormality toAbnormality(); 20 | 21 | /** 22 | * set the monkey result path of, must be called before toAbnormality 23 | * @param path 24 | */ 25 | public void setPath(final String path); 26 | } 27 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.6 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.6 12 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/model/NativeCrashAbnormality.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.model; 2 | 3 | /** 4 | * @brief This is an implement of native crash abnormality 5 | * 6 | * @author Alex Chen (apack1001@gmail.com) 7 | * 8 | * @since 2013/03/19 9 | */ 10 | public class NativeCrashAbnormality extends Abnormality { 11 | 12 | @Override 13 | public void setType(final AbnormalityType type) { 14 | // do noting 15 | } 16 | 17 | @Override 18 | public AbnormalityType getType() { 19 | return AbnormalityType.NATIVE; 20 | } 21 | 22 | @Override 23 | public void setValid(final boolean isValid) { 24 | this.isValid = isValid; 25 | } 26 | 27 | @Override 28 | public boolean isValid() { 29 | return isValid; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/scheduler/MonkeyTestDeviceFactory.java: -------------------------------------------------------------------------------- 1 | 2 | package com.github.monkey.runner.scheduler; 3 | 4 | import com.android.ddmlib.IDevice; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class MonkeyTestDeviceFactory { 10 | 11 | public static Map mBuffer = new HashMap(); 12 | 13 | public static MonkeyTestDevice newDevice(IDevice device) { 14 | if (!mBuffer.containsKey(device.getSerialNumber())) { 15 | mBuffer.put(device.getSerialNumber(), new MonkeyTestDevice(device)); 16 | } 17 | MonkeyTestDevice bufferedDevice = mBuffer.get(device.getSerialNumber()); 18 | return bufferedDevice; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/model/AbnormalityFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.model; 2 | 3 | /** 4 | * @brief This is an Abnormality Factory which is used to create derived, empty 5 | * Abnormality 6 | * @author Alex Chen (apack1001@gmail.com) 7 | * 8 | */ 9 | public class AbnormalityFactory { 10 | public static synchronized Abnormality newInstance(AbnormalityType type) { 11 | Abnormality abnormality = null; 12 | switch (type) { 13 | case ANR: 14 | case CRASH: 15 | abnormality = new AnrOrCrashAbnormality(); 16 | break; 17 | case NATIVE: 18 | abnormality = new NativeCrashAbnormality(); 19 | break; 20 | default: 21 | abnormality = new InvalidAbnormality(); 22 | break; 23 | } 24 | return abnormality; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/model/AnrOrCrashAbnormality.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.model; 2 | 3 | /** 4 | * This class is the default implementation of Abnormality.
5 | *
6 | * It can parse CRASH and ANR abnormality in a monkey log 7 | * 8 | * @author Alex Chen (apack1001@gmail.com) 9 | * 10 | */ 11 | public class AnrOrCrashAbnormality extends Abnormality { 12 | 13 | @Override 14 | public void setType(AbnormalityType type) { 15 | this.type = type; 16 | } 17 | 18 | @Override 19 | public AbnormalityType getType() { 20 | return type; 21 | } 22 | 23 | @Override 24 | public void setValid(boolean isValid) { 25 | this.isValid = isValid; 26 | } 27 | 28 | @Override 29 | public boolean isValid() { 30 | return isValid; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/helper/SharedProperties.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.runner.helper; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class SharedProperties { 6 | 7 | private static ArrayList properties = new ArrayList(); 8 | 9 | private static SharedProperties instance = new SharedProperties(); 10 | 11 | public static SharedProperties getInstance() { 12 | return instance; 13 | } 14 | 15 | private SharedProperties() { 16 | 17 | } 18 | 19 | public void add(final Object property) { 20 | synchronized (properties) { 21 | properties.add(property); 22 | } 23 | } 24 | 25 | public void clear() { 26 | synchronized (properties) { 27 | properties.clear(); 28 | } 29 | } 30 | 31 | public ArrayList getProperties() { 32 | return properties; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/model/InvalidAbnormality.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.model; 2 | 3 | /** 4 | * A default implementation of Invalid abnormality.
5 | *
6 | * If one instance of {@link IAnalyzer} cannot parse the monkey log, it will be 7 | * in use.
8 | *
9 | * This is Null Object Pattern 10 | * 11 | * @author Alex Chen (apack1001@gmail.com) 12 | * 13 | */ 14 | public class InvalidAbnormality extends Abnormality { 15 | @Override 16 | public AbnormalityType getType() { 17 | return AbnormalityType.UNKNOWN; 18 | } 19 | 20 | public void setValid(boolean isValid) { 21 | this.isValid = isValid; 22 | } 23 | 24 | @Override 25 | public boolean isValid() { 26 | return false; 27 | } 28 | 29 | @Override 30 | public void setType(AbnormalityType type) { 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/statistics/Tuple.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.statistics; 2 | 3 | import java.lang.ref.WeakReference; 4 | import java.util.ArrayList; 5 | 6 | import com.github.monkey.analyzer.model.Abnormality; 7 | 8 | /** 9 | * This class is a tuple, it will classify some similar abnormalities to one 10 | * {@link Tuple} class 11 | * 12 | * @author Alex Chen (apack1001@gmail.com) 13 | * TODO name and responsibility need to upgrade 14 | * 15 | */ 16 | public class Tuple { 17 | 18 | /** 19 | * first field 20 | */ 21 | public CrashAbnormalityAdapter crashAbnormalities = null; 22 | 23 | /** 24 | * second field which stores all the similar abnormalities of the same class 25 | */ 26 | public ArrayList> abnormalities = new ArrayList>(); 27 | } 28 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/scheduler/Console.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.runner.scheduler; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.Observable; 6 | 7 | public final class Console extends Observable { 8 | 9 | private static Console mThis = new Console(); 10 | 11 | public Console() { 12 | } 13 | 14 | public static Console getInstance() { 15 | return mThis; 16 | } 17 | 18 | public static void printMessage(String msg) { 19 | mThis.setChanged(); 20 | mThis.notifyObservers(msg); 21 | } 22 | 23 | public static void printLogMessage(String sn, String msg) { 24 | SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 25 | String log = String.format("%s\t%s\t%s", f.format(new Date()), sn, msg); 26 | System.out.println(log); 27 | mThis.setChanged(); 28 | mThis.notifyObservers(log); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /dist/gen_report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | chmod a+x ./adb/mac/adb 4 | # 1. Command Line interface of this tool 5 | DEVICE_ID=$1 6 | UNAME=$2 7 | APK_FILE=$3 8 | PKG_NAME=$4 9 | PKG_VERSION=$5 10 | SIG_DURA=$6 11 | SER_DURA=$6 12 | if [ -d "./logs/" ]; then 13 | echo "./logs/ exists! remove expired folder" 14 | # rm -r ./logs/ 15 | fi 16 | # 2. running command 17 | #java -jar monkey-adapter-runner.jar --device-id $DEVICE_ID --user-name $UNAME --pkg-path $APK_FILE --pkg-name $PKG_NAME --pkg-version $PKG_VERSION --single-duration $SIG_DURA --series-duration $SER_DURA 18 | # 3. analyzing command 19 | java -jar monkey-adapter-analyzer.jar --workspaces ./logs/ --monkey-log-file-name monkey_log.txt --logcat-log-file-name logcat_log.txt --traces-log-file-name traces_log.txt --bugreport-log-file-name bugreport_log.txt --properties-file-name properties.txt --duration $SER_DURA --package-name $PKG_NAME 20 | # 4. open html report 21 | open ./logs/index.html 22 | -------------------------------------------------------------------------------- /dist/run_monkey_mac.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | chmod a+x ./adb/mac/adb 4 | # 1. Command Line interface of this tool 5 | DEVICE_ID=$1 6 | UNAME=$2 7 | APK_FILE=$3 8 | PKG_NAME=$4 9 | PKG_VERSION=$5 10 | SIG_DURA=$6 11 | SER_DURA=$6 12 | if [ -d "./logs/" ]; then 13 | echo "./logs/ exists! remove expired folder" 14 | rm -r ./logs/ 15 | fi 16 | # 2. running command 17 | java -jar monkey-adapter-runner.jar --device-id $DEVICE_ID --user-name $UNAME --pkg-path $APK_FILE --pkg-name $PKG_NAME --pkg-version $PKG_VERSION --single-duration $SIG_DURA --series-duration $SER_DURA 18 | # 3. analyzing command 19 | java -jar monkey-adapter-analyzer.jar --workspaces ./logs/ --monkey-log-file-name monkey_log.txt --logcat-log-file-name logcat_log.txt --traces-log-file-name traces_log.txt --bugreport-log-file-name bugreport_log.txt --properties-file-name properties.txt --duration $SER_DURA --package-name $PKG_NAME 20 | # 4. open html report 21 | open ./logs/index.html 22 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/scheduler/MonkeyTestSeriesFactory.java: -------------------------------------------------------------------------------- 1 | 2 | package com.github.monkey.runner.scheduler; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | public class MonkeyTestSeriesFactory { 8 | 9 | public static Map mBuffer = new HashMap(); 10 | 11 | public static MonkeyTestSeries newSeries(MonkeyTestDevice device, String pkgName, 12 | String pkgVersion, String pkgFilePath, 13 | String rawCommand, String userName, String initFileName, 14 | long seriesDuration, long singleDuration) { 15 | mBuffer.put(device, new MonkeyTestSeries(device, pkgName, pkgVersion, pkgFilePath, 16 | rawCommand, userName, initFileName, 17 | seriesDuration, 18 | singleDuration)); 19 | MonkeyTestSeries bufferedSeries = mBuffer.get(device); 20 | return bufferedSeries; 21 | } 22 | 23 | public static MonkeyTestSeries getSeries(MonkeyTestDevice device) { 24 | return mBuffer.get(device); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/helper/PropertiesHelper.java: -------------------------------------------------------------------------------- 1 | 2 | package com.github.monkey.runner.helper; 3 | 4 | import java.io.BufferedWriter; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.util.Map; 8 | 9 | import com.github.monkey.runner.helper.LocationHelper.FileLocationException; 10 | 11 | public class PropertiesHelper { 12 | public static void setProperties(String id, String section, 13 | Map properties, boolean append) { 14 | try { 15 | String file = LocationHelper.getPropertiesLocation(id); 16 | BufferedWriter bw = new BufferedWriter(new FileWriter(file, append)); 17 | bw.newLine(); 18 | bw.write(String.format("[%s]", section)); 19 | bw.newLine(); 20 | for (Map.Entry prop : properties.entrySet()) { 21 | bw.write(String.format("%s=%s", prop.getKey(), prop.getValue())); 22 | bw.newLine(); 23 | } 24 | bw.flush(); 25 | bw.close(); 26 | 27 | } catch (FileLocationException e) { 28 | e.printStackTrace(); 29 | } catch (IOException e) { 30 | e.printStackTrace(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/analyze/CrashOrAnrAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.analyze; 2 | 3 | import java.util.LinkedList; 4 | 5 | import com.github.monkey.analyzer.model.Abnormality; 6 | 7 | /** 8 | * Default implementation of {@link IAnalyzer}
9 | * This is a business related class so coupling may occur during the following 10 | * upgrading
11 | * 12 | * @author Alex Chen (apack1001@gmail.com) 13 | * 14 | * @since 2013/01/14 15 | */ 16 | class CrashOrAnrAnalyzer extends Analyzer { 17 | 18 | public CrashOrAnrAnalyzer(AnalyzerConfiguration config) { 19 | super(config); 20 | } 21 | 22 | @Override 23 | protected void onMatch(Abnormality abnormality, 24 | StringBuilder message, 25 | LinkedList messageBefore, 26 | LinkedList messageAfter 27 | ) { 28 | StringBuilder msgAfter = new StringBuilder(); 29 | for (String msg : messageAfter) { 30 | msgAfter.append(msg); 31 | msgAfter.append("\n"); 32 | } 33 | abnormality.put(Abnormality.EXTRAS_KEY_AFTER_END_PATTERN, 34 | msgAfter.toString()); 35 | 36 | StringBuilder msgBefore = new StringBuilder(); 37 | for (String msg : messageBefore) { 38 | msgBefore.append(msg); 39 | msgBefore.append("\n"); 40 | } 41 | abnormality.put(Abnormality.EXTRAS_KEY_BEFORE_START_PATTERN, 42 | msgBefore.toString()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/helper/ZipHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.runner.helper; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.util.zip.ZipEntry; 7 | import java.util.zip.ZipOutputStream; 8 | 9 | public class ZipHelper { 10 | 11 | public static boolean zip(String src, String des) throws Exception { 12 | boolean result; 13 | ZipOutputStream out = null; 14 | try { 15 | File f = new File(src); 16 | out = new ZipOutputStream(new FileOutputStream(des)); 17 | zip(out, f, ""); 18 | f.delete(); 19 | result = true; 20 | } catch (Exception ex) { 21 | result = false; 22 | } finally { 23 | out.close(); 24 | } 25 | return result; 26 | } 27 | 28 | static void zip(ZipOutputStream out, File f, String base) throws Exception { 29 | if (f.isDirectory()) { 30 | File[] fl = f.listFiles(); 31 | out.putNextEntry(new ZipEntry(base + File.separator)); 32 | base = base.length() == 0 ? "" : base + File.separator; 33 | for (int i = 0; i < fl.length; i++) { 34 | zip(out, fl[i], base + fl[i].getName()); 35 | } 36 | } else { 37 | out.putNextEntry(new ZipEntry(base)); 38 | FileInputStream in = new FileInputStream(f); 39 | int b; 40 | System.out.println(base); 41 | while ((b = in.read()) != -1) { 42 | out.write(b); 43 | } 44 | in.close(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/helper/CommandHelper.java: -------------------------------------------------------------------------------- 1 | 2 | package com.github.monkey.runner.helper; 3 | 4 | import java.io.BufferedReader; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.io.OutputStreamWriter; 8 | import java.io.PrintWriter; 9 | import java.util.ArrayList; 10 | import java.util.logging.Level; 11 | import java.util.logging.Logger; 12 | 13 | public class CommandHelper { 14 | 15 | public static ArrayList exec(String command) throws InterruptedException { 16 | ArrayList out = new ArrayList(); 17 | Process pro = null; 18 | Runtime runTime = Runtime.getRuntime(); 19 | if (runTime == null) { 20 | System.err.println("Create runtime false!"); 21 | } 22 | try { 23 | pro = runTime.exec(command); 24 | BufferedReader input = new BufferedReader(new InputStreamReader(pro.getInputStream())); 25 | PrintWriter output = new PrintWriter(new OutputStreamWriter(pro.getOutputStream())); 26 | String line; 27 | while ((line = input.readLine()) != null) { 28 | System.out.println(line); 29 | out.add(line); 30 | } 31 | input.close(); 32 | output.close(); 33 | pro.destroy(); 34 | } catch (IOException ex) { 35 | Logger.getLogger(CommandHelper.class.getName()).log(Level.SEVERE, null, ex); 36 | } 37 | return out; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/report/JSONReportProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.report; 2 | 3 | import java.io.File; 4 | import org.json.JSONObject; 5 | 6 | /** 7 | * This class aims at generation the JSON value which is used to fill a HTML 8 | * report or other format.
9 | * Any way! You konw what to do!
10 | * 11 | * @author Alex Chen (apack1001@gmail.com) 12 | * 13 | */ 14 | final public class JSONReportProvider { 15 | 16 | /** 17 | * the report file will stored in this object 18 | */ 19 | private JSONObject json = new JSONObject(); 20 | 21 | /** 22 | * this Override the default implementation of {@link Object}. 23 | * 24 | * @return return the json's {@link }String} value 25 | */ 26 | @Override 27 | public String toString() { 28 | if (json == null) 29 | return ""; 30 | return json.toString(); 31 | } 32 | 33 | public void setJSONObject(final JSONObject json) { 34 | this.json = json; 35 | } 36 | 37 | /** 38 | * This will write the JSON Value to file.
39 | * This must be an not exists file and its parent folder exist
40 | * This won't create folder recursively
41 | * 42 | * @param path 43 | * JSON report file 44 | * 45 | */ 46 | public void toFile(final String path) { 47 | if (path == null || path == "") 48 | throw new RuntimeException("Invalid path"); 49 | 50 | File file = new File(path); 51 | 52 | if (file.isDirectory() && file.exists() == true) { 53 | throw new RuntimeException("Not exisiting file supported only!"); 54 | } 55 | throw new RuntimeException("not yet implemented"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/statistics/CrashAbnormalityAdapter.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.statistics; 2 | 3 | import com.github.monkey.analyzer.model.Abnormality; 4 | 5 | /** 6 | * This is an adapter Object of CRASH abnormality which split the abnormality 7 | * message.
8 | *
9 | * The code lines of a CRASH abnormality is chosen as the main key of an 10 | * abnormality. 11 | * 12 | * @author Alex Chen (apack1001@gmail.com) 13 | * 14 | */ 15 | public class CrashAbnormalityAdapter { 16 | /** 17 | * a brief of Crash Abnormality 18 | */ 19 | private String shortMessage = null; 20 | /** 21 | * code lines of a Crash Abnormality 22 | */ 23 | private String codeLines = null; 24 | 25 | /** 26 | * instantiate from an abnormality 27 | * 28 | * @param abnormality 29 | * instance of an abnormality 30 | */ 31 | public CrashAbnormalityAdapter(final Abnormality abnormality) { 32 | final String rawMessage = abnormality.getMessage(); 33 | String[] messages = rawMessage.split("\\n"); 34 | if (messages == null || messages.length == 0) 35 | throw new RuntimeException(); 36 | 37 | StringBuilder lines = new StringBuilder(); 38 | for (String message : messages) { 39 | if (message.contains("Short Msg:")) 40 | setShortMessage(message.substring(14)); 41 | else if (message.contains("at")) { 42 | lines.append(message); 43 | lines.append("\n"); 44 | } 45 | 46 | setCodeLines(lines.toString()); 47 | } 48 | } 49 | 50 | public void setShortMessage(final String shortMessage) { 51 | this.shortMessage = shortMessage; 52 | } 53 | 54 | public void setCodeLines(final String codeLines) { 55 | this.codeLines = codeLines; 56 | } 57 | 58 | public String getShortMessage() { 59 | return shortMessage; 60 | } 61 | 62 | public String getCodeLines() { 63 | return codeLines; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/analyze/NativeCrashAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.analyze; 2 | 3 | import java.io.File; 4 | import java.util.LinkedList; 5 | import java.util.regex.Pattern; 6 | 7 | import com.github.monkey.analyzer.model.Abnormality; 8 | import com.github.monkey.analyzer.model.AbnormalityType; 9 | 10 | class NativeCrashAnalyzer extends Analyzer { 11 | public NativeCrashAnalyzer(AnalyzerConfiguration config) { 12 | super(config); 13 | } 14 | 15 | @Override 16 | protected void onMatch(Abnormality abnormality, 17 | StringBuilder monkeyMsg, LinkedList logcatMsgBefore, 18 | LinkedList logcatMsgAfter) { 19 | if (monkeyMsg != null && !monkeyMsg.equals("")) { 20 | abnormality.setValid(true); 21 | } else { 22 | abnormality.setValid(false); 23 | } 24 | 25 | String logcatFilePath = filePath + File.separator 26 | + mConfig.getLogcatFileName(); 27 | File file = new File(logcatFilePath); 28 | 29 | if (file == null || false == file.exists()) 30 | return; 31 | 32 | final Pattern startPattern = Pattern.compile("I DEBUG : \\*\\*\\*"); 33 | final Pattern endPattern = Pattern.compile("I DEBUG : \\*\\*\\*"); 34 | final int beforeSize = 50; 35 | final int afterSize = -1; 36 | StringBuilder logcatMsg = new StringBuilder(); 37 | boolean matching = parse(file, logcatMsgBefore, logcatMsgAfter, 38 | logcatMsg, startPattern, endPattern, beforeSize, afterSize); 39 | if (matching) { 40 | StringBuilder msgAfter = new StringBuilder(); 41 | for (String msg : logcatMsgAfter) { 42 | msgAfter.append(msg); 43 | msgAfter.append("\n"); 44 | } 45 | StringBuilder msgBefore = new StringBuilder(); 46 | for (String msg : logcatMsgBefore) { 47 | msgBefore.append(msg); 48 | msgBefore.append("\n"); 49 | } 50 | StringBuilder message = new StringBuilder(); 51 | message.append(monkeyMsg.toString()); 52 | message.append('\n'); 53 | message.append(msgBefore.toString()); 54 | message.append(logcatMsg.toString()); 55 | message.append(msgAfter.toString()); 56 | 57 | abnormality.setType(AbnormalityType.NATIVE); 58 | abnormality.setMessage(message); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/Main.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | 7 | import com.github.monkey.analyzer.analyze.AnalyzerClient; 8 | import com.github.monkey.analyzer.analyze.Constants; 9 | import com.github.monkey.analyzer.model.Abnormality; 10 | import com.github.monkey.analyzer.report.Abnormalities2JSONReport; 11 | import com.github.monkey.analyzer.report.JSONReport2HtmlReport; 12 | import com.github.monkey.analyzer.statistics.AbnormalitiesAnalyzerWrapper; 13 | 14 | /** 15 | * 16 | * @author Alex Chen (apack1001@gmail.com) 17 | * 18 | */ 19 | public class Main { 20 | public static void main(String[] args) { 21 | CLIParser cli = new CLIParser(); 22 | boolean success = cli.parse(args); 23 | if (false == success) 24 | return; 25 | 26 | for (String dir : cli.workspaces) { 27 | AnalyzerClient client = new AnalyzerClient(); 28 | 29 | client.analyze(dir, 30 | cli.monkeyLogFileName, 31 | cli.bugreportFileName, 32 | cli.tracesFileName, 33 | cli.logcatFileName, 34 | cli.propertiesName, 35 | cli.pkgName 36 | ); 37 | 38 | final ArrayList knownAbnormalities = client.getKnownAbnormalities(); 39 | final ArrayList unknownAbnormalities = client 40 | .getUnknownAbnormalities(); 41 | 42 | final int count = client.getAbnormalitiesDirectoriesCount(dir); 43 | double duration = 0; 44 | try { 45 | duration = Double.parseDouble(cli.duration); 46 | } catch (Exception e) { 47 | e.printStackTrace(); 48 | } 49 | 50 | double avg = AbnormalitiesAnalyzerWrapper.getAverage( 51 | knownAbnormalities, 52 | unknownAbnormalities, 53 | duration 54 | ); 55 | HashMap info = fillTestInfo(avg, cli.duration); 56 | 57 | String result = Abnormalities2JSONReport.toJSONFormatStringReport( 58 | knownAbnormalities, 59 | unknownAbnormalities, 60 | info, 61 | duration, 62 | count); 63 | 64 | JSONReport2HtmlReport.toHTMLReport(result, 65 | dir + File.separator + "index.html", "gbk"); 66 | JSONReport2HtmlReport.toHTMLReport(result, 67 | dir + File.separator + "index_utf8.html", "utf-8"); 68 | } 69 | 70 | } 71 | 72 | private static HashMap fillTestInfo(double average, String duration) { 73 | HashMap hm = new HashMap(); 74 | hm.put(Constants.JSONReport.KEY_DURATION, duration); 75 | hm.put(Constants.JSONReport.KEY_AVERAGE, "" + average); 76 | return hm; 77 | } 78 | } -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/analyze/Constants.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.analyze; 2 | 3 | final public class Constants { 4 | /** 5 | * This class defines the keys of major displaying information 6 | * 7 | * @author herongtian 8 | * @author Alex Chen (apack1001@gmail.com) 9 | * 10 | */ 11 | public class JSONReport { 12 | public static final String KEY_PRODUCT_NAME = "productName"; 13 | public static final String KEY_VERSION = "version"; 14 | public static final String KEY_START_TIME = "startTime"; 15 | public static final String KEY_END_TIME = "endTime"; 16 | public static final String KEY_DURATION = "duration"; 17 | public static final String KEY_AVERAGE = "average"; 18 | public static final String KEY_EXECUTOR = "executor"; 19 | public static final String KEY_MOBILE_PLATFORM = "mobilePlatform"; 20 | public static final String KEY_MOBILE_PHONE_TYPE = "mobile"; 21 | public static final String KEY_ANR_NUMBER = "anrNumber"; 22 | public static final String KEY_CRASH_NUMBER = "crashNumber"; 23 | public static final String KEY_NATIVE_CRASH_NUMBER = "nativeCrashNumber"; 24 | 25 | public static final String KEY_ABNORMALITIS = "abnormalitis"; 26 | public static final String KEY_ABNORMALITIS_TYPE = "type"; 27 | public static final String KEY_ABNORMALITIS_MSG = "msg"; 28 | public static final String KEY_ABNORMALITIS_SHORT_MSG = "shortMsg"; 29 | public static final String KEY_ABNORMALITIS_TRACE_URL = "traceUrl"; 30 | public static final String KEY_MONKEY_LOG_URL = "monkeyLogUrl"; 31 | public static final String KEY_ABNORMALITIS_LOGCAT_URL = "logcatUrl"; 32 | public static final String KEY_ABNORMALITIS_BUGREPORT_URL = "bugreportUrl"; 33 | public static final String KEY_ABNORMALITIES_ZIP_URL = "zipUrl"; 34 | public static final String KEY_ABNORMALITIS_COUNT = "count"; 35 | public static final String KEY_ABNORMALITIS_DURATIONS = "durations"; 36 | 37 | } 38 | 39 | /** 40 | * This class defines the keys of an abnormality 41 | * 42 | * @author Alex Chen (apack1001@gmail.com) 43 | * 44 | */ 45 | public class AbnormalityProperties { 46 | public static final String KEY_SERIAL = "serialNumber"; 47 | public static final String KEY_START_TIME = "startTime"; 48 | public static final String KEY_END_TIME = "endTime"; 49 | public static final String KEY_DURATION_STRING = "duration"; 50 | public static final String KEY_DURATION_LONG = "longTypeDuration"; 51 | public static final String KEY_PLATFORM = "platform"; 52 | public static final String KEY_PACKAGE_NAME = "pkgName"; 53 | public static final String KEY_PACKAGE_VERSION = "pkgVersion"; 54 | public static final String KEY_PATHS = "path"; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/CLIParser.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.runner; 2 | 3 | import org.apache.commons.cli.CommandLine; 4 | import org.apache.commons.cli.CommandLineParser; 5 | import org.apache.commons.cli.GnuParser; 6 | import org.apache.commons.cli.HelpFormatter; 7 | import org.apache.commons.cli.Options; 8 | import org.apache.commons.cli.ParseException; 9 | 10 | /** 11 | * 12 | * @author Alex Chen (apack1001@gmail.com) 13 | * 14 | */ 15 | public class CLIParser { 16 | 17 | public String [] deivcesId; 18 | public String singleDuration; 19 | public String seriesDuration; 20 | public String pkgName; 21 | public String user; 22 | public String pkgPath; 23 | public String pkgVersion; 24 | public String unlockCmd; 25 | 26 | public boolean parse(String [] args) { 27 | Options options = new Options(); 28 | 29 | options.addOption("d", "device-id", true, "the id list of the devices which is need to run monkey test"); 30 | options.addOption("r", "user-name", true, "user name of this job owner"); 31 | options.addOption("v", "pkg-version", true, "version of this application"); 32 | options.addOption("n", "pkg-name", true, "package name of this appliacation"); 33 | options.addOption("p", "pkg-path", true, "point to an Android application path in the storage"); 34 | options.addOption("t", "series-duration", true, "expected total monkey jobs duration (hour)"); 35 | options.addOption("s", "single-duration", true, "expected one monkey job duration (hour)"); 36 | options.addOption("u", "unlock-cmd-path", true, "point to an unlock script path which must be standalone executable"); 37 | options.addOption("h", "help", false, "Output help information!"); 38 | 39 | String formatstr = "java -jar jarfile [-options/ --options]...\n"; 40 | String headerstr = "options are as below:"; 41 | 42 | CommandLineParser parser = new GnuParser(); 43 | HelpFormatter formatter = new HelpFormatter(); 44 | CommandLine cmd = null; 45 | 46 | try { 47 | cmd = parser.parse( options, args ); 48 | } catch (ParseException e) { 49 | formatter.printHelp(formatstr, headerstr, options, ""); 50 | return false; 51 | } 52 | 53 | if (cmd == null || cmd.hasOption("h") || cmd.getOptions().length == 0) { 54 | formatter.printHelp(formatstr, options); 55 | return false; 56 | } 57 | if (cmd.hasOption("d")) { 58 | this.deivcesId = cmd.getOptionValues("d"); 59 | } 60 | if (cmd.hasOption("r")) { 61 | this.user = cmd.getOptionValue("r"); 62 | } 63 | if (cmd.hasOption("u")) { 64 | this.unlockCmd = cmd.getOptionValue("u"); 65 | } 66 | if (cmd.hasOption("v")) { 67 | this.pkgVersion = cmd.getOptionValue("v"); 68 | } 69 | if (cmd.hasOption("n")) { 70 | this.pkgName = cmd.getOptionValue("n"); 71 | } 72 | if (cmd.hasOption("p")) { 73 | this.pkgPath = cmd.getOptionValue("p"); 74 | } 75 | if (cmd.hasOption("t")) { 76 | this.seriesDuration = cmd.getOptionValue("t"); 77 | } 78 | if (cmd.hasOption("s")) { 79 | this.singleDuration = cmd.getOptionValue("s"); 80 | } 81 | if (cmd.hasOption("u")) { 82 | this.unlockCmd = cmd.getOptionValue("u"); 83 | } 84 | return true; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/model/Abnormality.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Map.Entry; 6 | import java.util.Set; 7 | 8 | /** 9 | * This class define the interface of an abnormality.
10 | *
11 | * For example, running monkey test may introduce CRASH or ANR in the target 12 | * application to be tested.
13 | *
14 | * 15 | * Developer or maintainer of this tools should inherit this abstract 16 | * class 17 | * 18 | * @author Alex Chen (apack1001@gmail.com) 19 | * 20 | */ 21 | public abstract class Abnormality { 22 | protected String message = ""; 23 | protected AbnormalityType type = AbnormalityType.UNKNOWN; 24 | protected HashMap extraInfo = new HashMap(); 25 | protected boolean isValid = false; 26 | 27 | public static final String EXTRAS_KEY_BEFORE_START_PATTERN = "before"; 28 | public static final String EXTRAS_KEY_AFTER_END_PATTERN = "after"; 29 | public static final String EXTRAS_KEY_PATH = "path"; 30 | /** 31 | * set the type of abnormality 32 | * 33 | */ 34 | public abstract void setType(final AbnormalityType type); 35 | 36 | /** 37 | * get the type of abnormality 38 | * 39 | * @return instance of FilterType 40 | */ 41 | public abstract AbnormalityType getType(); 42 | 43 | /** 44 | * set whether this Abnormality is Valid 45 | * 46 | * @param isValid 47 | * whether this Abnormality is Valid 48 | */ 49 | 50 | public void setValid(boolean isValid) { 51 | this.isValid = isValid; 52 | } 53 | 54 | /** 55 | * tell user whether the abnormality is valid 56 | * 57 | * @return true if valid, else return invalid 58 | */ 59 | public abstract boolean isValid(); 60 | 61 | /** 62 | * set the main message of this abnormality file, it is usually monkey log 63 | * 64 | * @return abnormality message 65 | */ 66 | final public void setMessage(StringBuilder sb) { 67 | if (sb == null) 68 | message = ""; 69 | else 70 | message = sb.toString(); 71 | } 72 | 73 | /** 74 | * the main message of this abnormality file, it is usually monkey log 75 | * 76 | * @return abnormality message 77 | */ 78 | public String getMessage() { 79 | return message; 80 | } 81 | 82 | /** 83 | * extra message of this abnormality file, not in use now. it is used for 84 | * extension 85 | * 86 | * @return extra information of this abnormality 87 | */ 88 | final public HashMap getExtras() { 89 | return extraInfo; 90 | } 91 | 92 | /** 93 | * put one entry to extras container 94 | */ 95 | final public String put(String key, String value) { 96 | return extraInfo.put(key, value); 97 | } 98 | /** 99 | * copy from an 100 | */ 101 | final public void copyFrom(Map extras) { 102 | if (extras == null || extras.isEmpty()) 103 | return; 104 | 105 | Set> entries = extras.entrySet(); 106 | 107 | if (entries == null || entries.isEmpty()) 108 | return; 109 | 110 | for (Entry entry : entries) { 111 | extraInfo.put(entry.getKey(), entry.getValue()); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/CLIParser.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer; 2 | 3 | import org.apache.commons.cli.CommandLine; 4 | import org.apache.commons.cli.CommandLineParser; 5 | import org.apache.commons.cli.GnuParser; 6 | import org.apache.commons.cli.HelpFormatter; 7 | import org.apache.commons.cli.Option; 8 | import org.apache.commons.cli.OptionBuilder; 9 | import org.apache.commons.cli.Options; 10 | import org.apache.commons.cli.ParseException; 11 | import org.apache.commons.cli.PosixParser; 12 | 13 | /** 14 | * 15 | * @author Alex Chen (apack1001@gmail.com) 16 | * 17 | */ 18 | public class CLIParser { 19 | public String[] workspaces; 20 | public String monkeyLogFileName; 21 | public String bugreportFileName; 22 | public String tracesFileName; 23 | public String logcatFileName; 24 | public String propertiesName; 25 | public String pkgName; 26 | public String duration; 27 | 28 | public boolean parse(String [] args) { 29 | Options options = new Options(); 30 | 31 | @SuppressWarnings("static-access") 32 | Option workspaces = OptionBuilder.withArgName("w") 33 | .withLongOpt("workspaces") 34 | .hasArgs() 35 | .withDescription("Workspace of monkey running directoy.") 36 | .isRequired() 37 | .create("w"); 38 | options.addOption(workspaces); 39 | options.addOption("d", "duration", true, "Expected uration of single monkey job(8 hours or 4.5 hours)."); 40 | options.addOption("m", "monkey-log-file-name", true, "File name of monkey log."); 41 | options.addOption("l", "logcat-log-file-name", true, "File name of logcat log."); 42 | options.addOption("t", "traces-log-file-name", true, "File name of traces log."); 43 | options.addOption("b", "bugreport-log-file-name", true, "File name of bugreport log."); 44 | options.addOption("p", "properties-file-name", true, "File name of each monkey running summary."); 45 | options.addOption("n", "package-name", true, "Package name of an Android Application."); 46 | options.addOption("h", "help", false, "Output help information!"); 47 | 48 | String formatstr = "java -jar jarfile [-options/ --options]...\n"; 49 | String headerstr = "options are as below:"; 50 | 51 | CommandLineParser parser = new GnuParser(); 52 | HelpFormatter formatter = new HelpFormatter(); 53 | CommandLine cmd = null; 54 | 55 | try { 56 | cmd = parser.parse( options, args ); 57 | } catch (ParseException e) { 58 | formatter.printHelp(formatstr, headerstr, options, ""); 59 | return false; 60 | } 61 | 62 | if (cmd == null || cmd.hasOption("h") || cmd.getOptions().length == 0) { 63 | formatter.printHelp(formatstr, options); 64 | return false; 65 | } 66 | if (cmd.hasOption("w")) { 67 | this.workspaces = cmd.getOptionValues("w"); 68 | } 69 | if (cmd.hasOption("d")) { 70 | this.duration = cmd.getOptionValue("d"); 71 | } 72 | if (cmd.hasOption("m")) { 73 | this.monkeyLogFileName = cmd.getOptionValue("m"); 74 | } 75 | if (cmd.hasOption("l")) { 76 | this.logcatFileName = cmd.getOptionValue("l"); 77 | } 78 | if (cmd.hasOption("t")) { 79 | this.tracesFileName = cmd.getOptionValue("t"); 80 | } 81 | if (cmd.hasOption("p")) { 82 | this.propertiesName = cmd.getOptionValue("p"); 83 | } 84 | if (cmd.hasOption("n")) { 85 | this.pkgName = cmd.getOptionValue("n"); 86 | } 87 | return true; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/helper/DeviceConnectHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.runner.helper; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import com.android.ddmlib.AndroidDebugBridge; 8 | import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; 9 | import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; 10 | import com.android.ddmlib.IDevice.DeviceState; 11 | import com.android.ddmlib.IDevice; 12 | import com.github.monkey.runner.scheduler.Console; 13 | 14 | public final class DeviceConnectHelper implements IDebugBridgeChangeListener, IDeviceChangeListener{ 15 | private List devices = Collections 16 | .synchronizedList(new ArrayList()); 17 | private String id = null; 18 | 19 | public static void init(final String adbLocation) { 20 | AndroidDebugBridge.init(false); 21 | AndroidDebugBridge.createBridge(adbLocation, true); 22 | 23 | } 24 | 25 | public IDevice getConnecedDevice(final String serialNumber) { 26 | for (IDevice device : AndroidDebugBridge.getBridge().getDevices()) { 27 | if (serialNumber.equals(device.getSerialNumber()) 28 | && device.getState() == DeviceState.ONLINE) { 29 | return device; 30 | } 31 | } 32 | return null; 33 | } 34 | 35 | public IDevice waitForDeviceConnected(final String serialNumber, final long timeout) throws InterruptedException { 36 | id = serialNumber; 37 | Console.printLogMessage(serialNumber, "waitForDeviceConnected(" + serialNumber + " to be connected start"); 38 | 39 | IDevice [] devices = AndroidDebugBridge.getBridge().getDevices(); 40 | if (devices != null) { 41 | for (IDevice device : devices) { 42 | if (device.getSerialNumber() == serialNumber) { 43 | Console.printLogMessage("", device.getSerialNumber()); 44 | return device; 45 | } 46 | } 47 | } 48 | 49 | AndroidDebugBridge.addDebugBridgeChangeListener(DeviceConnectHelper.this); 50 | AndroidDebugBridge.addDeviceChangeListener(DeviceConnectHelper.this); 51 | synchronized (this) { 52 | while (getConnecedDevice(serialNumber) == null) { 53 | Console.printLogMessage("", "wait"); 54 | wait(60000); 55 | } 56 | } 57 | Console.printLogMessage(serialNumber, "waitForDeviceConnected(" + serialNumber + " to be connected end"); 58 | 59 | return getConnecedDevice(serialNumber); 60 | } 61 | 62 | @Override 63 | public void deviceChanged(IDevice arg0, int arg1) { 64 | // Console.printLogMessage(""arg0.getSerialNumber()); 65 | 66 | } 67 | 68 | @Override 69 | public void deviceConnected(IDevice device) { 70 | Console.printLogMessage(device.getSerialNumber(), "device [" + device.getSerialNumber() + "] connected"); 71 | 72 | if (device.getSerialNumber().equals(id)) { 73 | Console.printLogMessage(id, "device [" + device.getSerialNumber() + "] matching expected! "); 74 | Console.printLogMessage(id, "notify"); 75 | synchronized (this) { 76 | devices.add(device); 77 | notifyAll(); 78 | } 79 | } 80 | 81 | } 82 | 83 | @Override 84 | public void deviceDisconnected(IDevice device) { 85 | synchronized (devices) { 86 | synchronized (this) { 87 | devices.remove(device); 88 | } 89 | } 90 | } 91 | 92 | @Override 93 | public void bridgeChanged(AndroidDebugBridge bridge) { 94 | 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/analyze/AnalyzerInvoker.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.analyze; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import com.github.monkey.analyzer.model.Abnormality; 8 | import com.github.monkey.analyzer.model.InvalidAbnormality; 9 | 10 | /** 11 | * 12 | * @author Alex Chen (apack1001@gmail.com) 13 | * 14 | */ 15 | class AnalyzerInvoker { 16 | /** 17 | * The context of all analyzers, it stores all the Commands 18 | */ 19 | protected List analyzers = Collections 20 | .synchronizedList(new ArrayList()); 21 | /** 22 | * The target monkey result file to be analyzed 23 | */ 24 | protected List paths = Collections 25 | .synchronizedList(new ArrayList()); 26 | 27 | /** 28 | * All the known results 29 | */ 30 | protected ArrayList knownAbnormalities = new ArrayList(); 31 | 32 | /** 33 | * All the unknown results 34 | */ 35 | protected ArrayList unknownAbnormalties = new ArrayList(); 36 | 37 | /** 38 | * Traverse the command iteration an 39 | */ 40 | public final void analyze() { 41 | if (analyzers.size() == 0 || paths.size() == 0) 42 | return; 43 | 44 | for (String path : paths) { 45 | // Matching! Add it to the known abnormalities 46 | boolean matching = false; 47 | for (IAnalyzer analyzer : analyzers) { 48 | analyzer.setPath(path); 49 | Abnormality abnormality = analyzer.toAbnormality(); 50 | if (abnormality.isValid()) { 51 | knownAbnormalities.add(abnormality); 52 | matching = true; 53 | break; 54 | } 55 | } 56 | 57 | // Not matching! Add it to the unknown abnormalities 58 | if (false == matching) { 59 | Abnormality invalidAbnormality = null; 60 | try { 61 | invalidAbnormality = InvalidAbnormality.class.newInstance(); 62 | if (analyzers.size() > 0) 63 | invalidAbnormality.copyFrom(analyzers.get(0) 64 | .toAbnormality().getExtras()); 65 | 66 | if (invalidAbnormality != null) 67 | unknownAbnormalties.add(invalidAbnormality); 68 | 69 | } catch (InstantiationException e) { 70 | e.printStackTrace(); 71 | } catch (IllegalAccessException e) { 72 | e.printStackTrace(); 73 | } 74 | 75 | 76 | } 77 | matching = false; 78 | } 79 | } 80 | 81 | final public void registerAnalyzer(IAnalyzer analyzer) { 82 | analyzers.add(analyzer); 83 | } 84 | 85 | /** 86 | * add one monkey result path to be analyzed 87 | * 88 | * @param path 89 | * monkey result directory parent path 90 | */ 91 | final public void addPath(String path) { 92 | if (path == null || path == "") 93 | throw new IllegalArgumentException("Invalid file path"); 94 | 95 | paths.add(path); 96 | } 97 | 98 | /** 99 | * Get all the known kind of abnormalities which are unprocessed.
100 | * So duplicated result may exist
101 | * 102 | * @return all the known kind of abnormalities 103 | */ 104 | final public ArrayList getKnownAbnormalities() { 105 | return knownAbnormalities; 106 | } 107 | 108 | /** 109 | * Get all the unknown kind of abnormalities.
110 | * 111 | * @return all the unknown kind of abnormalities 112 | */ 113 | final public ArrayList getUnknownAbnormalities() { 114 | return unknownAbnormalties; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AndroidMonkeyAdapter 2 | ==================== 3 | Manage monkey jobs and analyze all the running logs that will determine which kind of abnormality a crash belongs to 4 | Introduction 5 | -------------------- 6 | 7 | - Install Android application, run Monkey testing job, analyze and classify crash automatically 8 | - Dalvik Crash, ANR, Native Crash supported 9 | - Dulplicated abnormality classification 10 | - HTML-format report supported 11 | - Multiple platform supported(Windows, Mac, Linux) 12 | - Jenkins platform supported 13 | 14 | Manual 15 | -------------------- 16 | Usage of Android Monkey Adapter runner 17 | ``` sh 18 | usage: java -jar jarfile [-options/ --options]... 19 | 20 | -d,--device-id the id list of the devices which is need to 21 | run monkey test 22 | -h,--help Output help information! 23 | -n,--pkg-name package name of this appliacation 24 | -p,--pkg-path point to an Android application path in the 25 | storage 26 | -r,--user-name user name of this job owner 27 | -s,--single-duration expected one monkey job duration (hour) 28 | -t,--series-duration expected total monkey jobs duration (hour) 29 | -u,--unlock-cmd-path point to an unlock script path which must be 30 | standalone executable 31 | -v,--pkg-version version of this application 32 | 33 | ``` 34 | Usage of Android Monkey Adapter analyzer 35 | ``` sh 36 | usage: java -jar jarfile [-options/ --options]... 37 | 38 | options are as below: 39 | -b,--bugreport-log-file-name File name of bugreport log. 40 | -d,--duration Expected uration of single monkey 41 | job(8 hours or 4.5 hours). 42 | -h,--help Output help information! 43 | -l,--logcat-log-file-name File name of logcat log. 44 | -m,--monkey-log-file-name File name of monkey log. 45 | -n,--package-name Package name of an Android 46 | Application. 47 | -p,--properties-file-name File name of each monkey running 48 | summary. 49 | -t,--traces-log-file-name File name of traces log. 50 | -w,--workspaces Workspace of monkey running 51 | directoy. 52 | ``` 53 | 54 | Example 55 | ``` sh 56 | java -jar monkey-adapter-runner.jar --device-id 45071c540c04197 --user-name xxxxxx --pkg-path ./example.apk --pkg-name com.example --pkg-version 3.0 --single-duration 8 --series-duration 8 57 | java -jar monkey-adapter-analyzer.jar --workspaces ./logs/ --monkey-log-file-name monkey_log.txt --logcat-log-file-name logcat_log.txt --traces-log-file-name traces_log.txt --bugreport-log-file-name bugreport_log.txt --properties-file-name properties.txt --duration 8 --package-name com.example 58 | ``` 59 | 60 | Get Started! 61 | --------------------- 62 | Here is a easier example of using this tool which encapsulates the jarfile operations. 63 | ``` sh 64 | git clone https://github.com/apack1001/Android-Monkey-Adapter/ 65 | cd Android-Monkey-Adapter/dist/ 66 | # executing encapsulated script 67 | sh run_monkey_mac.sh 68 | # example: sh run_monkey_mac.sh emulator-5554 alex example.apk com.example 1.0 8 69 | 70 | ``` 71 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/Main.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.runner; 2 | 3 | import com.android.ddmlib.AndroidDebugBridge; 4 | import com.android.ddmlib.IDevice; 5 | import com.github.monkey.runner.helper.DeviceConnectHelper; 6 | import com.github.monkey.runner.scheduler.Console; 7 | import com.github.monkey.runner.scheduler.MonkeyTestDevice; 8 | import com.github.monkey.runner.scheduler.MonkeyTestDeviceFactory; 9 | import com.github.monkey.runner.scheduler.MonkeyTestSeries; 10 | import com.github.monkey.runner.scheduler.MonkeyTestSeriesFactory; 11 | 12 | import java.io.File; 13 | import java.util.ArrayList; 14 | 15 | public class Main { 16 | public static void main(String[] args) { 17 | String initFileName = ""; 18 | String rawCommand = "monkey --throttle 300 -c android.intent.category.MONKEY -c android.intent.category.LAUNCHER -c android.intent.category.DEFAULT --monitor-native-crashes --kill-process-after-error --pct-touch 43 --pct-motion 30 --pct-majornav 10 --pct-syskeys 15 --pct-appswitch 2 -v -v -v 99999"; 19 | 20 | CLIParser cli = new CLIParser(); 21 | boolean success = cli.parse(args); 22 | if (false == success) 23 | return; 24 | 25 | File adbFile = new File(getADB()); 26 | if (false == adbFile.exists()) 27 | throw new RuntimeException("ADB is not exist!\nCurrent Localtion is " + getADB()); 28 | DeviceConnectHelper.init(getADB()); 29 | try { 30 | ArrayList serieslist = new ArrayList(); 31 | DeviceConnectHelper deviceHelper = new DeviceConnectHelper(); 32 | 33 | for (String serialNumber : cli.deivcesId) { 34 | IDevice device = deviceHelper.getConnecedDevice(serialNumber); 35 | if (device == null) 36 | device = deviceHelper.waitForDeviceConnected(serialNumber, 60000); 37 | 38 | if (device == null) { 39 | Console.printLogMessage(serialNumber, "device " + serialNumber + "cannot be connected"); 40 | continue; 41 | } 42 | 43 | MonkeyTestDevice monkeyTestDevice = MonkeyTestDeviceFactory 44 | .newDevice(device); 45 | monkeyTestDevice.install(cli.pkgPath); 46 | 47 | MonkeyTestSeries series = MonkeyTestSeriesFactory.newSeries( 48 | monkeyTestDevice, 49 | cli.pkgName, 50 | cli.pkgVersion, 51 | cli.pkgPath, 52 | rawCommand, 53 | cli.user, 54 | initFileName, 55 | (long) (1000 * 3600 * Float.parseFloat(cli.seriesDuration)), 56 | (long) (1000 * 3600 * Float.parseFloat(cli.singleDuration))); 57 | series.start(); 58 | serieslist.add(series); 59 | } 60 | 61 | // Wait until the monkey series completed! 62 | for (MonkeyTestSeries series: serieslist) 63 | series.mExecutor.join(); 64 | 65 | } catch (Throwable e) { 66 | e.printStackTrace(); 67 | } finally { 68 | AndroidDebugBridge.terminate(); 69 | } 70 | 71 | System.exit(0); 72 | } 73 | 74 | static boolean isMac() { 75 | return getPlatform().startsWith("Mac OS"); 76 | } 77 | 78 | static boolean isWindows() { 79 | return getPlatform().startsWith("Windows"); 80 | } 81 | 82 | static boolean isLinux() { 83 | return getPlatform().startsWith("Linux"); 84 | } 85 | 86 | private static String getPlatform() { 87 | return System.getProperty("os.name"); 88 | } 89 | 90 | private static String getADB() { 91 | if (isWindows()) { 92 | return "." + File.separator + "adb" + File.separator + getPlatform().split(" ")[0] + File.separator + "adb.exe"; 93 | } else if (isMac() || isLinux()){ 94 | return new File("").getAbsolutePath() + File.separator + "adb" + File.separator + getPlatform().split(" ")[0] + File.separator + "adb"; 95 | } else { 96 | throw new RuntimeException("not yet implement"); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/analyze/AnalyzerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.analyze; 2 | 3 | import com.github.monkey.analyzer.model.AbnormalityType; 4 | 5 | /** 6 | * Configuration of an abnormality analyzer(implemented using a Builder 7 | * pattern) 8 | * 9 | * @author Alex Chen (apack1001@gmail.com) 10 | * 11 | */ 12 | class AnalyzerConfiguration { 13 | private String startPattern = ""; 14 | private String endPattern = ""; 15 | private AbnormalityType type = null; 16 | private int beforeSize = 0; 17 | private int afterSize = 0; 18 | private String packageName; 19 | private String monkeyLogFileName; 20 | private String bugreportFileName; 21 | private String tracesFileName; 22 | private String logcatFileName; 23 | private String propertiesFileName; 24 | 25 | private AnalyzerConfiguration(String startPattern, String endPattern, 26 | AbnormalityType type, int beforeSize, int afterSize, 27 | String packageName, String monkeyLogFielName, 28 | String bugreportFileName, String tracesFileName, 29 | String logcatFileName, String propertiesName) { 30 | this.startPattern = startPattern; 31 | this.endPattern = endPattern; 32 | this.type = type; 33 | this.beforeSize = beforeSize; 34 | this.afterSize = afterSize; 35 | this.packageName = packageName; 36 | this.monkeyLogFileName = monkeyLogFielName; 37 | this.bugreportFileName = bugreportFileName; 38 | this.tracesFileName = tracesFileName; 39 | this.logcatFileName = logcatFileName; 40 | this.propertiesFileName = propertiesName; 41 | } 42 | 43 | public String getStartPattern() { 44 | return startPattern; 45 | } 46 | 47 | public String getEndPattern() { 48 | return endPattern; 49 | } 50 | 51 | public AbnormalityType getType() { 52 | return type; 53 | } 54 | 55 | public int getBeforeSize() { 56 | return beforeSize; 57 | } 58 | 59 | public int getAfterSize() { 60 | return afterSize; 61 | } 62 | 63 | public String getPackageName() { 64 | return packageName; 65 | } 66 | 67 | public String getBugreportFileName() { 68 | return bugreportFileName; 69 | } 70 | 71 | public String getTracesFileName() { 72 | return tracesFileName; 73 | } 74 | 75 | public String getLogcatFileName() { 76 | return logcatFileName; 77 | } 78 | 79 | public String getMonkeyLogFileName() { 80 | return monkeyLogFileName; 81 | } 82 | 83 | public String getPropertiesFileName() { 84 | return propertiesFileName; 85 | } 86 | 87 | public static class Builder { 88 | private String startPattern = ""; 89 | private String endPattern = ""; 90 | private AbnormalityType type = null; 91 | private int beforeSize = 0; 92 | private int afterSize = 0; 93 | private String monkeyLogFileName; 94 | private String bugreportFileName; 95 | private String tracesFileName; 96 | private String logcatFileName; 97 | private String packageName; 98 | private String propertiesName; 99 | 100 | public Builder setStartPattern(String pattern) { 101 | startPattern = pattern; 102 | return this; 103 | } 104 | 105 | public Builder setEndPattern(String pattern) { 106 | endPattern = pattern; 107 | return this; 108 | } 109 | 110 | public Builder setType(final AbnormalityType type) { 111 | this.type = type; 112 | return this; 113 | } 114 | 115 | public Builder setContentLengthBeforeStartPattern(int size) { 116 | beforeSize = size; 117 | return this; 118 | } 119 | 120 | public Builder setContentLengthAfterEndPattern(int size) { 121 | afterSize = size; 122 | return this; 123 | } 124 | 125 | public Builder setPackageName(final String packageName) { 126 | this.packageName = packageName; 127 | return this; 128 | } 129 | 130 | public Builder setBugReportFileName(final String bugreportFileName) { 131 | this.bugreportFileName = bugreportFileName; 132 | return this; 133 | } 134 | 135 | public Builder setTracesFileName(final String tracesFileName) { 136 | this.tracesFileName = tracesFileName; 137 | return this; 138 | } 139 | 140 | public Builder setMonkeyLogFileName(final String monkeyLogFileName) { 141 | this.monkeyLogFileName = monkeyLogFileName; 142 | return this; 143 | } 144 | 145 | public Builder setLogcatFileName(final String logcatFileName) { 146 | this.logcatFileName = logcatFileName; 147 | return this; 148 | } 149 | 150 | public Builder setPropertiesFileName(final String propertiesName) { 151 | this.propertiesName = propertiesName; 152 | return this; 153 | } 154 | 155 | public AnalyzerConfiguration build() { 156 | return new AnalyzerConfiguration(startPattern, endPattern, type, 157 | beforeSize, afterSize, packageName, monkeyLogFileName, 158 | bugreportFileName, tracesFileName, logcatFileName, 159 | propertiesName); 160 | } 161 | 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /dist/logs/c6103ba0-5c61-4a18-abd9-ec3acbaf23cd/properties.txt: -------------------------------------------------------------------------------- 1 | 2 | [monkey] 3 | apk.package=com.example 4 | test.finish=2014-03-31 19:48:03 5 | series.id=9318aac0-5e1f-4172-994b-60b58b3e7e66 6 | status=FAIL 7 | apk.version=1.0 8 | test.duration.expect=180 9 | test.start=2014-03-31 19:48:00 10 | executor=alex 11 | test.duration.actual=3 12 | test.id=c6103ba0-5c61-4a18-abd9-ec3acbaf23cd 13 | command=monkey -p com.example -s 1331320713742931433 --throttle 300 -c android.intent.category.MONKEY -c android.intent.category.LAUNCHER -c android.intent.category.DEFAULT --monitor-native-crashes --kill-process-after-error --pct-touch 43 --pct-motion 30 --pct-majornav 10 --pct-syskeys 15 --pct-appswitch 2 -v -v -v 99999 14 | series.duration.expect=180 15 | 16 | [device] 17 | ro.product.manufacturer=unknown 18 | gsm.sim.operator.alpha=Android 19 | ro.kernel.ndns=2 20 | ro.debuggable=1 21 | init.svc.debuggerd=running 22 | ro.product.model=sdk 23 | gsm.operator.iso-country=us 24 | persist.sys.language=en 25 | ro.HEAVY_WEIGHT_APP_ADJ=3 26 | ro.HOME_APP_MEM=6144 27 | ro.factorytest=0 28 | ro.build.fingerprint=generic/sdk/generic:2.3.3/GRI34/101070:eng/test-keys 29 | ro.VISIBLE_APP_ADJ=1 30 | ro.build.product=generic 31 | net.dnschange=1 32 | ro.bootloader=unknown 33 | init.svc.vold=running 34 | ro.setupwizard.mode=OPTIONAL 35 | net.hostname=android_d28276dd0a5150fe 36 | net.bt.name=Android 37 | ro.build.version.sdk=10 38 | qemu.hw.mainkeys=1 39 | net.eth0.dns1=10.0.2.3 40 | ro.PERCEPTIBLE_APP_ADJ=2 41 | ro.bootmode=unknown 42 | ro.baseband=unknown 43 | ro.build.host=android-test-26.mtv.corp.google.com 44 | ro.runtime.firstboot=1396263906712 45 | ro.BACKUP_APP_ADJ=5 46 | ARGH=ARGH 47 | ro.build.display.id=sdk-eng 2.3.3 GRI34 101070 test-keys 48 | ro.revision=0 49 | ro.HOME_APP_ADJ=6 50 | ro.HIDDEN_APP_MEM=7168 51 | hw.keyboards.65536.devname=qwerty2 52 | init.svc.qemud=running 53 | ro.kernel.android.qemud=ttyS1 54 | ro.product.locale.region= 55 | gsm.operator.isroaming=false 56 | persist.sys.timezone=Asia/Harbin 57 | ro.VISIBLE_APP_MEM=3072 58 | gsm.operator.numeric=310260 59 | ro.EMPTY_APP_MEM=8192 60 | ro.allow.mock.location=1 61 | persist.service.adb.enable=1 62 | net.tcp.buffersize.wifi=4095,87380,110208,4096,16384,110208 63 | ro.HEAVY_WEIGHT_APP_MEM=4096 64 | ro.kernel.qemu.gles=0 65 | persist.sys.localevar= 66 | gsm.nitz.time=1396263893682 67 | ro.HIDDEN_APP_MIN_ADJ=7 68 | ro.product.brand=generic 69 | gsm.sim.operator.numeric=310260 70 | ro.config.notification_sound=OnTheHunt.ogg 71 | ro.build.date.utc=1296773036 72 | ro.board.platform= 73 | ro.com.google.locationfeatures=1 74 | ro.product.device=generic 75 | ro.product.locale.language=ldpi 76 | dev.bootcomplete=1 77 | qemu.sf.lcd_density=160 78 | gsm.sim.state=READY 79 | ro.monkey=true 80 | init.svc.keystore=running 81 | gsm.defaultpdpcontext.active=true 82 | ro.hardware=goldfish 83 | status.battery.level=5 84 | status.battery.level_scale=9 85 | init.svc.netd=running 86 | ro.FOREGROUND_APP_MEM=2048 87 | xmpp.auto-presence=true 88 | ro.kernel.console=ttyS0 89 | ro.build.id=GRI34 90 | ro.PERCEPTIBLE_APP_MEM=4096 91 | ro.radio.use-ppp=no 92 | net.gprs.local-ip=10.0.2.15 93 | net.tcp.buffersize.umts=4094,87380,110208,4096,16384,110208 94 | net.tcp.buffersize.default=4096,87380,110208,4096,16384,110208 95 | net.gprs.http-proxy= 96 | rild.libpath=/system/lib/libreference-ril.so 97 | sys.boot_completed=1 98 | net.change=net.dnschange 99 | ro.product.name=sdk 100 | ro.build.tags=test-keys 101 | ro.config.alarm_alert=Alarm_Classic.ogg 102 | init.svc.goldfish-logcat=stopped 103 | status.battery.level_raw=50 104 | gsm.version.ril-impl=android reference-ril 1.0 105 | ro.product.board= 106 | ro.config.low_ram=true 107 | init.svc.media=running 108 | ro.SECONDARY_SERVER_MEM=6144 109 | ro.build.description=sdk-eng 2.3.3 GRI34 101070 test-keys 110 | ro.SECONDARY_SERVER_ADJ=4 111 | ro.build.version.codename=REL 112 | ro.config.nocheckin=yes 113 | ro.product.cpu.abi=armeabi 114 | rild.libargs=-d /dev/ttyS0 115 | ro.kernel.android.checkjni=1 116 | qemu.sf.fake_camera=none 117 | ro.qemu.init.completed=1 118 | dalvik.vm.heapsize=16m 119 | init.svc.bootanim=stopped 120 | dalvik.vm.stack-trace-file=/data/anr/traces.txt 121 | net.tcp.buffersize.edge=4093,26280,35040,4096,16384,35040 122 | ro.serialno= 123 | persist.sys.country=US 124 | net.tcp.buffersize.gprs=4092,8760,11680,4096,8760,11680 125 | ro.build.date=Thu Feb 3 14:43:56 PST 2011 126 | ro.build.version.release=2.3.3 127 | init.svc.console=running 128 | init.svc.dumpstate=stopped 129 | init.svc.zygote=running 130 | init.svc.servicemanager=running 131 | gsm.current.phone-type=1 132 | ro.build.user=android-build 133 | gsm.sim.operator.iso-country=us 134 | ro.FOREGROUND_APP_ADJ=0 135 | init.svc.installd=running 136 | ro.EMPTY_APP_ADJ=15 137 | ro.kernel.qemu=1 138 | ro.carrier=unknown 139 | ro.secure=0 140 | ro.build.type=eng 141 | ro.build.version.incremental=101070 142 | ro.wifi.channels= 143 | init.svc.adbd=running 144 | init.svc.goldfish-setup=stopped 145 | gsm.operator.alpha=Android 146 | gsm.network.type=UMTS 147 | ro.BACKUP_APP_MEM=6144 148 | net.dns1=10.0.2.3 149 | init.svc.ril-daemon=running 150 | status.battery.state=Slow 151 | -------------------------------------------------------------------------------- /dist/logs/f451e7dc-66c0-4f8e-b7fb-d96402134267/properties.txt: -------------------------------------------------------------------------------- 1 | 2 | [monkey] 3 | apk.package=com.example 4 | test.finish=2014-03-31 19:46:35 5 | series.id=9318aac0-5e1f-4172-994b-60b58b3e7e66 6 | status=FAIL 7 | apk.version=1.0 8 | test.duration.expect=180 9 | test.start=2014-03-31 19:46:32 10 | executor=alex 11 | test.duration.actual=3 12 | test.id=f451e7dc-66c0-4f8e-b7fb-d96402134267 13 | command=monkey -p com.example -s -2997299013541685614 --throttle 300 -c android.intent.category.MONKEY -c android.intent.category.LAUNCHER -c android.intent.category.DEFAULT --monitor-native-crashes --kill-process-after-error --pct-touch 43 --pct-motion 30 --pct-majornav 10 --pct-syskeys 15 --pct-appswitch 2 -v -v -v 99999 14 | series.duration.expect=180 15 | 16 | [device] 17 | ro.product.manufacturer=unknown 18 | gsm.sim.operator.alpha=Android 19 | ro.kernel.ndns=2 20 | ro.debuggable=1 21 | init.svc.debuggerd=running 22 | ro.product.model=sdk 23 | gsm.operator.iso-country=us 24 | persist.sys.language=en 25 | ro.HEAVY_WEIGHT_APP_ADJ=3 26 | ro.HOME_APP_MEM=6144 27 | ro.factorytest=0 28 | ro.build.fingerprint=generic/sdk/generic:2.3.3/GRI34/101070:eng/test-keys 29 | ro.VISIBLE_APP_ADJ=1 30 | ro.build.product=generic 31 | net.dnschange=1 32 | ro.bootloader=unknown 33 | init.svc.vold=running 34 | ro.setupwizard.mode=OPTIONAL 35 | net.hostname=android_d28276dd0a5150fe 36 | net.bt.name=Android 37 | ro.build.version.sdk=10 38 | qemu.hw.mainkeys=1 39 | net.eth0.dns1=10.0.2.3 40 | ro.PERCEPTIBLE_APP_ADJ=2 41 | ro.bootmode=unknown 42 | ro.baseband=unknown 43 | ro.build.host=android-test-26.mtv.corp.google.com 44 | ro.runtime.firstboot=1396263906712 45 | ro.BACKUP_APP_ADJ=5 46 | ARGH=ARGH 47 | ro.build.display.id=sdk-eng 2.3.3 GRI34 101070 test-keys 48 | ro.revision=0 49 | ro.HOME_APP_ADJ=6 50 | ro.HIDDEN_APP_MEM=7168 51 | hw.keyboards.65536.devname=qwerty2 52 | init.svc.qemud=running 53 | ro.kernel.android.qemud=ttyS1 54 | ro.product.locale.region= 55 | gsm.operator.isroaming=false 56 | persist.sys.timezone=Asia/Harbin 57 | ro.VISIBLE_APP_MEM=3072 58 | gsm.operator.numeric=310260 59 | ro.EMPTY_APP_MEM=8192 60 | ro.allow.mock.location=1 61 | persist.service.adb.enable=1 62 | net.tcp.buffersize.wifi=4095,87380,110208,4096,16384,110208 63 | ro.HEAVY_WEIGHT_APP_MEM=4096 64 | ro.kernel.qemu.gles=0 65 | persist.sys.localevar= 66 | gsm.nitz.time=1396263893682 67 | ro.HIDDEN_APP_MIN_ADJ=7 68 | ro.product.brand=generic 69 | gsm.sim.operator.numeric=310260 70 | ro.config.notification_sound=OnTheHunt.ogg 71 | ro.build.date.utc=1296773036 72 | ro.board.platform= 73 | ro.com.google.locationfeatures=1 74 | ro.product.device=generic 75 | ro.product.locale.language=ldpi 76 | dev.bootcomplete=1 77 | qemu.sf.lcd_density=160 78 | gsm.sim.state=READY 79 | ro.monkey=true 80 | init.svc.keystore=running 81 | gsm.defaultpdpcontext.active=true 82 | ro.hardware=goldfish 83 | status.battery.level=5 84 | status.battery.level_scale=9 85 | init.svc.netd=running 86 | ro.FOREGROUND_APP_MEM=2048 87 | xmpp.auto-presence=true 88 | ro.kernel.console=ttyS0 89 | ro.build.id=GRI34 90 | ro.PERCEPTIBLE_APP_MEM=4096 91 | ro.radio.use-ppp=no 92 | net.gprs.local-ip=10.0.2.15 93 | net.tcp.buffersize.umts=4094,87380,110208,4096,16384,110208 94 | net.tcp.buffersize.default=4096,87380,110208,4096,16384,110208 95 | net.gprs.http-proxy= 96 | rild.libpath=/system/lib/libreference-ril.so 97 | sys.boot_completed=1 98 | net.change=net.dnschange 99 | ro.product.name=sdk 100 | ro.build.tags=test-keys 101 | ro.config.alarm_alert=Alarm_Classic.ogg 102 | init.svc.goldfish-logcat=stopped 103 | status.battery.level_raw=50 104 | gsm.version.ril-impl=android reference-ril 1.0 105 | ro.product.board= 106 | ro.config.low_ram=true 107 | init.svc.media=running 108 | ro.SECONDARY_SERVER_MEM=6144 109 | ro.build.description=sdk-eng 2.3.3 GRI34 101070 test-keys 110 | ro.SECONDARY_SERVER_ADJ=4 111 | ro.build.version.codename=REL 112 | ro.config.nocheckin=yes 113 | ro.product.cpu.abi=armeabi 114 | rild.libargs=-d /dev/ttyS0 115 | ro.kernel.android.checkjni=1 116 | qemu.sf.fake_camera=none 117 | ro.qemu.init.completed=1 118 | dalvik.vm.heapsize=16m 119 | init.svc.bootanim=stopped 120 | dalvik.vm.stack-trace-file=/data/anr/traces.txt 121 | net.tcp.buffersize.edge=4093,26280,35040,4096,16384,35040 122 | ro.serialno= 123 | persist.sys.country=US 124 | net.tcp.buffersize.gprs=4092,8760,11680,4096,8760,11680 125 | ro.build.date=Thu Feb 3 14:43:56 PST 2011 126 | ro.build.version.release=2.3.3 127 | init.svc.console=running 128 | init.svc.dumpstate=stopped 129 | init.svc.zygote=running 130 | init.svc.servicemanager=running 131 | gsm.current.phone-type=1 132 | ro.build.user=android-build 133 | gsm.sim.operator.iso-country=us 134 | ro.FOREGROUND_APP_ADJ=0 135 | init.svc.installd=running 136 | ro.EMPTY_APP_ADJ=15 137 | ro.kernel.qemu=1 138 | ro.carrier=unknown 139 | ro.secure=0 140 | ro.build.type=eng 141 | ro.build.version.incremental=101070 142 | ro.wifi.channels= 143 | init.svc.adbd=running 144 | init.svc.goldfish-setup=stopped 145 | gsm.operator.alpha=Android 146 | gsm.network.type=UMTS 147 | ro.BACKUP_APP_MEM=6144 148 | net.dns1=10.0.2.3 149 | init.svc.ril-daemon=running 150 | status.battery.state=Slow 151 | -------------------------------------------------------------------------------- /dist/logs/index_utf8.html: -------------------------------------------------------------------------------- 1 | Android Monkey Report
执行结果信息汇总
基本信息
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
执行人-
总执行时间0.05
平均时长(出现Crash/ANR取所有异常停止执行时间之平均值)0.0小时
产品名称com.example
产品版本1.0
手机信息generic/sdk/generic:2.3.3/GRI34/101070:eng/test-keys
ANR数量0
CRASH数量2
Native CRASH数量0
20 |
详细信息
21 | 22 |
TypeShort MessageNumDurationMessageLog
crashjava.lang.NullPointerException20小时0分钟3秒
0小时0分钟3秒
// CRASH: com.example (pid 987)
// Short Msg: java.lang.NullPointerException
// Long Msg: java.lang.NullPointerException: Unable to start activity ComponentInfo{com.example/com.example.MainActivity}: java.lang.NullPointerException
// Build Label: generic/sdk/generic:2.3.3/GRI34/101070:eng/test-keys
// Build Changelist: 101070
// Build Time: 1296773036000
// java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.MainActivity}: java.lang.NullPointerException
// at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
// at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
// at android.app.ActivityThread.access$1500(ActivityThread.java:117)
// at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
// at android.os.Handler.dispatchMessage(Handler.java:99)
// at android.os.Looper.loop(Looper.java:123)
// at android.app.ActivityThread.main(ActivityThread.java:3683)
// at java.lang.reflect.Method.invokeNative(Native Method)
// at java.lang.reflect.Method.invoke(Method.java:507)
// at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
// at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
// at dalvik.system.NativeStart.main(Native Method)
// Caused by: java.lang.NullPointerException
// at com.android.internal.os.LoggingPrintStream.println(LoggingPrintStream.java:298)
// at com.example.MainActivity.onCreate(MainActivity.java:21)
// at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
// at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
// ... 11 more
//
下载
下载
-------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/model/AbnormalityProperties.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.model; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileNotFoundException; 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | import java.text.ParsePosition; 10 | import java.text.SimpleDateFormat; 11 | import java.util.Date; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | import com.github.monkey.analyzer.analyze.Constants; 16 | 17 | /** 18 | * Read the file that stores Monkey result and Device information and save some 19 | * important key
20 | * 21 | * @author Alex Chen (apack1001@gmail.com) 22 | * 23 | */ 24 | public class AbnormalityProperties { 25 | private static final String REGEX_PACKAGE = "apk.package="; 26 | private static final String REGEX_PACKAGE_VERSION = "apk.version="; 27 | private static final String REGEX_SERIAL = "ro.serialno="; 28 | private static final String REGEX_START = "test.start="; 29 | private static final String REGEX_END = "test.finish="; 30 | private static final String REGEX_FINGERPRINT = "ro.build.fingerprint="; 31 | 32 | private Map properties = new HashMap(); 33 | 34 | public Map getProperties() { 35 | return properties; 36 | } 37 | 38 | /** 39 | * open a file from filePath then get all the necessary information 40 | * 41 | * @param filePath 42 | * the path of properties file 43 | */ 44 | public AbnormalityProperties(final String filePath) { 45 | File file = new File(filePath); 46 | if (file.exists() && file.isFile()) { 47 | InputStreamReader inputStreamReader; 48 | try { 49 | inputStreamReader = new InputStreamReader(new FileInputStream( 50 | file)); 51 | BufferedReader bufferedReader = new BufferedReader( 52 | inputStreamReader); 53 | 54 | String line = null; 55 | while ((line = bufferedReader.readLine()) != null) { 56 | if (line.contains(REGEX_PACKAGE)) { 57 | String[] splits = line.split(REGEX_PACKAGE); 58 | if (splits != null && splits.length == 2) { 59 | properties 60 | .put(Constants.AbnormalityProperties.KEY_PACKAGE_NAME, 61 | splits[1]); 62 | } 63 | } 64 | 65 | if (line.contains(REGEX_START)) { 66 | String[] splits = line.split(REGEX_START); 67 | if (splits != null && splits.length == 2) { 68 | properties 69 | .put(Constants.AbnormalityProperties.KEY_START_TIME, 70 | splits[1]); 71 | } 72 | } 73 | 74 | if (line.contains(REGEX_END)) { 75 | String[] splits = line.split(REGEX_END); 76 | if (splits != null && splits.length == 2) { 77 | properties 78 | .put(Constants.AbnormalityProperties.KEY_END_TIME, 79 | splits[1]); 80 | } 81 | } 82 | 83 | if (line.contains(REGEX_SERIAL)) { 84 | String[] splits = line.split(REGEX_SERIAL); 85 | if (splits != null && splits.length == 2) { 86 | properties.put( 87 | Constants.AbnormalityProperties.KEY_SERIAL, 88 | splits[1]); 89 | } 90 | } 91 | 92 | if (line.contains(REGEX_FINGERPRINT)) { 93 | String[] splits = line.split(REGEX_FINGERPRINT); 94 | if (splits != null && splits.length == 2) { 95 | properties 96 | .put(Constants.AbnormalityProperties.KEY_PLATFORM, 97 | splits[1]); 98 | } 99 | } 100 | 101 | if (line.contains(REGEX_PACKAGE_VERSION)) { 102 | String[] splits = line.split(REGEX_PACKAGE_VERSION); 103 | if (splits != null && splits.length == 2) { 104 | properties 105 | .put(Constants.AbnormalityProperties.KEY_PACKAGE_VERSION, 106 | splits[1]); 107 | } 108 | } 109 | } 110 | 111 | bufferedReader.close(); 112 | 113 | } catch (FileNotFoundException e) { 114 | e.printStackTrace(); 115 | } catch (IOException e) { 116 | e.printStackTrace(); 117 | } 118 | 119 | if (properties == null) 120 | return; 121 | 122 | String start = properties 123 | .get(Constants.AbnormalityProperties.KEY_START_TIME); 124 | String end = properties 125 | .get(Constants.AbnormalityProperties.KEY_END_TIME); 126 | 127 | if (start == null || end == null) 128 | return; 129 | 130 | String duration = getDurationString(start, end); 131 | properties.put(Constants.AbnormalityProperties.KEY_DURATION_LONG, 132 | Long.toString(getDurationLong(start, end))); 133 | properties.put(Constants.AbnormalityProperties.KEY_DURATION_STRING, 134 | duration); 135 | } 136 | } 137 | 138 | private String getDurationString(String start, String end) { 139 | long lDuration = getDurationLong(start, end); 140 | long hour = lDuration / 1000 / 3600; 141 | long minite = (lDuration - hour * 1000 * 3600) / 1000 / 60; 142 | long seconds = lDuration / 1000 % 60; 143 | String duration = "" + hour + "小时" + minite + "分钟" + seconds + "秒"; 144 | return duration; 145 | } 146 | 147 | private long getDurationLong(final String start, final String end) { 148 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 149 | ParsePosition pos = new ParsePosition(0); 150 | ParsePosition pos1 = new ParsePosition(0); 151 | Date dt1 = formatter.parse(start, pos); 152 | Date dt2 = formatter.parse(end, pos1); 153 | long duration = dt2.getTime() - dt1.getTime(); 154 | return duration; 155 | } 156 | 157 | public void setProperties(final Map properties) { 158 | this.properties.clear(); 159 | this.properties = properties; 160 | } 161 | 162 | /** 163 | * Get one property with given key 164 | * 165 | * 166 | * @param key 167 | * @return 168 | */ 169 | public String get(final String key) { 170 | if (properties == null) 171 | return null; 172 | 173 | return properties.get(key); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/statistics/AbnormalitiesAnalyzerWrapper.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.statistics; 2 | 3 | import java.lang.ref.WeakReference; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import com.github.monkey.analyzer.analyze.Constants; 10 | import com.github.monkey.analyzer.model.Abnormality; 11 | import com.github.monkey.analyzer.model.AbnormalityType; 12 | import com.github.monkey.analyzer.model.AnrOrCrashAbnormality; 13 | 14 | /** 15 | * A wrapper class which is to strengthen the function of the Analyze module 16 | * 17 | * @author Alex Chen (apack1001@gmail.com) 18 | * 19 | */ 20 | final public class AbnormalitiesAnalyzerWrapper { 21 | private static Map statistics = Collections 22 | .synchronizedMap(new HashMap()); 23 | 24 | /** 25 | * Get the count of some given kind of abnormalities 26 | * 27 | * @param abnormalitiesRef 28 | * instance of {@link WeakReference 29 | * >} 30 | * @param type 31 | * Type of Abnormality 32 | * @return the count 33 | */ 34 | public final static int getCount( 35 | final WeakReference> abnormalitiesRef, 36 | final AbnormalityType type) { 37 | statistics.clear(); 38 | if (abnormalitiesRef == null || abnormalitiesRef.get() == null) 39 | throw new RuntimeException(); 40 | 41 | for (Abnormality abnormality : abnormalitiesRef.get()) { 42 | if (abnormality.getType() == type) { 43 | if (statistics.containsKey(type)) 44 | statistics.put(type, statistics.get(type) + 1); 45 | else 46 | statistics.put(type, 1); 47 | } 48 | } 49 | 50 | if (statistics.get(type) == null) 51 | return 0; 52 | 53 | return Integer.valueOf(statistics.get(type)); 54 | } 55 | 56 | /** 57 | * Print all the abnormalities 58 | * 59 | * @param abnormalities 60 | * all the abnormalities 61 | */ 62 | public static void printAbnormalities( 63 | ArrayList abnormalities) { 64 | 65 | HashMap hm = removeDulplicatedCrashes(abnormalities); 66 | 67 | for (Abnormality abnormality : abnormalities) { 68 | if (abnormality.getType() == AbnormalityType.ANR) { 69 | System.out.println("出现ANR\n" + abnormality.getMessage() + "1次"); 70 | String temp = abnormality.getExtras().get( 71 | AnrOrCrashAbnormality.EXTRAS_KEY_AFTER_END_PATTERN); 72 | System.out.println(temp); 73 | System.out.println("--\n"); 74 | } 75 | } 76 | 77 | for (String key : hm.keySet()) { 78 | System.out.println("出现" + hm.get(key).abnormalities.size() + "次" 79 | + hm.get(key).crashAbnormalities.getShortMessage() + "异常"); 80 | System.out.println("" 81 | + hm.get(key).abnormalities.get(0).get().getMessage()); 82 | // System.out.println("" 83 | // + hm.get(key).abnormality.getExtras().toString()); 84 | System.out.println("--\n"); 85 | } 86 | } 87 | 88 | /** 89 | * Remove all the duplicated crash abnormalities, duplicate carsh 90 | * abnormalities will save in an instance of tuple 91 | * 92 | * @param abnormalities 93 | * all the abnormalities to be processed 94 | * @return an instance of {@code HashMap} which store all the 95 | * processed abnormalities 96 | */ 97 | public static HashMap removeDulplicatedCrashes( 98 | ArrayList abnormalities) { 99 | HashMap theProcessed = new HashMap(); 100 | 101 | for (Abnormality abnormality : abnormalities) { 102 | if (abnormality.getType() == AbnormalityType.CRASH) { 103 | CrashAbnormalityAdapter abnormalityAdapter = new CrashAbnormalityAdapter( 104 | abnormality); 105 | 106 | if (theProcessed.containsKey(abnormalityAdapter.getCodeLines())) { 107 | Tuple tuple = theProcessed.get(abnormalityAdapter 108 | .getCodeLines()); 109 | tuple.abnormalities 110 | .add(new WeakReference( 111 | abnormality)); 112 | theProcessed.put(abnormalityAdapter.getCodeLines(), tuple); 113 | } else { 114 | Tuple tuple = new Tuple(); 115 | tuple.crashAbnormalities = abnormalityAdapter; 116 | tuple.abnormalities 117 | .add(new WeakReference( 118 | abnormality)); 119 | theProcessed.put(abnormalityAdapter.getCodeLines(), tuple); 120 | } 121 | } 122 | } 123 | return theProcessed; 124 | } 125 | 126 | /** 127 | * Calculate the average duration of this monkey test 128 | * 129 | * @param knownAbnormalities 130 | * all the known abnormalities 131 | * @param unknownAbnormalities 132 | * all the unknown abnormalities 133 | * @param monkeyTestDuration 134 | * expected one monkey test duration 135 | * @return the average duration of the monkey test 136 | */ 137 | public static double getAverage( 138 | ArrayList knownAbnormalities, 139 | ArrayList unknownAbnormalities, 140 | double monkeyTestDuration) { 141 | if (monkeyTestDuration <= 0) 142 | throw new IllegalArgumentException("Some arguments maybe wrong!"); 143 | 144 | if (knownAbnormalities == null || knownAbnormalities.size() == 0) { 145 | return monkeyTestDuration; 146 | } else { 147 | long durationSum = 0; 148 | for (Abnormality abnormality : knownAbnormalities) { 149 | if (abnormality.getExtras().containsKey( 150 | Constants.AbnormalityProperties.KEY_DURATION_LONG) == false) 151 | return monkeyTestDuration / knownAbnormalities.size(); 152 | try { 153 | durationSum += Long.parseLong(abnormality.getExtras().get( 154 | Constants.AbnormalityProperties.KEY_DURATION_LONG)); 155 | } catch (Exception e) { 156 | e.printStackTrace(); 157 | } 158 | } 159 | // 保留小数点后2位,4舍5入 160 | long temp = durationSum / 3600 / knownAbnormalities.size(); 161 | temp = (temp + (temp % 10 >= 5 ? 10 : 0) - temp % 10) / 10 ; 162 | return (double) temp / 100; 163 | } 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/scheduler/MonkeyTestSeries.java: -------------------------------------------------------------------------------- 1 | 2 | package com.github.monkey.runner.scheduler; 3 | 4 | import java.util.Random; 5 | import java.util.UUID; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import com.android.ddmlib.IShellOutputReceiver; 9 | 10 | public class MonkeyTestSeries { 11 | 12 | final MonkeyTestDevice mDevice; 13 | final String mSeriesId; 14 | final String mPkgName; 15 | final String mPkgVersion; 16 | final String mPkgFilePath; 17 | final String mRawCommand; 18 | final String mUserName; 19 | final String mInitFilePath; 20 | final long mSeriesExpectDuration; 21 | final long mSingleExpectDuration; 22 | long mSeriesActualDuration = 0; 23 | long mSeriesStart = 0; 24 | long mSeriesFinish = 0; 25 | boolean mIsSeriesStarted = false; 26 | boolean mIsSeriesFinished = false; 27 | public MonkeyTestSeriesExecutor mExecutor; 28 | MonkeyTest mCurrentTest; 29 | 30 | public MonkeyTestSeries( 31 | MonkeyTestDevice device, 32 | String pkgName, 33 | String pkgVersion, 34 | String pkgFilePath, 35 | String rawCommand, 36 | String userName, 37 | String initFilePath, 38 | long seriesDuration, 39 | long singleDuration) { 40 | mSeriesId = UUID.randomUUID().toString(); 41 | mDevice = device; 42 | mPkgName = pkgName; 43 | mPkgVersion = pkgVersion; 44 | mPkgFilePath = pkgFilePath; 45 | mRawCommand = rawCommand; 46 | mUserName = userName; 47 | mInitFilePath = initFilePath; 48 | mSeriesExpectDuration = seriesDuration; 49 | mSingleExpectDuration = singleDuration; 50 | } 51 | 52 | public boolean isStarted() { 53 | return mIsSeriesStarted; 54 | } 55 | 56 | public boolean isFinished() { 57 | return mIsSeriesFinished; 58 | } 59 | 60 | public void cleanUp() { 61 | if (mIsSeriesFinished) { 62 | mExecutor = null; 63 | mCurrentTest = null; 64 | mIsSeriesStarted = false; 65 | mIsSeriesFinished = false; 66 | } 67 | } 68 | 69 | public void start() { 70 | if (mExecutor != null) { 71 | return; 72 | } 73 | mExecutor = new MonkeyTestSeriesExecutor(mSeriesId); 74 | mExecutor.start(); 75 | mIsSeriesStarted = true; 76 | } 77 | 78 | public void interrupt() { 79 | if (mExecutor == null || mCurrentTest == null) { 80 | return; 81 | } 82 | mExecutor.interrupt(); 83 | mCurrentTest.interrupt(); 84 | } 85 | 86 | public MonkeyTestDevice getDevice() { 87 | return mDevice; 88 | } 89 | 90 | public class MonkeyTestSeriesExecutor extends Thread { 91 | 92 | public MonkeyTestSeriesExecutor(String name) { 93 | super(name); 94 | } 95 | 96 | void log(String msg) { 97 | Console.printLogMessage(mDevice.getSerialNumber(), msg); 98 | } 99 | 100 | String getCommand() 101 | { 102 | String command = mRawCommand; 103 | if (!command.contains(" -s ")) 104 | { 105 | Random r = new Random(); 106 | command = command.replace("monkey", String.format("monkey -s %s", r.nextLong())); 107 | } 108 | if (!command.contains(" -p ")) 109 | { 110 | command = command.replace("monkey", String.format("monkey -p %s", mPkgName)); 111 | } 112 | return command; 113 | } 114 | 115 | @Override 116 | public void run() { 117 | try { 118 | log("Monkey test series started."); 119 | String msg = mDevice.install(mPkgFilePath); 120 | if (msg == null || msg.isEmpty()) 121 | { 122 | log("Install package success."); 123 | } else 124 | { 125 | log("Install package fail (" + msg + ")."); 126 | } 127 | mSeriesStart = System.currentTimeMillis(); 128 | while (!mIsSeriesFinished) { 129 | mCurrentTest = new MonkeyTest(mDevice, mSeriesId, mPkgName, 130 | mPkgVersion, getCommand(), 131 | mUserName, mInitFilePath, mSingleExpectDuration, mSeriesExpectDuration); 132 | mCurrentTest.start(); 133 | 134 | while (true) { 135 | TimeUnit.SECONDS.sleep(1); 136 | mSeriesFinish = System.currentTimeMillis(); 137 | mSeriesActualDuration = mSeriesFinish - mSeriesStart; 138 | // System.out.println("mSeriesActualDuration" + mSeriesActualDuration + "mSeriesExpectDuration" + mSeriesExpectDuration); 139 | if (mCurrentTest.isFinished()) { 140 | break; 141 | } 142 | if (mSeriesActualDuration >= mSeriesExpectDuration) { 143 | mSeriesActualDuration = mSeriesExpectDuration; 144 | mIsSeriesFinished = true; 145 | break; 146 | } 147 | } 148 | } 149 | } catch (InterruptedException e) { 150 | e.printStackTrace(); 151 | } finally { 152 | if (mCurrentTest != null && mCurrentTest.isStarted() && !mCurrentTest.isFinished()) { 153 | mCurrentTest.interrupt(); 154 | } 155 | mIsSeriesFinished = true; 156 | // disable reboot 157 | //mDevice.reboot(); 158 | log("Monkey test series finished."); 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/helper/LocationHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.runner.helper; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Helper for fetch the os location of the files (including configure files, log 7 | * files etc.) 8 | */ 9 | public final class LocationHelper { 10 | 11 | /** 12 | * Throw when the location of the file couldn't be found. 13 | */ 14 | public static final class FileLocationException extends Exception { 15 | 16 | private static final long serialVersionUID = 1L; 17 | 18 | public FileLocationException(String string) { 19 | super(string); 20 | } 21 | } 22 | 23 | private static String sHomeLocation = null; 24 | private static String sConfLocation = null; 25 | private static String sLogsLocation = null; 26 | private static String sScriptLocation = null; 27 | 28 | /** 29 | * Returns the home location. 30 | * 31 | * @return an OS specific path, terminated by a separator. 32 | * 33 | * @throws FileLocationException 34 | */ 35 | public final static String getHomeLocation() throws FileLocationException { 36 | if (sHomeLocation == null) { 37 | String home = findValidPath("user.dir", "HOME"); 38 | // if the above failed, we throw an exception. 39 | if (home == null) { 40 | throw new FileLocationException("Unable to get home directory."); 41 | } else { 42 | sHomeLocation = home; 43 | } 44 | } 45 | return sHomeLocation; 46 | } 47 | 48 | /** 49 | * Returns the folder used to store preference related files. 50 | * 51 | * @return an OS specific path, terminated by a separator. 52 | * 53 | * @throws FileLocationException 54 | */ 55 | public final static String getConfLocation() throws FileLocationException { 56 | if (sConfLocation == null) { 57 | sConfLocation = getHomeLocation() + File.separator + "conf"; 58 | checkFolder(sConfLocation); 59 | } 60 | return sConfLocation; 61 | } 62 | 63 | /** 64 | * Returns the home folder used to store all log related files. 65 | * 66 | * @return an OS specific path, terminated by a separator. 67 | * 68 | * @throws FileLocationException 69 | */ 70 | public final static String getLogsLocation() throws FileLocationException { 71 | if (sLogsLocation == null) { 72 | sLogsLocation = getHomeLocation() + File.separator + "logs"; 73 | checkFolder(sLogsLocation); 74 | } 75 | return sLogsLocation; 76 | } 77 | 78 | /** 79 | * Returns the home folder used to store all script related files. 80 | * 81 | * @return an OS specific path, terminated by a separator. 82 | * 83 | * @throws FileLocationException 84 | */ 85 | public final static String getScriptLocation() throws FileLocationException { 86 | if (sScriptLocation == null) { 87 | sScriptLocation = getHomeLocation() + File.separator + "script"; 88 | checkFolder(sScriptLocation); 89 | } 90 | return sScriptLocation; 91 | } 92 | 93 | /** 94 | * Returns the folder used to store log related files for specific monkey 95 | * test item. 96 | * 97 | * @param id 98 | * the id of specific monkey test item 99 | * 100 | * 101 | * @return an OS specific path, terminated by a separator. 102 | * 103 | * @throws FileLocationException 104 | */ 105 | public final static String getLogsLocation(String id) 106 | throws FileLocationException { 107 | if (id == null) { 108 | return null; 109 | } 110 | String location = getLogsLocation() + File.separator + id; 111 | checkFolder(location); 112 | return location; 113 | } 114 | 115 | public final static String getZipLocation(String id) 116 | throws FileLocationException { 117 | return getLogsLocation() + File.separator + id + ".zip"; 118 | } 119 | 120 | public final static String getMonkeyLogLocation(String id) 121 | throws FileLocationException { 122 | return getLogsLocation(id) + File.separator + "monkey_log.txt"; 123 | } 124 | 125 | public final static String getLogcatLogLocation(String id) 126 | throws FileLocationException { 127 | return getLogsLocation(id) + File.separator + "logcat_log.txt"; 128 | } 129 | 130 | public final static String getBugreportLocation(String id) 131 | throws FileLocationException { 132 | return getLogsLocation(id) + File.separator + "bugreport_log.txt"; 133 | } 134 | 135 | public final static String getTracesLocation(String id) 136 | throws FileLocationException { 137 | return getLogsLocation(id) + File.separator + "traces_log.txt"; 138 | } 139 | 140 | public final static String getPropertiesLocation(String id) 141 | throws FileLocationException { 142 | return getLogsLocation(id) + File.separator + "properties.txt"; 143 | } 144 | 145 | public final static String getLaunchPackageLocation() 146 | throws FileLocationException { 147 | String location = getScriptLocation() + File.separator 148 | + "getLaunchPackage"; 149 | checkFile(location); 150 | return location; 151 | } 152 | 153 | public final static String getLaunchPackageJarLocation() 154 | throws FileLocationException { 155 | String location = getScriptLocation() + File.separator 156 | + "getLaunchPackage.jar"; 157 | checkFile(location); 158 | return location; 159 | } 160 | 161 | private static void checkFolder(String location) 162 | throws FileLocationException { 163 | File f = new File(location); 164 | // make sure the folder exists! 165 | if (f.exists() == false) { 166 | try { 167 | f.mkdir(); 168 | } catch (SecurityException e) { 169 | FileLocationException e2 = new FileLocationException( 170 | String.format("Unable to create folder '%1$s'.", 171 | location)); 172 | e2.initCause(e); 173 | throw e2; 174 | } 175 | } else if (f.isFile()) { 176 | throw new FileLocationException(String.format( 177 | "'%1$s' is not a directory!.", location)); 178 | } 179 | } 180 | 181 | private static void checkFile(String location) throws FileLocationException { 182 | File f = new File(location); 183 | if (f.exists() == false) { 184 | throw new FileLocationException(String.format("'%1$s' not exist.", 185 | location)); 186 | } 187 | } 188 | 189 | /** 190 | * Checks a list of system properties and/or system environment variables 191 | * for validity, and existing director, and returns the first one. 192 | * 193 | * @param names 194 | * @return the content of the first property/variable. 195 | */ 196 | private static String findValidPath(String... names) { 197 | for (String name : names) { 198 | String path; 199 | if (name.indexOf('.') != -1) { 200 | path = System.getProperty(name); 201 | } else { 202 | path = System.getenv(name); 203 | } 204 | 205 | if (path != null) { 206 | File f = new File(path); 207 | if (f.isDirectory()) { 208 | return path; 209 | } 210 | } 211 | } 212 | 213 | return null; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/scheduler/MonkeyTestDevice.java: -------------------------------------------------------------------------------- 1 | 2 | package com.github.monkey.runner.scheduler; 3 | 4 | import java.io.IOException; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import com.android.ddmlib.AndroidDebugBridge; 8 | import com.android.ddmlib.IDevice; 9 | import com.android.ddmlib.IShellOutputReceiver; 10 | import com.android.ddmlib.ShellCommandUnresponsiveException; 11 | import com.android.ddmlib.IDevice.DeviceState; 12 | 13 | public class MonkeyTestDevice { 14 | 15 | private IDevice mDevice; 16 | private final String mSerialNumber; 17 | 18 | public MonkeyTestDevice(IDevice device) { 19 | if (device == null) { 20 | throw new IllegalArgumentException( 21 | "MonkeyTestDevice(IDevice device), device should not be null."); 22 | } 23 | mDevice = device; 24 | String serialno = device.getSerialNumber(); 25 | if (serialno == null || serialno.isEmpty() || serialno.contains("?")) { 26 | throw new IllegalArgumentException( 27 | "MonkeyTestDevice(IDevice device), device should not have a illegal serial number, like null, empty or ??????"); 28 | } 29 | mSerialNumber = serialno; 30 | } 31 | 32 | /** 33 | * Returns the properties of device. 34 | */ 35 | public Map getProperties() { 36 | Map properties = new HashMap(); 37 | try { 38 | properties.putAll(mDevice.getProperties()); 39 | } catch (NullPointerException npe) { 40 | // when device rebooting, mDevice will be null 41 | properties.put("ro.serialno", mSerialNumber); 42 | } 43 | return properties; 44 | } 45 | 46 | /** 47 | * Returns the serial number of device. 48 | */ 49 | public String getSerialNumber() { 50 | return mSerialNumber; 51 | } 52 | 53 | public boolean connect() { 54 | boolean result = false; 55 | for (IDevice device : AndroidDebugBridge.getBridge().getDevices()) { 56 | if (mDevice.getSerialNumber().equals(device.getSerialNumber()) 57 | && mDevice.getState() == DeviceState.ONLINE) { 58 | mDevice = device; 59 | result = true; 60 | break; 61 | } 62 | } 63 | return result; 64 | } 65 | 66 | /** 67 | * Returns if the device is on line. 68 | * 69 | * @return true if {@link IDevice#getState()} returns 70 | * {@link DeviceState#ONLINE}. 71 | */ 72 | public boolean isOnline() { 73 | if (mDevice == null) { 74 | return false; 75 | } 76 | if (mDevice.getState() == DeviceState.ONLINE) { 77 | return true; 78 | } else { 79 | return false; 80 | } 81 | } 82 | 83 | /** 84 | * Returns if the SUT is off line. 85 | */ 86 | public boolean isOffline() { 87 | return !isOnline(); 88 | } 89 | 90 | public String install(String pkgFilePath) { 91 | String msg = ""; 92 | try { 93 | msg = mDevice.installPackage(pkgFilePath, true); 94 | } catch (Exception ex) { 95 | msg = ex.getMessage(); 96 | } 97 | return msg; 98 | } 99 | 100 | public String initialize(String shFilePath) 101 | { 102 | String msg = ""; 103 | String cmd = String.format("bash %s %s", shFilePath, 104 | mSerialNumber); 105 | Runtime runtime = Runtime.getRuntime(); 106 | Process process; 107 | try { 108 | process = runtime.exec(cmd); 109 | msg = "exit " + process.waitFor(); 110 | } catch (IOException ioe) { 111 | msg = ioe.getMessage(); 112 | } catch (InterruptedException ie) { 113 | msg = ie.getMessage(); 114 | } 115 | return msg; 116 | } 117 | 118 | public String reboot() { 119 | String msg = ""; 120 | try { 121 | mDevice.reboot(null); 122 | } catch (Exception ex) { 123 | msg = ex.getMessage(); 124 | } 125 | return msg; 126 | } 127 | 128 | /** 129 | * Executes a monkey command on the device, and sends the result to a 130 | * receiver. 131 | * 132 | * @param command the shell command to execute 133 | * @param receiver the {@link IShellOutputReceiver} that will receives the 134 | * output of the shell command 135 | */ 136 | public boolean monkey(String command, IShellOutputReceiver receiver) { 137 | boolean result = false; 138 | try { 139 | mDevice.executeShellCommand(command, receiver, 60000); 140 | result = true; 141 | } catch (ShellCommandUnresponsiveException mttor) { 142 | result = true; 143 | } catch (Exception ex) { 144 | result = false; 145 | } 146 | return result; 147 | } 148 | 149 | /** 150 | * Executes a logcat command on the device, and sends the result to a 151 | * receiver. 152 | * 153 | * @param receiver the {@link IShellOutputReceiver} that will receives the 154 | * output of the shell command 155 | */ 156 | public boolean logcat(IShellOutputReceiver receiver) { 157 | boolean result = false; 158 | try { 159 | mDevice.executeShellCommand("logcat -v threadtime *:V", receiver, 160 | 60000); 161 | result = true; 162 | } catch (ShellCommandUnresponsiveException mttor) { 163 | result = true; 164 | } catch (Exception ex) { 165 | result = false; 166 | } 167 | return result; 168 | } 169 | 170 | /** 171 | * Executes a bugreport command on the device, and sends the result to a 172 | * receiver. 173 | * 174 | * @param receiver the {@link IShellOutputReceiver} that will receives the 175 | * output of the shell command 176 | */ 177 | public boolean bugreport(IShellOutputReceiver receiver) { 178 | boolean result = false; 179 | try { 180 | mDevice.executeShellCommand("bugreport", receiver, 181 | 60000); 182 | result = true; 183 | } catch (ShellCommandUnresponsiveException mttor) { 184 | result = true; 185 | } catch (Exception ex) { 186 | result = false; 187 | } 188 | return result; 189 | } 190 | 191 | 192 | /** 193 | * Executes a cat /data/anr/traces.txt command on the device, and sends the result to a 194 | * receiver. 195 | * 196 | * @param receiver the {@link IShellOutputReceiver} that will receives the 197 | * output of the shell command 198 | */ 199 | public boolean traces(IShellOutputReceiver receiver) { 200 | boolean result = false; 201 | try { 202 | mDevice.executeShellCommand("cat /data/anr/traces.txt", receiver, 203 | 60000); 204 | result = true; 205 | } catch (ShellCommandUnresponsiveException mttor) { 206 | result = true; 207 | } catch (Exception ex) { 208 | result = false; 209 | } 210 | return result; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/analyze/Analyzer.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.analyze; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileNotFoundException; 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | import java.util.LinkedList; 10 | import java.util.Map; 11 | import java.util.regex.Pattern; 12 | 13 | import com.github.monkey.analyzer.model.Abnormality; 14 | import com.github.monkey.analyzer.model.AbnormalityFactory; 15 | import com.github.monkey.analyzer.model.AbnormalityProperties; 16 | import com.github.monkey.analyzer.model.AnrOrCrashAbnormality; 17 | 18 | /** 19 | * @brief This is an Analyzer template for abnormality analyzing which type is 20 | * Crash、ANR、native Crash 21 | * @author Alex Chen (apack1001@gmail.com) 22 | * @since 2013/03/20 23 | * 24 | */ 25 | public abstract class Analyzer implements IAnalyzer { 26 | 27 | protected AnalyzerConfiguration mConfig; 28 | protected String filePath; 29 | 30 | public Analyzer(AnalyzerConfiguration anrConfig) { 31 | mConfig = anrConfig; 32 | } 33 | 34 | public Analyzer() { 35 | super(); 36 | } 37 | 38 | @Override 39 | public void setPath(String path) { 40 | filePath = path; 41 | } 42 | 43 | @Override 44 | public Abnormality toAbnormality() { 45 | if (mConfig == null) 46 | throw new IllegalStateException("No analyzing configuration set"); 47 | LinkedList messageBefore = new LinkedList(); 48 | LinkedList messageAfter = new LinkedList(); 49 | Abnormality abnormality = AbnormalityFactory.newInstance(mConfig.getType()); 50 | 51 | StringBuilder message = new StringBuilder(); 52 | 53 | abnormality.setType(mConfig.getType()); 54 | if (filePath == null || abnormality == null) { 55 | throw new RuntimeException( 56 | "AnalyzerConfiguration or filePath not set"); 57 | } 58 | 59 | final Pattern startPattern = Pattern.compile(mConfig.getStartPattern()); 60 | final Pattern endPattern = Pattern.compile(mConfig.getEndPattern()); 61 | final String monkeyFilePath = filePath + mConfig.getMonkeyLogFileName(); 62 | File file = new File(monkeyFilePath); 63 | final int beforeSize = mConfig.getBeforeSize(); 64 | final int afterSize = mConfig.getAfterSize(); 65 | boolean matching = false; 66 | if (file.exists() && file.isFile()) { 67 | // StringBuilder message = new StringBuilder(); 68 | // message = new StringBuilder(""); 69 | matching = parse(file, messageBefore, messageAfter, message, 70 | startPattern, endPattern, beforeSize, afterSize); 71 | 72 | AbnormalityProperties properties = new AbnormalityProperties( 73 | filePath + mConfig.getPropertiesFileName()); 74 | // System.out.println(properties.getProperties().toString()); 75 | final Map map = properties.getProperties(); 76 | abnormality.copyFrom(map); 77 | 78 | // 匹配完成,输出匹配结果 79 | if (matching) { 80 | 81 | abnormality.setMessage(message); 82 | if (new File(filePath).isDirectory()) 83 | filePath = filePath.substring(0, filePath.length() - 1); 84 | abnormality.put(AnrOrCrashAbnormality.EXTRAS_KEY_PATH, filePath 85 | + ".zip"); 86 | 87 | onMatch(abnormality, message, messageBefore, messageAfter); 88 | // System.out.println(abnormality.getExtras().toString()); 89 | if (isPackageDefinedConfiguration() == false) 90 | abnormality.setValid(true); 91 | else { 92 | System.out.println("=================" + mConfig.getPackageName()); 93 | System.out.println(message.toString()); 94 | 95 | if (message.toString().contains(mConfig.getPackageName())) { 96 | abnormality.setValid(true); 97 | // System.out.println(abnormality.getMessage()); 98 | } 99 | } 100 | } 101 | 102 | } 103 | return abnormality; 104 | } 105 | 106 | /** 107 | * Parse a file via given IN parameter(file, startPattern, 108 | * endPattern, beforeSize, afterSize)
109 | * After parsing, results will be put into 110 | * outparameter(messageBefore, messageAfter, message)
111 | * 112 | * @param file 113 | * log file 114 | * @param messageBefore 115 | * log line String before startPattern 116 | * @param messageAfter 117 | * log line String after endPattern 118 | * @param message 119 | * the message of this log 120 | * @param startPattern 121 | * the pattern which the log start with 122 | * @param endPattern 123 | * the pattern which the log end with 124 | * @param beforeSize 125 | * log line number before startPattern 126 | * @param afterSize 127 | * log line number after endPattern 128 | * @return {@code true} if matching the Patterns 129 | */ 130 | final protected boolean parse(File file, LinkedList messageBefore, 131 | LinkedList messageAfter, StringBuilder message, 132 | final Pattern startPattern, final Pattern endPattern, 133 | final int beforeSize, final int afterSize) { 134 | boolean matching = false; 135 | try { 136 | InputStreamReader inputStreamReader = new InputStreamReader( 137 | new FileInputStream(file)); 138 | BufferedReader bufferedReader = new BufferedReader( 139 | inputStreamReader); 140 | 141 | String line = null; 142 | boolean matchStart = false; 143 | boolean matchEnd = false; 144 | 145 | while ((line = bufferedReader.readLine()) != null) { 146 | // 先匹配开始选项 147 | if (startPattern.matcher(line).find()) { 148 | matchStart = true; 149 | } 150 | // 拿到beforePattern之前n行的数据 151 | if (!matchStart && beforeSize > 0) { 152 | if (messageBefore.size() == beforeSize) 153 | messageBefore.pollFirst(); 154 | messageBefore.addLast(line); 155 | } 156 | 157 | // 填充真正的Message 158 | if (matchStart && !matchEnd) { 159 | message.append(line); 160 | message.append("\n"); 161 | } 162 | 163 | // 匹配结束选项 164 | if (matchStart && endPattern.matcher(line).find()) { 165 | matchEnd = true; 166 | continue; 167 | } 168 | if (matchEnd && afterSize < 0) { 169 | if (messageAfter.size() < 50) 170 | messageAfter.addLast(line); 171 | } else if (matchEnd && messageAfter.size() < afterSize) { 172 | if (messageAfter.size() < 50) 173 | messageAfter.addLast(line); 174 | } 175 | } 176 | matching = matchStart && matchEnd && message != null; 177 | bufferedReader.close(); 178 | inputStreamReader.close(); 179 | } catch (FileNotFoundException e) { 180 | e.printStackTrace(); 181 | } catch (IOException e) { 182 | e.printStackTrace(); 183 | } 184 | return matching; 185 | } 186 | 187 | /** 188 | * It is a Template pattern, toAbnormality will called to convert from 189 | * matching result to an abnormality 190 | * 191 | * @param abnormality 192 | * abnormality to be filled 193 | * @param message 194 | * the message passed to the abnormality 195 | * @param messageBefore 196 | * lines before message 197 | * @param messageAfter 198 | * lines after message 199 | */ 200 | protected abstract void onMatch(Abnormality abnormality, 201 | StringBuilder message, LinkedList messageBefore, 202 | LinkedList messageAfter); 203 | 204 | /** 205 | * Get whether the target package under analyzing is defined in the 206 | * configuration 207 | * 208 | * @return whether the package is defined or not 209 | */ 210 | private boolean isPackageDefinedConfiguration() { 211 | if (mConfig == null) 212 | throw new RuntimeException("Analyzing Configuration is null"); 213 | 214 | final String pkgName = mConfig.getPackageName(); 215 | if (pkgName != null && pkgName != "") { 216 | return true; 217 | } 218 | return false; 219 | } 220 | } -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/analyze/AnalyzerClient.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.analyze; 2 | 3 | import java.io.File; 4 | import java.io.FileFilter; 5 | import java.util.ArrayList; 6 | 7 | import com.github.monkey.analyzer.model.Abnormality; 8 | import com.github.monkey.analyzer.model.AbnormalityType; 9 | 10 | /** 11 | * An facade of this analyzing engine, make it easier to use.
12 | *
13 | * Responsibilities are as below.
14 | * 1.  define all the analyzing configuration
15 | * 2.  using all the configurations to generate pure or unprocessed 16 | * abnormalities
17 | * 18 | * @author Alex Chen (apack1001@gmail.com) 19 | * 20 | */ 21 | public class AnalyzerClient { 22 | /** 23 | * handle of AnalyzerInvokr, which represent it's invoked or not 24 | */ 25 | boolean invokeHandle = false; 26 | 27 | /** 28 | * handle of AnalyzerInvoker 29 | */ 30 | AnalyzerInvoker invoker = null; 31 | 32 | private static ArrayList EMPTY_ABNORMALITIES = new ArrayList(); 33 | 34 | 35 | /** 36 | * initialized 37 | */ 38 | public void init() { 39 | invokeHandle = false; 40 | } 41 | 42 | /** 43 | * Uninitialized 44 | */ 45 | public void uninit() { 46 | invokeHandle = false; 47 | } 48 | 49 | /** 50 | * 根据传入的log文件的路径,遍历其中的log文件,筛选出已知类型的异常文件 51 | * 52 | * @param invoker 53 | * instance of an {@link AnalyzerInvoker} 54 | * 55 | * @return an {@link ArrayList} which store all the 56 | * known abnormalities 57 | */ 58 | public ArrayList getKnownAbnormalities() { 59 | 60 | if (invokeHandle == false) 61 | throw new IllegalStateException( 62 | "you must call this method after analyzer"); 63 | 64 | if (invoker != null) 65 | return invoker.getKnownAbnormalities(); 66 | 67 | return EMPTY_ABNORMALITIES; 68 | } 69 | /** 70 | * Call this method will return all the unknown abnormalities.
71 | * This method must be called after {@link #invokeAnalyze} 72 | * 73 | * @return an {@link ArrayList} of {@link Abnormality} 74 | */ 75 | public ArrayList getUnknownAbnormalities() { 76 | if (invokeHandle == false) 77 | throw new IllegalStateException( 78 | "you must call this method after analyzer"); 79 | 80 | if (invoker != null) 81 | return invoker.getUnknownAbnormalities(); 82 | return EMPTY_ABNORMALITIES; 83 | } 84 | 85 | 86 | public void analyze(String logFilesPath, 87 | String monkeyLogFilename, String bugreportLogFilename, 88 | String tracesFilename, String logcatFilename, 89 | String propertiesFilename, String pkgName) { 90 | invoker = invokeAnalyzer(logFilesPath, monkeyLogFilename, 91 | bugreportLogFilename, tracesFilename, logcatFilename, 92 | propertiesFilename, pkgName); 93 | } 94 | 95 | private AnalyzerInvoker invokeAnalyzer(String logFilesPath, 96 | String monkeyLogFilename, String bugreportLogFilename, 97 | String tracesFilename, String logcatFilename, 98 | String propertiesFilename, String pkgName) { 99 | // ANR 100 | AnalyzerConfiguration anrConfig = getANRAnalyzingConfiguration( 101 | monkeyLogFilename, bugreportLogFilename, tracesFilename, 102 | logcatFilename, propertiesFilename, pkgName); 103 | IAnalyzer anrAnalyzer = new CrashOrAnrAnalyzer(anrConfig); 104 | 105 | // CRASH 106 | AnalyzerConfiguration crashConfig = getCrashAnalyzingConfiguration( 107 | monkeyLogFilename, bugreportLogFilename, tracesFilename, 108 | logcatFilename, propertiesFilename, pkgName); 109 | IAnalyzer crashAnalyzer = new CrashOrAnrAnalyzer(crashConfig); 110 | 111 | // Native CRASH 112 | AnalyzerConfiguration nativeCrashConfig = getNativeCrashAnalyzingConfiguration( 113 | monkeyLogFilename, bugreportLogFilename, tracesFilename, 114 | logcatFilename, propertiesFilename, pkgName); 115 | IAnalyzer nativeCrashAnalyzer = new NativeCrashAnalyzer(nativeCrashConfig); 116 | 117 | AnalyzerInvoker invoker = new AnalyzerInvoker(); 118 | 119 | File[] files = getAbnormalitiesDirectories(logFilesPath); 120 | 121 | for (int i = 0; files != null && i < files.length; i++) { 122 | invoker.addPath(files[i].getAbsolutePath() + File.separator); 123 | } 124 | invoker.registerAnalyzer(anrAnalyzer); 125 | invoker.registerAnalyzer(crashAnalyzer); 126 | invoker.registerAnalyzer(nativeCrashAnalyzer); 127 | invoker.analyze(); 128 | 129 | invokeHandle = true; 130 | 131 | return invoker; 132 | } 133 | 134 | /** 135 | * Default implementation of the configuration to analyze CRASH-type 136 | * abnormality 137 | * 138 | * @param monkeyLogFilename 139 | * android monkey file name pattern 140 | * @param bugreportLogFilename 141 | * android bugreport file name pattern 142 | * @param tracesFilename 143 | * android trace file name pattern, usually used to analyze ANR 144 | * @param logcatFilename 145 | * android logcat file name pattern 146 | * @param propertiesFilename 147 | * monkey running results and device info 148 | * @param pkgName 149 | * the package name of the target package 150 | * @return instance of a configuration 151 | */ 152 | private AnalyzerConfiguration getCrashAnalyzingConfiguration( 153 | String monkeyLogFilename, String bugreportLogFilename, 154 | String tracesFilename, String logcatFilename, 155 | String propertiesFilename, String pkgName) { 156 | AnalyzerConfiguration crashConfig = new AnalyzerConfiguration.Builder() 157 | .setStartPattern("// CRASH: ").setEndPattern("^// $") 158 | .setType(AbnormalityType.CRASH) 159 | .setMonkeyLogFileName(monkeyLogFilename) 160 | .setBugReportFileName(bugreportLogFilename) 161 | .setTracesFileName(tracesFilename) 162 | .setLogcatFileName(logcatFilename) 163 | .setPropertiesFileName(propertiesFilename) 164 | .setContentLengthBeforeStartPattern(10) 165 | .setContentLengthAfterEndPattern(10) 166 | .setPackageName(pkgName) 167 | .build(); 168 | return crashConfig; 169 | } 170 | 171 | /** 172 | * Default implementation of the configuration to analyze ANR-type 173 | * abnormality 174 | * 175 | * @param monkeyLogFilename 176 | * android monkey file name pattern 177 | * @param bugreportLogFilename 178 | * android bugreport file name pattern 179 | * @param tracesFilename 180 | * android trace file name pattern, usually used to analyze ANR 181 | * @param logcatFilename 182 | * android logcat file name pattern 183 | * @param propertiesFilename 184 | * monkey running results and device info 185 | * @param pkgName 186 | * the package name of the target package 187 | * @return instance of a configuration 188 | */ 189 | private AnalyzerConfiguration getANRAnalyzingConfiguration( 190 | String monkeyLogFilename, String bugreportLogFilename, 191 | String tracesFilename, String logcatFilename, 192 | String propertiesFilename, String pkgName) { 193 | AnalyzerConfiguration anrConfig = new AnalyzerConfiguration.Builder() 194 | .setStartPattern("// NOT RESPONDING") 195 | .setEndPattern("// NOT RESPONDING") 196 | .setType(AbnormalityType.ANR) 197 | .setContentLengthBeforeStartPattern(10) 198 | .setContentLengthAfterEndPattern(10) 199 | .setMonkeyLogFileName(monkeyLogFilename) 200 | .setBugReportFileName(bugreportLogFilename) 201 | .setTracesFileName(tracesFilename) 202 | .setLogcatFileName(logcatFilename) 203 | .setPropertiesFileName(propertiesFilename) 204 | .setPackageName(pkgName) 205 | .build(); 206 | return anrConfig; 207 | } 208 | 209 | 210 | /** 211 | * Default implementation of the configuration to analyze ANR-type 212 | * abnormality 213 | * 214 | * @param monkeyLogFilename 215 | * android monkey file name pattern 216 | * @param bugreportLogFilename 217 | * android bugreport file name pattern 218 | * @param tracesFilename 219 | * android trace file name pattern, usually used to analyze ANR 220 | * @param logcatFilename 221 | * android logcat file name pattern 222 | * @param propertiesFilename 223 | * monkey running results and device info 224 | * @param pkgName 225 | * the package name of the target package 226 | * @return instance of a configuration 227 | */ 228 | private AnalyzerConfiguration getNativeCrashAnalyzingConfiguration( 229 | String monkeyLogFilename, String bugreportLogFilename, 230 | String tracesFilename, String logcatFilename, 231 | String propertiesFilename, String pkgName) { 232 | AnalyzerConfiguration nativeCrashConfig = new AnalyzerConfiguration.Builder() 233 | .setStartPattern("\\*\\* New native crash detected") 234 | .setEndPattern("\\*\\* New native crash detected.") 235 | .setType(AbnormalityType.NATIVE) 236 | .setMonkeyLogFileName(monkeyLogFilename) 237 | .setBugReportFileName(bugreportLogFilename) 238 | .setTracesFileName(tracesFilename) 239 | .setLogcatFileName(logcatFilename) 240 | .setPropertiesFileName(propertiesFilename) 241 | .setPackageName(pkgName) 242 | .build(); 243 | return nativeCrashConfig; 244 | } 245 | 246 | 247 | /** 248 | * Get All the abnormality directories count 249 | * 250 | * @param path 251 | * abnormalities file path 252 | * @return all directories count 253 | */ 254 | public int getAbnormalitiesDirectoriesCount(final String path) { 255 | File[] paths = getAbnormalitiesDirectories(path); 256 | return paths == null ? 0 : paths.length; 257 | } 258 | 259 | /** 260 | * Get All the abnormality directories 261 | * 262 | * @param path 263 | * abnormalities file path 264 | * @return all directories 265 | */ 266 | private File[] getAbnormalitiesDirectories(final String path) { 267 | if (path == null || path == "") 268 | throw new RuntimeException("Illegal File Path"); 269 | 270 | File logDir = new File(path); 271 | if (logDir == null || !logDir.isDirectory() || !logDir.exists()) 272 | return null; 273 | File[] dirs = logDir.listFiles(new FileFilter() { 274 | 275 | @Override 276 | public boolean accept(File pathname) { 277 | if (pathname.isDirectory()) 278 | return true; 279 | 280 | return false; 281 | } 282 | }); 283 | return dirs; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/report/JSONReport2HtmlReport.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.report; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileOutputStream; 7 | import java.io.InputStreamReader; 8 | import java.io.OutputStreamWriter; 9 | 10 | import org.json.JSONArray; 11 | import org.json.JSONObject; 12 | 13 | import com.github.monkey.analyzer.analyze.Constants; 14 | 15 | /** 16 | * 17 | * @author herongtian -- handle the major implementation 18 | * @author Alex Chen (apack1001@gmail.com) -- handle the refactoring tasks, make it more readable 19 | * 20 | * This class 21 | * 22 | */ 23 | final public class JSONReport2HtmlReport { 24 | 25 | /** 26 | * Construct the HTML content header. 27 | */ 28 | private final static String getHtmlHeader(final String encoding) { 29 | final String headStr = "" 30 | + "Android Monkey Report
"; 42 | return headStr; 43 | } 44 | 45 | /** 46 | * Convert a JSON-format String report to a HTML-format report file. 47 | * 48 | * @param jSONFormatStringReport 49 | * a JSON-format String 50 | * @param htmlReportFilePath 51 | * the path of target HTML report file 52 | * @param encoding 53 | * the coding of target HTML report file 54 | */ 55 | public static void toHTMLReport(String jSONFormatStringReport, 56 | String htmlReportFilePath, String encoding) { 57 | try { 58 | OutputStreamWriter out = new OutputStreamWriter( 59 | new FileOutputStream(htmlReportFilePath), encoding); 60 | JSONObject monkeyResultJSONObject = new JSONObject( 61 | jSONFormatStringReport); 62 | String tab1 = getHtmlHeader(encoding); 63 | String nametd = ""; 64 | String valuetd = ""; 65 | 66 | tab1 += "
执行结果信息汇总
基本信息
" 67 | + "
\n"; 68 | String executor = "-"; 69 | if (monkeyResultJSONObject.has(Constants.JSONReport.KEY_EXECUTOR)) 70 | executor = monkeyResultJSONObject 71 | .getString(Constants.JSONReport.KEY_EXECUTOR); 72 | tab1 += "" + nametd + "执行人" + valuetd + executor 73 | + "\n"; 74 | // tab1 += "" + nametd + "手机类型" + valuetd 75 | // + 76 | // monkeyResult.getString(JSONReportConstants.JSONReport.KEY_MOBILE_PHONE_TYPE) 77 | // + "\n"; 78 | // tab1 += "" + nametd + "开始时间" + valuetd 79 | // + monkeyResult.getString(JSONReportConstants.JSONReport.KEY_START_TIME) 80 | // + "\n"; 81 | // tab1 += "" + nametd + "结束时间" + valuetd 82 | // + monkeyResult.getString(JSONReportConstants.JSONReport.KEY_END_TIME) 83 | // + "\n"; 84 | String duration = "-"; 85 | if (monkeyResultJSONObject.has(Constants.JSONReport.KEY_DURATION)) 86 | duration = monkeyResultJSONObject 87 | .getString(Constants.JSONReport.KEY_DURATION); 88 | tab1 += "" + nametd + "总执行时间" + valuetd + duration 89 | + "\n"; 90 | String average = "-"; 91 | if (monkeyResultJSONObject.has(Constants.JSONReport.KEY_AVERAGE)) 92 | average = monkeyResultJSONObject 93 | .getString(Constants.JSONReport.KEY_AVERAGE); 94 | tab1 += "" + nametd + "平均时长(出现Crash/ANR取所有异常停止执行时间之平均值)" + valuetd + average 95 | + "\n"; 96 | String productName = "-"; 97 | if (monkeyResultJSONObject 98 | .has(Constants.JSONReport.KEY_PRODUCT_NAME)) 99 | productName = monkeyResultJSONObject 100 | .getString(Constants.JSONReport.KEY_PRODUCT_NAME); 101 | tab1 += "" + nametd + "产品名称" + valuetd + productName 102 | + "\n"; 103 | String productVer = "-"; 104 | if (monkeyResultJSONObject.has(Constants.JSONReport.KEY_VERSION)) 105 | productVer = monkeyResultJSONObject 106 | .getString(Constants.JSONReport.KEY_VERSION); 107 | tab1 += "" + nametd + "产品版本" + valuetd + productVer 108 | + "\n"; 109 | String phoneInfo = "-"; 110 | if (monkeyResultJSONObject 111 | .has(Constants.JSONReport.KEY_MOBILE_PLATFORM)) 112 | phoneInfo = monkeyResultJSONObject 113 | .getString(Constants.JSONReport.KEY_MOBILE_PLATFORM); 114 | tab1 += "" + nametd + "手机信息" + valuetd + phoneInfo 115 | + "\n"; 116 | int anrCount = 0; 117 | if (monkeyResultJSONObject.has(Constants.JSONReport.KEY_ANR_NUMBER)) 118 | anrCount = monkeyResultJSONObject 119 | .getInt(Constants.JSONReport.KEY_ANR_NUMBER); 120 | tab1 += "" + nametd + "ANR数量" + valuetd + anrCount 121 | + "\n"; 122 | int crashCount = 0; 123 | if (monkeyResultJSONObject 124 | .has(Constants.JSONReport.KEY_CRASH_NUMBER)) 125 | crashCount = monkeyResultJSONObject 126 | .getInt(Constants.JSONReport.KEY_CRASH_NUMBER); 127 | tab1 += "" + nametd + "CRASH数量" + valuetd + crashCount 128 | + ""; 129 | 130 | int nativeCrashCount = 0; 131 | if (monkeyResultJSONObject 132 | .has(Constants.JSONReport.KEY_NATIVE_CRASH_NUMBER)) 133 | nativeCrashCount = monkeyResultJSONObject 134 | .getInt(Constants.JSONReport.KEY_NATIVE_CRASH_NUMBER); 135 | tab1 += "" + nametd + "Native CRASH数量" + valuetd + nativeCrashCount 136 | + "" + "
\n"; 137 | 138 | tab1 += "
详细信息
\n" 139 | + "" 140 | + "" 141 | + "" 142 | + "" 143 | + "" 144 | + "" 145 | + "" 146 | + "" 147 | // + 148 | // "" 149 | // + 150 | // "" 151 | // + 152 | // "\n" 153 | ; 154 | if (monkeyResultJSONObject 155 | .has(Constants.JSONReport.KEY_ABNORMALITIS)) { 156 | 157 | JSONArray abnormalities = monkeyResultJSONObject 158 | .getJSONArray(Constants.JSONReport.KEY_ABNORMALITIS); 159 | // String startUrl1 = 160 | // ""; 164 | for (int i = 0; i < abnormalities.length(); i++) { 165 | 166 | JSONObject json = abnormalities.getJSONObject(i); 167 | 168 | String type = "-"; 169 | if (json.has(Constants.JSONReport.KEY_ABNORMALITIS_TYPE)) 170 | type = json 171 | .getString(Constants.JSONReport.KEY_ABNORMALITIS_TYPE); 172 | String shortMsg = "-"; 173 | 174 | if (json.has(Constants.JSONReport.KEY_ABNORMALITIS_SHORT_MSG)) 175 | shortMsg = json 176 | .getString(Constants.JSONReport.KEY_ABNORMALITIS_SHORT_MSG); 177 | String msg = "-"; 178 | if (json.has(Constants.JSONReport.KEY_ABNORMALITIS_MSG)) 179 | msg = json 180 | .getString(Constants.JSONReport.KEY_ABNORMALITIS_MSG); 181 | int count = 0; 182 | if (json.has(Constants.JSONReport.KEY_ABNORMALITIS_COUNT)) 183 | count = json 184 | .getInt(Constants.JSONReport.KEY_ABNORMALITIS_COUNT); 185 | String durations = "-"; 186 | if (json.has(Constants.JSONReport.KEY_ABNORMALITIS_DURATIONS)) 187 | durations = json 188 | .getString(Constants.JSONReport.KEY_ABNORMALITIS_DURATIONS); 189 | // String traceUrl = 190 | // json.getString(JSONReportConstants.JSONReport.KEY_ABNORMALITIS_TRACE_URL); 191 | // String logcatUrl = 192 | // json.getString(JSONReportConstants.JSONReport.KEY_ABNORMALITIS_LOGCAT_URL); 193 | // String bugUrl = 194 | // json.getString(JSONReportConstants.JSONReport.KEY_ABNORMALITIS_BUGREPORT_URL); 195 | String zip = "-"; 196 | if (json.has(Constants.JSONReport.KEY_ABNORMALITIES_ZIP_URL)) { 197 | zip = json 198 | .getString(Constants.JSONReport.KEY_ABNORMALITIES_ZIP_URL); 199 | } 200 | tab1 += "\n"; 213 | } 214 | } 215 | tab1 += "
TypeShort MessageNumDurationMessageLog
Bug ReportLogcatTrace
go
" + type 201 | + "" 202 | + shortMsg 203 | + "" 204 | + count 205 | + "" 206 | + durations 207 | + "" + msg 208 | + "" + zip 209 | // + startUrl1 + bugUrl + endUrl + startUrl1 + 210 | // traceUrl 211 | // + endUrl + startUrl2 + logcatUrl + endUrl 212 | + "
"; 216 | out.write(tab1); 217 | out.close(); 218 | } catch (Exception e) { 219 | e.printStackTrace(); 220 | System.out 221 | .println("invalid JSON file type,please check definition of your JSONArrays!"); 222 | } 223 | } 224 | 225 | /** 226 | * Read JSON-format String from 227 | * 228 | * @param path 229 | * the path of the JSON report 230 | */ 231 | @SuppressWarnings("unused") 232 | private static String getJSONString(final String path) { 233 | String jsonString = ""; 234 | try { 235 | File f = new File(path); 236 | if (f.exists() && f.isFile()) { 237 | InputStreamReader read = new InputStreamReader( 238 | new FileInputStream(f)); 239 | BufferedReader bReader = new BufferedReader(read); 240 | String line = null; 241 | 242 | while ((line = bReader.readLine()) != null) { 243 | jsonString += line; 244 | } 245 | 246 | bReader.close(); 247 | read.close(); 248 | 249 | } else 250 | System.out.println("invalid JSON file path!"); 251 | 252 | } catch (Exception e) { 253 | System.out.println("error accures while reading the JSON file!"); 254 | } 255 | 256 | return jsonString; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/AndroidMonkeyRunningAdapter/src/com/github/monkey/runner/scheduler/MonkeyTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.github.monkey.runner.scheduler; 3 | 4 | import java.io.BufferedWriter; 5 | import java.io.FileWriter; 6 | import java.text.DateFormat; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.UUID; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | import com.android.ddmlib.IDevice; 15 | import com.android.ddmlib.MultiLineReceiver; 16 | import com.android.ddmuilib.log.event.BugReportImporter; 17 | import com.github.monkey.runner.helper.LocationHelper; 18 | import com.github.monkey.runner.helper.PropertiesHelper; 19 | import com.github.monkey.runner.helper.SharedProperties; 20 | import com.github.monkey.runner.helper.ZipHelper; 21 | import com.github.monkey.runner.helper.LocationHelper.FileLocationException; 22 | 23 | /** 24 | * Represents a item of monkey test. 25 | * 26 | */ 27 | public class MonkeyTest { 28 | 29 | final String mTestId; 30 | final String mSeriesId; 31 | final MonkeyTestDevice mDevice; 32 | final String mCommand; 33 | final String mUserName; 34 | final String mPkgName; 35 | final String mPkgVersion; 36 | final String mInitFilePath; 37 | final long mSeriesExpectDuration; 38 | final long mSingleExpectDuration; 39 | 40 | long mActualDuration = 0; 41 | long mStart = 0; 42 | long mFinish = 0; 43 | boolean mIsStarted = false; 44 | boolean mIsFinished = false; 45 | 46 | MonkeyTestItemExecutor mExecutor; 47 | 48 | public MonkeyTest( 49 | MonkeyTestDevice device, 50 | String seriesId, 51 | String pkgName, 52 | String pkgVersion, 53 | String command, 54 | String userName, 55 | String initFilePath, 56 | long singleExpectDuration, 57 | long seriesExpectDuration) { 58 | mTestId = UUID.randomUUID().toString(); 59 | mSeriesId = seriesId; 60 | mDevice = device; 61 | mPkgName = pkgName; 62 | mPkgVersion = pkgVersion; 63 | mCommand = command; 64 | mUserName = userName; 65 | mInitFilePath = initFilePath; 66 | mSingleExpectDuration = singleExpectDuration; 67 | mSeriesExpectDuration = seriesExpectDuration; 68 | } 69 | 70 | public boolean isStarted() { 71 | return mIsStarted; 72 | } 73 | 74 | public boolean isFinished() { 75 | return mIsFinished; 76 | } 77 | 78 | public void start() { 79 | if (mExecutor != null) { 80 | return; 81 | } 82 | mExecutor = new MonkeyTestItemExecutor(mTestId); 83 | mExecutor.start(); 84 | mIsStarted = true; 85 | } 86 | 87 | public void interrupt() { 88 | mIsFinished = true; 89 | if (!mExecutor.isInterrupted()) { 90 | mExecutor.interrupt(); 91 | } 92 | } 93 | 94 | public class MonkeyTestItemExecutor extends Thread { 95 | 96 | private LogOutputReceiver mMonkeyLogOutputReceiver; 97 | private LogOutputReceiver mLogcatLogOutputReceiver; 98 | private LogOutputReceiver mTracesLogOutputReceiver; 99 | private LogOutputReceiver mBugreportOutputReceiver; 100 | 101 | public MonkeyTestItemExecutor(String name) { 102 | super(name); 103 | } 104 | 105 | void log(String msg) { 106 | Console.printLogMessage(mDevice.getSerialNumber(), msg); 107 | } 108 | 109 | void startMonkeyThread() { 110 | try { 111 | String f = LocationHelper.getMonkeyLogLocation(mTestId); 112 | mMonkeyLogOutputReceiver = new LogOutputReceiver(f); 113 | Thread t = new Thread(new Runnable() { 114 | @Override 115 | public void run() { 116 | mDevice.monkey(mCommand, mMonkeyLogOutputReceiver); 117 | mMonkeyLogOutputReceiver.cancel(); 118 | } 119 | }); 120 | t.start(); 121 | } catch (Exception ex) { 122 | // Pass 123 | } 124 | } 125 | 126 | void startLogcatThread() { 127 | try { 128 | String f = LocationHelper.getLogcatLogLocation(mTestId); 129 | mLogcatLogOutputReceiver = new LogOutputReceiver(f); 130 | Thread t = new Thread(new Runnable() { 131 | @Override 132 | public void run() { 133 | mDevice.logcat(mLogcatLogOutputReceiver); 134 | mLogcatLogOutputReceiver.cancel(); 135 | } 136 | }); 137 | t.start(); 138 | } catch (Exception ex) { 139 | // Pass 140 | } 141 | } 142 | 143 | void startBugreport() { 144 | try { 145 | String bugreportPath = LocationHelper.getBugreportLocation(mTestId); 146 | mBugreportOutputReceiver = new LogOutputReceiver(bugreportPath); 147 | mDevice.bugreport(mBugreportOutputReceiver); 148 | } catch (Exception e) { 149 | 150 | } 151 | } 152 | 153 | void startTraces() { 154 | try { 155 | String tracesPath = LocationHelper.getTracesLocation(mTestId); 156 | mTracesLogOutputReceiver = new LogOutputReceiver(tracesPath); 157 | mDevice.traces(mTracesLogOutputReceiver); 158 | } catch (Exception e) { 159 | 160 | } 161 | 162 | } 163 | 164 | @Override 165 | public void run() { 166 | try { 167 | log("Monkey test started."); 168 | 169 | // wait for device 170 | log("Wating for device 60 seconds"); 171 | TimeUnit.SECONDS.sleep(60); 172 | if (mDevice.connect()) { 173 | log("Device connected success"); 174 | } else { 175 | log("Device not found, monkey test not started"); 176 | mIsFinished = true; 177 | return; 178 | } 179 | 180 | // unlock screen for device 181 | String msg = mDevice.initialize(mInitFilePath); 182 | log("Execute init script (" + msg + ")."); 183 | 184 | // monkey 185 | mStart = System.currentTimeMillis(); 186 | saveProperties(false); 187 | startMonkeyThread(); 188 | startLogcatThread(); 189 | log("Monkey test started"); 190 | while (true) { 191 | TimeUnit.SECONDS.sleep(1); 192 | if (mMonkeyLogOutputReceiver.isCancelled()) { 193 | log("Monkey test finished, since monkey log output break."); 194 | break; 195 | } 196 | if (mLogcatLogOutputReceiver.isCancelled()) { 197 | log("Monkey test finished, since logcat log output break."); 198 | break; 199 | } 200 | mFinish = System.currentTimeMillis(); 201 | mActualDuration = mFinish - mStart; 202 | if (mActualDuration >= mSingleExpectDuration) { 203 | mActualDuration = mSingleExpectDuration; 204 | log("Monkey test finished, since reach ecpect duration"); 205 | break; 206 | } 207 | } 208 | mMonkeyLogOutputReceiver.cancel(); 209 | mLogcatLogOutputReceiver.cancel(); 210 | log(String.format("Monkey test lasts for %s ms", 211 | mActualDuration)); 212 | 213 | Map props = saveProperties(); 214 | // save bugreport and trace 215 | startBugreport(); 216 | startTraces(); 217 | // zip & upload 218 | String zipSrc = LocationHelper.getLogsLocation(mTestId); 219 | String zipDes = LocationHelper.getZipLocation(mTestId); 220 | ZipHelper.zip(zipSrc, zipDes); 221 | 222 | SharedProperties.getInstance().add(props); 223 | // TODO: Upload 224 | 225 | } catch (InterruptedException ie) { 226 | // Pass 227 | } catch (Exception ex) { 228 | log(ex.getMessage()); 229 | } finally { 230 | // // reboot 231 | // log(String.format("Rebooted! (%s)", mDevice.reboot())); 232 | try { 233 | TimeUnit.SECONDS.sleep(5); 234 | } catch (InterruptedException e) { 235 | // Pass 236 | } 237 | mIsFinished = true; 238 | log("Monkey test finished."); 239 | } 240 | } 241 | 242 | private Map saveProperties() 243 | throws FileLocationException { 244 | return saveProperties(true); 245 | } 246 | 247 | private Map saveProperties(boolean shouldSaveFinishTime) 248 | throws FileLocationException { 249 | // store monkey properties 250 | Map props = new HashMap(); 251 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 252 | 253 | props.put("test.id", mTestId);//$NON-NLS-1$ 254 | props.put("test.start", df.format(new Date(mStart)));//$NON-NLFFS-1$ 255 | props.put("test.finish", df.format(new Date(mFinish)));//$NON-NLS-1$ 256 | props.put("test.duration.expect", String.valueOf(mSingleExpectDuration / 1000));//$NON-NLS-1$ 257 | 258 | if (shouldSaveFinishTime) 259 | props.put("test.duration.actual", String.valueOf(mActualDuration / 1000));//$NON-NLS-1$ 260 | 261 | props.put("series.id", mSeriesId);//$NON-NLS-1$ 262 | props.put("series.duration.expect", String.valueOf(mSeriesExpectDuration / 1000));//$NON-NLS-1$ 263 | 264 | props.put("apk.package", mPkgName);//$NON-NLS-1$ 265 | props.put("apk.version", mPkgVersion);//$NON-NLS-1$ 266 | 267 | props.put("command", mCommand);//$NON-NLS-1$ 268 | props.put("executor", mUserName);//$NON-NLS-1$ 269 | props.put("status", mActualDuration < mSingleExpectDuration ? "FAIL" : "PASS");//$NON-NLS-1$ 270 | 271 | PropertiesHelper.setProperties(mTestId, "monkey", props, false); 272 | 273 | // store device properties 274 | props = mDevice.getProperties(); 275 | PropertiesHelper.setProperties(mTestId, "device", props, true); 276 | 277 | log(String.format("Log store at %s", 278 | LocationHelper.getLogsLocation(mTestId))); 279 | return props; 280 | } 281 | 282 | /** 283 | * LogOutputReceiver implements 284 | * {@link MultiLineReceiver#processNewLines(String[])}, which is called 285 | * whenever there is output from log. This class is expected to be used 286 | * from a different thread, and the only way to stop that thread is by 287 | * using the {@link LogOutputReceiver#mIsCancelled} variable. See 288 | * {@link IDevice#executeShellCommand(String, IShellOutputReceiver, int)} 289 | * for more details. 290 | */ 291 | private class LogOutputReceiver extends MultiLineReceiver { 292 | 293 | private boolean mIsCancelled; 294 | private String mFileName; 295 | 296 | public LogOutputReceiver(String fileName) { 297 | mFileName = fileName; 298 | setTrimLine(false); 299 | } 300 | 301 | /** Implements {@link IShellOutputReceiver#isCancelled() }. */ 302 | @Override 303 | public boolean isCancelled() { 304 | return mIsCancelled; 305 | } 306 | 307 | @Override 308 | public void processNewLines(String[] lines) { 309 | if (!mIsCancelled) { 310 | try { 311 | FileWriter fw = new FileWriter(mFileName, true); 312 | BufferedWriter bw = new BufferedWriter(fw); 313 | for (String line : lines) { 314 | bw.write(line.toString()); 315 | bw.newLine(); 316 | } 317 | bw.close(); 318 | fw.close(); 319 | } catch (Exception ex) { 320 | // Pass 321 | } 322 | } 323 | } 324 | 325 | public void cancel() { 326 | mIsCancelled = true; 327 | } 328 | } 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /dist/logs/c6103ba0-5c61-4a18-abd9-ec3acbaf23cd/monkey_log.txt: -------------------------------------------------------------------------------- 1 | :Monkey: seed=1331320713742931433 count=99999 2 | :AllowPackage: com.example 3 | :IncludeCategory: android.intent.category.MONKEY 4 | :IncludeCategory: android.intent.category.LAUNCHER 5 | :IncludeCategory: android.intent.category.DEFAULT 6 | // Selecting main activities from category android.intent.category.MONKEY 7 | // - NOT USING main activity com.android.launcher2.Launcher (from package com.android.launcher) 8 | // - NOT USING main activity com.android.settings.applications.ManageApplications (from package com.android.settings) 9 | // - NOT USING main activity com.android.settings.RunningServices (from package com.android.settings) 10 | // - NOT USING main activity com.android.settings.applications.StorageUse (from package com.android.settings) 11 | // - NOT USING main activity com.android.quicksearchbox.google.GoogleSearch (from package com.android.quicksearchbox) 12 | // Selecting main activities from category android.intent.category.LAUNCHER 13 | // - NOT USING main activity com.android.music.MusicBrowserActivity (from package com.android.music) 14 | // - NOT USING main activity com.android.camera.GalleryPicker (from package com.android.gallery) 15 | // - NOT USING main activity com.android.mms.ui.ConversationList (from package com.android.mms) 16 | // - NOT USING main activity com.android.spare_parts.SpareParts (from package com.android.spare_parts) 17 | // - NOT USING main activity com.android.settings.Settings (from package com.android.settings) 18 | // - NOT USING main activity com.android.deskclock.DeskClock (from package com.android.deskclock) 19 | // - NOT USING main activity com.android.browser.BrowserActivity (from package com.android.browser) 20 | // - NOT USING main activity com.android.contacts.DialtactsActivity (from package com.android.contacts) 21 | // - NOT USING main activity com.android.contacts.DialtactsContactsEntryActivity (from package com.android.contacts) 22 | // - NOT USING main activity com.android.camera.Camera (from package com.android.camera) 23 | // - NOT USING main activity com.android.email.activity.Welcome (from package com.android.email) 24 | // - NOT USING main activity com.android.gesture.builder.GestureBuilderActivity (from package com.android.gesture.builder) 25 | // - NOT USING main activity com.example.android.apis.ApiDemos (from package com.example.android.apis) 26 | // - NOT USING main activity com.android.quicksearchbox.SearchActivity (from package com.android.quicksearchbox) 27 | // - NOT USING main activity com.android.providers.downloads.ui.DownloadList (from package com.android.providers.downloads.ui) 28 | // - NOT USING main activity com.android.customlocale.CustomLocaleActivity (from package com.android.customlocale) 29 | // - NOT USING main activity com.android.speechrecorder.SpeechRecorderActivity (from package com.android.speechrecorder) 30 | // - NOT USING main activity com.android.calculator2.Calculator (from package com.android.calculator2) 31 | // - NOT USING main activity com.android.development.Development (from package com.android.development) 32 | // + Using main activity com.example.MainActivity (from package com.example) 33 | // Selecting main activities from category android.intent.category.DEFAULT 34 | // - NOT USING main activity com.android.music.MusicBrowserActivity (from package com.android.music) 35 | // - NOT USING main activity com.android.inputmethod.latin.InputLanguageSelection (from package com.android.inputmethod.latin) 36 | // - NOT USING main activity com.android.camera.GalleryPicker (from package com.android.gallery) 37 | // - NOT USING main activity com.android.camera.ImageGallery (from package com.android.gallery) 38 | // - NOT USING main activity com.android.settings.ManageAccountsSettings (from package com.android.providers.subscribedfeeds) 39 | // - NOT USING main activity com.android.settings.AccountSyncSettings (from package com.android.providers.subscribedfeeds) 40 | // - NOT USING main activity com.android.settings.AccountSyncSettingsInAddAccount (from package com.android.providers.subscribedfeeds) 41 | // - NOT USING main activity com.android.settings.AddAccountSettings (from package com.android.providers.subscribedfeeds) 42 | // - NOT USING main activity com.android.settings.SyncActivityTooManyDeletes (from package com.android.providers.subscribedfeeds) 43 | // - NOT USING main activity com.android.launcher2.Launcher (from package com.android.launcher) 44 | // - NOT USING main activity com.android.mms.ui.ConversationList (from package com.android.mms) 45 | // - NOT USING main activity com.android.spare_parts.SpareParts (from package com.android.spare_parts) 46 | // - NOT USING main activity com.android.settings.Settings (from package com.android.settings) 47 | // - NOT USING main activity com.android.settings.WirelessSettings (from package com.android.settings) 48 | // - NOT USING main activity com.android.settings.wifi.WifiSettings (from package com.android.settings) 49 | // - NOT USING main activity com.android.settings.wifi.AdvancedSettings (from package com.android.settings) 50 | // - NOT USING main activity com.android.settings.wifi.WifiInfo (from package com.android.settings) 51 | // - NOT USING main activity com.android.settings.wifi.WifiConfigInfo (from package com.android.settings) 52 | // - NOT USING main activity com.android.settings.wifi.WifiAPITest (from package com.android.settings) 53 | // - NOT USING main activity com.android.settings.wifi.WifiStatusTest (from package com.android.settings) 54 | // - NOT USING main activity com.android.settings.wifi.WifiApSettings (from package com.android.settings) 55 | // - NOT USING main activity com.android.settings.ApnSettings (from package com.android.settings) 56 | // - NOT USING main activity com.android.settings.bluetooth.BluetoothSettings (from package com.android.settings) 57 | // - NOT USING main activity com.android.settings.TetherSettings (from package com.android.settings) 58 | // - NOT USING main activity com.android.settings.vpn.VpnSettings (from package com.android.settings) 59 | // - NOT USING main activity com.android.settings.DateTimeSettings (from package com.android.settings) 60 | // - NOT USING main activity com.android.settings.LocalePicker (from package com.android.settings) 61 | // - NOT USING main activity com.android.settings.LanguageSettings (from package com.android.settings) 62 | // - NOT USING main activity com.android.settings.PhysicalKeyboardSettings (from package com.android.settings) 63 | // - NOT USING main activity com.android.settings.UserDictionarySettings (from package com.android.settings) 64 | // - NOT USING main activity com.android.settings.SoundSettings (from package com.android.settings) 65 | // - NOT USING main activity com.android.settings.DisplaySettings (from package com.android.settings) 66 | // - NOT USING main activity com.android.settings.DockSettings (from package com.android.settings) 67 | // - NOT USING main activity com.android.settings.DeviceInfoSettings (from package com.android.settings) 68 | // - NOT USING main activity com.android.settings.ApplicationSettings (from package com.android.settings) 69 | // - NOT USING main activity com.android.settings.applications.ManageApplications (from package com.android.settings) 70 | // - NOT USING main activity com.android.settings.RunningServices (from package com.android.settings) 71 | // - NOT USING main activity com.android.settings.applications.StorageUse (from package com.android.settings) 72 | // - NOT USING main activity com.android.settings.SecuritySettings (from package com.android.settings) 73 | // - NOT USING main activity com.android.settings.PrivacySettings (from package com.android.settings) 74 | // - NOT USING main activity com.android.settings.DeviceAdminSettings (from package com.android.settings) 75 | // - NOT USING main activity com.android.settings.IccLockSettings (from package com.android.settings) 76 | // - NOT USING main activity com.android.settings.AccessibilitySettings (from package com.android.settings) 77 | // - NOT USING main activity com.android.settings.VoiceInputOutputSettings (from package com.android.settings) 78 | // - NOT USING main activity com.android.settings.TextToSpeechSettings (from package com.android.settings) 79 | // - NOT USING main activity com.android.settings.deviceinfo.Status (from package com.android.settings) 80 | // - NOT USING main activity com.android.settings.deviceinfo.Memory (from package com.android.settings) 81 | // - NOT USING main activity com.android.settings.quicklaunch.QuickLaunchSettings (from package com.android.settings) 82 | // - NOT USING main activity com.android.settings.DevelopmentSettings (from package com.android.settings) 83 | // - NOT USING main activity com.android.settings.BandMode (from package com.android.settings) 84 | // - NOT USING main activity com.android.settings.TestingSettings (from package com.android.settings) 85 | // - NOT USING main activity com.android.settings.fuelgauge.PowerUsageSummary (from package com.android.settings) 86 | // - NOT USING main activity com.android.settings.fuelgauge.PowerUsageDetail (from package com.android.settings) 87 | // - NOT USING main activity com.android.settings.fuelgauge.BatteryHistoryDetail (from package com.android.settings) 88 | // - NOT USING main activity com.android.deskclock.DeskClock (from package com.android.deskclock) 89 | // - NOT USING main activity com.android.browser.BrowserActivity (from package com.android.browser) 90 | // - NOT USING main activity com.android.certinstaller.CertInstaller (from package com.android.certinstaller) 91 | // - NOT USING main activity com.android.certinstaller.CertFileList (from package com.android.certinstaller) 92 | // - NOT USING main activity com.android.netspeed.NetSpeedActivity (from package com.android.netspeed) 93 | // - NOT USING main activity com.android.contacts.DialtactsActivity (from package com.android.contacts) 94 | // - NOT USING main activity com.android.contacts.DialtactsContactsEntryActivity (from package com.android.contacts) 95 | // - NOT USING main activity com.android.contacts.DialtactsFavoritesEntryActivity (from package com.android.contacts) 96 | // - NOT USING main activity com.android.camera.Camera (from package com.android.camera) 97 | // - NOT USING main activity com.android.email.activity.Welcome (from package com.android.email) 98 | // - NOT USING main activity com.android.phone.CallFeaturesSetting (from package com.android.phone) 99 | // - NOT USING main activity com.android.gesture.builder.GestureBuilderActivity (from package com.android.gesture.builder) 100 | // - NOT USING main activity com.example.android.apis.ApiDemos (from package com.example.android.apis) 101 | // Seeded: 1331320713742931433 102 | // Event percentages: 103 | // 0: 43.0% 104 | // 1: 30.0% 105 | // 2: 0.0% 106 | // 3: 0.0% 107 | // 4: 10.0% 108 | // 5: 15.0% 109 | // 6: 2.0% 110 | // 7: 0.0% 111 | // 8: 0.0% 112 | :Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10000000;component=com.example/.MainActivity;end 113 | // Allowing start of Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example/.MainActivity } in package com.example 114 | Sleeping for 300 milliseconds 115 | // CRASH: com.example (pid 987) 116 | // Short Msg: java.lang.NullPointerException 117 | // Long Msg: java.lang.NullPointerException: Unable to start activity ComponentInfo{com.example/com.example.MainActivity}: java.lang.NullPointerException 118 | // Build Label: generic/sdk/generic:2.3.3/GRI34/101070:eng/test-keys 119 | // Build Changelist: 101070 120 | // Build Time: 1296773036000 121 | // java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.MainActivity}: java.lang.NullPointerException 122 | // at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647) 123 | // at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663) 124 | // at android.app.ActivityThread.access$1500(ActivityThread.java:117) 125 | // at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931) 126 | // at android.os.Handler.dispatchMessage(Handler.java:99) 127 | // at android.os.Looper.loop(Looper.java:123) 128 | // at android.app.ActivityThread.main(ActivityThread.java:3683) 129 | // at java.lang.reflect.Method.invokeNative(Native Method) 130 | // at java.lang.reflect.Method.invoke(Method.java:507) 131 | // at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 132 | // at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 133 | // at dalvik.system.NativeStart.main(Native Method) 134 | // Caused by: java.lang.NullPointerException 135 | // at com.android.internal.os.LoggingPrintStream.println(LoggingPrintStream.java:298) 136 | // at com.example.MainActivity.onCreate(MainActivity.java:21) 137 | // at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) 138 | // at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611) 139 | // ... 11 more 140 | // 141 | ** Monkey aborted due to error. 142 | Events injected: 1 143 | :Dropped: keys=0 pointers=0 trackballs=0 flips=0 144 | ## Network stats: elapsed time=1400ms (1400ms mobile, 0ms wifi, 0ms not connected) 145 | ** System appears to have crashed at event 1 of 99999 using seed 1331320713742931433 146 | 147 | -------------------------------------------------------------------------------- /dist/logs/f451e7dc-66c0-4f8e-b7fb-d96402134267/monkey_log.txt: -------------------------------------------------------------------------------- 1 | :Monkey: seed=-2997299013541685614 count=99999 2 | :AllowPackage: com.example 3 | :IncludeCategory: android.intent.category.MONKEY 4 | :IncludeCategory: android.intent.category.LAUNCHER 5 | :IncludeCategory: android.intent.category.DEFAULT 6 | // Selecting main activities from category android.intent.category.MONKEY 7 | // - NOT USING main activity com.android.launcher2.Launcher (from package com.android.launcher) 8 | // - NOT USING main activity com.android.settings.applications.ManageApplications (from package com.android.settings) 9 | // - NOT USING main activity com.android.settings.RunningServices (from package com.android.settings) 10 | // - NOT USING main activity com.android.settings.applications.StorageUse (from package com.android.settings) 11 | // - NOT USING main activity com.android.quicksearchbox.google.GoogleSearch (from package com.android.quicksearchbox) 12 | // Selecting main activities from category android.intent.category.LAUNCHER 13 | // - NOT USING main activity com.android.music.MusicBrowserActivity (from package com.android.music) 14 | // - NOT USING main activity com.android.camera.GalleryPicker (from package com.android.gallery) 15 | // - NOT USING main activity com.android.mms.ui.ConversationList (from package com.android.mms) 16 | // - NOT USING main activity com.android.spare_parts.SpareParts (from package com.android.spare_parts) 17 | // - NOT USING main activity com.android.settings.Settings (from package com.android.settings) 18 | // - NOT USING main activity com.android.deskclock.DeskClock (from package com.android.deskclock) 19 | // - NOT USING main activity com.android.browser.BrowserActivity (from package com.android.browser) 20 | // - NOT USING main activity com.android.contacts.DialtactsActivity (from package com.android.contacts) 21 | // - NOT USING main activity com.android.contacts.DialtactsContactsEntryActivity (from package com.android.contacts) 22 | // - NOT USING main activity com.android.camera.Camera (from package com.android.camera) 23 | // - NOT USING main activity com.android.email.activity.Welcome (from package com.android.email) 24 | // - NOT USING main activity com.android.gesture.builder.GestureBuilderActivity (from package com.android.gesture.builder) 25 | // - NOT USING main activity com.example.android.apis.ApiDemos (from package com.example.android.apis) 26 | // - NOT USING main activity com.android.quicksearchbox.SearchActivity (from package com.android.quicksearchbox) 27 | // - NOT USING main activity com.android.providers.downloads.ui.DownloadList (from package com.android.providers.downloads.ui) 28 | // - NOT USING main activity com.android.customlocale.CustomLocaleActivity (from package com.android.customlocale) 29 | // - NOT USING main activity com.android.speechrecorder.SpeechRecorderActivity (from package com.android.speechrecorder) 30 | // - NOT USING main activity com.android.calculator2.Calculator (from package com.android.calculator2) 31 | // - NOT USING main activity com.android.development.Development (from package com.android.development) 32 | // + Using main activity com.example.MainActivity (from package com.example) 33 | // Selecting main activities from category android.intent.category.DEFAULT 34 | // - NOT USING main activity com.android.music.MusicBrowserActivity (from package com.android.music) 35 | // - NOT USING main activity com.android.inputmethod.latin.InputLanguageSelection (from package com.android.inputmethod.latin) 36 | // - NOT USING main activity com.android.camera.GalleryPicker (from package com.android.gallery) 37 | // - NOT USING main activity com.android.camera.ImageGallery (from package com.android.gallery) 38 | // - NOT USING main activity com.android.settings.ManageAccountsSettings (from package com.android.providers.subscribedfeeds) 39 | // - NOT USING main activity com.android.settings.AccountSyncSettings (from package com.android.providers.subscribedfeeds) 40 | // - NOT USING main activity com.android.settings.AccountSyncSettingsInAddAccount (from package com.android.providers.subscribedfeeds) 41 | // - NOT USING main activity com.android.settings.AddAccountSettings (from package com.android.providers.subscribedfeeds) 42 | // - NOT USING main activity com.android.settings.SyncActivityTooManyDeletes (from package com.android.providers.subscribedfeeds) 43 | // - NOT USING main activity com.android.launcher2.Launcher (from package com.android.launcher) 44 | // - NOT USING main activity com.android.mms.ui.ConversationList (from package com.android.mms) 45 | // - NOT USING main activity com.android.spare_parts.SpareParts (from package com.android.spare_parts) 46 | // - NOT USING main activity com.android.settings.Settings (from package com.android.settings) 47 | // - NOT USING main activity com.android.settings.WirelessSettings (from package com.android.settings) 48 | // - NOT USING main activity com.android.settings.wifi.WifiSettings (from package com.android.settings) 49 | // - NOT USING main activity com.android.settings.wifi.AdvancedSettings (from package com.android.settings) 50 | // - NOT USING main activity com.android.settings.wifi.WifiInfo (from package com.android.settings) 51 | // - NOT USING main activity com.android.settings.wifi.WifiConfigInfo (from package com.android.settings) 52 | // - NOT USING main activity com.android.settings.wifi.WifiAPITest (from package com.android.settings) 53 | // - NOT USING main activity com.android.settings.wifi.WifiStatusTest (from package com.android.settings) 54 | // - NOT USING main activity com.android.settings.wifi.WifiApSettings (from package com.android.settings) 55 | // - NOT USING main activity com.android.settings.ApnSettings (from package com.android.settings) 56 | // - NOT USING main activity com.android.settings.bluetooth.BluetoothSettings (from package com.android.settings) 57 | // - NOT USING main activity com.android.settings.TetherSettings (from package com.android.settings) 58 | // - NOT USING main activity com.android.settings.vpn.VpnSettings (from package com.android.settings) 59 | // - NOT USING main activity com.android.settings.DateTimeSettings (from package com.android.settings) 60 | // - NOT USING main activity com.android.settings.LocalePicker (from package com.android.settings) 61 | // - NOT USING main activity com.android.settings.LanguageSettings (from package com.android.settings) 62 | // - NOT USING main activity com.android.settings.PhysicalKeyboardSettings (from package com.android.settings) 63 | // - NOT USING main activity com.android.settings.UserDictionarySettings (from package com.android.settings) 64 | // - NOT USING main activity com.android.settings.SoundSettings (from package com.android.settings) 65 | // - NOT USING main activity com.android.settings.DisplaySettings (from package com.android.settings) 66 | // - NOT USING main activity com.android.settings.DockSettings (from package com.android.settings) 67 | // - NOT USING main activity com.android.settings.DeviceInfoSettings (from package com.android.settings) 68 | // - NOT USING main activity com.android.settings.ApplicationSettings (from package com.android.settings) 69 | // - NOT USING main activity com.android.settings.applications.ManageApplications (from package com.android.settings) 70 | // - NOT USING main activity com.android.settings.RunningServices (from package com.android.settings) 71 | // - NOT USING main activity com.android.settings.applications.StorageUse (from package com.android.settings) 72 | // - NOT USING main activity com.android.settings.SecuritySettings (from package com.android.settings) 73 | // - NOT USING main activity com.android.settings.PrivacySettings (from package com.android.settings) 74 | // - NOT USING main activity com.android.settings.DeviceAdminSettings (from package com.android.settings) 75 | // - NOT USING main activity com.android.settings.IccLockSettings (from package com.android.settings) 76 | // - NOT USING main activity com.android.settings.AccessibilitySettings (from package com.android.settings) 77 | // - NOT USING main activity com.android.settings.VoiceInputOutputSettings (from package com.android.settings) 78 | // - NOT USING main activity com.android.settings.TextToSpeechSettings (from package com.android.settings) 79 | // - NOT USING main activity com.android.settings.deviceinfo.Status (from package com.android.settings) 80 | // - NOT USING main activity com.android.settings.deviceinfo.Memory (from package com.android.settings) 81 | // - NOT USING main activity com.android.settings.quicklaunch.QuickLaunchSettings (from package com.android.settings) 82 | // - NOT USING main activity com.android.settings.DevelopmentSettings (from package com.android.settings) 83 | // - NOT USING main activity com.android.settings.BandMode (from package com.android.settings) 84 | // - NOT USING main activity com.android.settings.TestingSettings (from package com.android.settings) 85 | // - NOT USING main activity com.android.settings.fuelgauge.PowerUsageSummary (from package com.android.settings) 86 | // - NOT USING main activity com.android.settings.fuelgauge.PowerUsageDetail (from package com.android.settings) 87 | // - NOT USING main activity com.android.settings.fuelgauge.BatteryHistoryDetail (from package com.android.settings) 88 | // - NOT USING main activity com.android.deskclock.DeskClock (from package com.android.deskclock) 89 | // - NOT USING main activity com.android.browser.BrowserActivity (from package com.android.browser) 90 | // - NOT USING main activity com.android.certinstaller.CertInstaller (from package com.android.certinstaller) 91 | // - NOT USING main activity com.android.certinstaller.CertFileList (from package com.android.certinstaller) 92 | // - NOT USING main activity com.android.netspeed.NetSpeedActivity (from package com.android.netspeed) 93 | // - NOT USING main activity com.android.contacts.DialtactsActivity (from package com.android.contacts) 94 | // - NOT USING main activity com.android.contacts.DialtactsContactsEntryActivity (from package com.android.contacts) 95 | // - NOT USING main activity com.android.contacts.DialtactsFavoritesEntryActivity (from package com.android.contacts) 96 | // - NOT USING main activity com.android.camera.Camera (from package com.android.camera) 97 | // - NOT USING main activity com.android.email.activity.Welcome (from package com.android.email) 98 | // - NOT USING main activity com.android.phone.CallFeaturesSetting (from package com.android.phone) 99 | // - NOT USING main activity com.android.gesture.builder.GestureBuilderActivity (from package com.android.gesture.builder) 100 | // - NOT USING main activity com.example.android.apis.ApiDemos (from package com.example.android.apis) 101 | // Seeded: -2997299013541685614 102 | // Event percentages: 103 | // 0: 43.0% 104 | // 1: 30.0% 105 | // 2: 0.0% 106 | // 3: 0.0% 107 | // 4: 10.0% 108 | // 5: 15.0% 109 | // 6: 2.0% 110 | // 7: 0.0% 111 | // 8: 0.0% 112 | :Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10000000;component=com.example/.MainActivity;end 113 | // Allowing start of Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example/.MainActivity } in package com.example 114 | Sleeping for 300 milliseconds 115 | // CRASH: com.example (pid 945) 116 | // Short Msg: java.lang.NullPointerException 117 | // Long Msg: java.lang.NullPointerException: Unable to start activity ComponentInfo{com.example/com.example.MainActivity}: java.lang.NullPointerException 118 | // Build Label: generic/sdk/generic:2.3.3/GRI34/101070:eng/test-keys 119 | // Build Changelist: 101070 120 | // Build Time: 1296773036000 121 | // java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.MainActivity}: java.lang.NullPointerException 122 | // at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647) 123 | // at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663) 124 | // at android.app.ActivityThread.access$1500(ActivityThread.java:117) 125 | // at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931) 126 | // at android.os.Handler.dispatchMessage(Handler.java:99) 127 | // at android.os.Looper.loop(Looper.java:123) 128 | // at android.app.ActivityThread.main(ActivityThread.java:3683) 129 | // at java.lang.reflect.Method.invokeNative(Native Method) 130 | // at java.lang.reflect.Method.invoke(Method.java:507) 131 | // at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 132 | // at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 133 | // at dalvik.system.NativeStart.main(Native Method) 134 | // Caused by: java.lang.NullPointerException 135 | // at com.android.internal.os.LoggingPrintStream.println(LoggingPrintStream.java:298) 136 | // at com.example.MainActivity.onCreate(MainActivity.java:21) 137 | // at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) 138 | // at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611) 139 | // ... 11 more 140 | // 141 | ** Monkey aborted due to error. 142 | Events injected: 1 143 | :Dropped: keys=0 pointers=0 trackballs=0 flips=0 144 | ## Network stats: elapsed time=1308ms (1308ms mobile, 0ms wifi, 0ms not connected) 145 | ** System appears to have crashed at event 1 of 99999 using seed -2997299013541685614 146 | 147 | -------------------------------------------------------------------------------- /src/AndroidMonkeyResultAnalyzer/src/com/github/monkey/analyzer/report/Abnormalities2JSONReport.java: -------------------------------------------------------------------------------- 1 | package com.github.monkey.analyzer.report; 2 | 3 | import java.lang.ref.WeakReference; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | 7 | import org.json.JSONArray; 8 | import org.json.JSONException; 9 | import org.json.JSONObject; 10 | 11 | import com.github.monkey.analyzer.analyze.Constants; 12 | import com.github.monkey.analyzer.model.Abnormality; 13 | import com.github.monkey.analyzer.model.AbnormalityType; 14 | import com.github.monkey.analyzer.model.AnrOrCrashAbnormality; 15 | import com.github.monkey.analyzer.statistics.AbnormalitiesAnalyzerWrapper; 16 | import com.github.monkey.analyzer.statistics.Tuple; 17 | 18 | final public class Abnormalities2JSONReport { 19 | 20 | /** 21 | * 22 | * Convert all the abnormalities to 23 | * 24 | * TODO This kind of implement is not very good. JSON report should not 25 | * contain HTML 26 | * 27 | * @param knownAbnormalities 28 | * abnormalities which type is known 29 | * @param testingInfo 30 | * Extra testing information which store device information and 31 | * monkey executing result 32 | * @param duration 33 | * monkey test duration (hour) 34 | * @param abnormalitiesCount 35 | * all the abnormalities count 36 | * @return a JSON-format String value which represent the Monkey Report 37 | */ 38 | public static String toJSONFormatStringReport( 39 | ArrayList knownAbnormalities, 40 | ArrayList unknownAbnormalities, 41 | HashMap testingInfo, 42 | final double duration, 43 | final int abnormalitiesCount) { 44 | // 0. handle input data 45 | if ((knownAbnormalities == null || knownAbnormalities.size() == 0) 46 | && (unknownAbnormalities == null || unknownAbnormalities.size() == 0)) 47 | return "{}"; 48 | 49 | JSONReportProvider report = new JSONReportProvider(); 50 | JSONObject reportJSON = new JSONObject(); 51 | 52 | JSONArray abnormalitiesJsonArray = new JSONArray(); 53 | 54 | // 1. fill each crash abnormalities 55 | handleCrashes(knownAbnormalities, abnormalitiesJsonArray); 56 | 57 | // 2. fill ANR Info 58 | handleANRs(knownAbnormalities, abnormalitiesJsonArray); 59 | // 3. fill native crash 60 | handleNativeCrashes(knownAbnormalities, abnormalitiesJsonArray); 61 | 62 | // 4. fill summary 63 | fillSummary(knownAbnormalities, unknownAbnormalities, 64 | duration, testingInfo, reportJSON); 65 | 66 | // 5. file abnormalities to JSON report 67 | try { 68 | reportJSON.put(Constants.JSONReport.KEY_ABNORMALITIS, 69 | abnormalitiesJsonArray); 70 | } catch (JSONException e) { 71 | e.printStackTrace(); 72 | } 73 | report.setJSONObject(reportJSON); 74 | return report.toString(); 75 | } 76 | 77 | /** 78 | * 79 | * @param knownAbnormalities 80 | * abnormalities with known AbnormalityType 81 | * @param unknownAbnormalities 82 | * abnormalities with unknown AbnormalityType 83 | * @param testingInfo 84 | * @param reportJSON 85 | */ 86 | private static void fillSummary( 87 | final ArrayList knownAbnormalities, 88 | final ArrayList unknownAbnormalities, 89 | final double monkeyTestDuration, 90 | final HashMap testingInfo, 91 | final JSONObject reportJSON) { 92 | int anrCount = AbnormalitiesAnalyzerWrapper.getCount( 93 | new WeakReference>( 94 | knownAbnormalities), AbnormalityType.ANR); 95 | int crashCount = AbnormalitiesAnalyzerWrapper.getCount( 96 | new WeakReference>( 97 | knownAbnormalities), AbnormalityType.CRASH); 98 | int nativeCrashCount = AbnormalitiesAnalyzerWrapper.getCount( 99 | new WeakReference>( 100 | knownAbnormalities), AbnormalityType.NATIVE); 101 | 102 | try { 103 | reportJSON.put(Constants.JSONReport.KEY_ANR_NUMBER, anrCount); 104 | reportJSON.put(Constants.JSONReport.KEY_CRASH_NUMBER, crashCount); 105 | reportJSON.put(Constants.JSONReport.KEY_NATIVE_CRASH_NUMBER, nativeCrashCount); 106 | 107 | // TODO 108 | reportJSON.put(Constants.JSONReport.KEY_START_TIME, "-"); 109 | 110 | String duration = testingInfo 111 | .get(Constants.JSONReport.KEY_DURATION); 112 | if (duration == null || duration.isEmpty()) 113 | duration = "-"; 114 | reportJSON.put(Constants.JSONReport.KEY_DURATION, duration); 115 | 116 | 117 | String average = testingInfo 118 | .get(Constants.JSONReport.KEY_AVERAGE); 119 | if (average == null || duration.isEmpty()) 120 | average = "-"; 121 | reportJSON.put(Constants.JSONReport.KEY_AVERAGE, average + "小时"); 122 | 123 | reportJSON.put(Constants.JSONReport.KEY_END_TIME, "-"); 124 | reportJSON.put(Constants.JSONReport.KEY_EXECUTOR, "-"); 125 | reportJSON.put(Constants.JSONReport.KEY_MONKEY_LOG_URL, "-"); 126 | 127 | String fingerprint = null; 128 | if (hasMoreThanOne(knownAbnormalities)) { 129 | fingerprint = knownAbnormalities.get(0).getExtras() 130 | .get(Constants.AbnormalityProperties.KEY_PLATFORM); 131 | } else if (hasMoreThanOne(unknownAbnormalities)) { 132 | fingerprint = unknownAbnormalities.get(0).getExtras() 133 | .get(Constants.AbnormalityProperties.KEY_PLATFORM); 134 | } 135 | 136 | if (fingerprint == null) 137 | fingerprint = ""; 138 | reportJSON.put(Constants.JSONReport.KEY_MOBILE_PLATFORM, 139 | fingerprint); 140 | String pkgName = null; 141 | if (hasMoreThanOne(knownAbnormalities)) { 142 | pkgName = knownAbnormalities.get(0).getExtras() 143 | .get(Constants.AbnormalityProperties.KEY_PACKAGE_NAME); 144 | } else if (hasMoreThanOne(unknownAbnormalities)) { 145 | pkgName = unknownAbnormalities.get(0).getExtras() 146 | .get(Constants.AbnormalityProperties.KEY_PACKAGE_NAME); 147 | } 148 | if (pkgName == null) 149 | pkgName = ""; 150 | 151 | reportJSON.put(Constants.JSONReport.KEY_PRODUCT_NAME, pkgName); 152 | String pkgVersion = null; 153 | if (hasMoreThanOne(knownAbnormalities)) { 154 | pkgVersion = knownAbnormalities 155 | .get(0) 156 | .getExtras() 157 | .get(Constants.AbnormalityProperties.KEY_PACKAGE_VERSION); 158 | } else if (hasMoreThanOne(unknownAbnormalities)) { 159 | pkgVersion = unknownAbnormalities 160 | .get(0) 161 | .getExtras() 162 | .get(Constants.AbnormalityProperties.KEY_PACKAGE_VERSION); 163 | } 164 | 165 | if (pkgVersion == null) 166 | pkgVersion = ""; 167 | reportJSON.put(Constants.JSONReport.KEY_VERSION, pkgVersion); 168 | 169 | reportJSON.put(Constants.JSONReport.KEY_MOBILE_PHONE_TYPE, "-"); 170 | 171 | } catch (JSONException e2) { 172 | e2.printStackTrace(); 173 | } 174 | 175 | try { 176 | reportJSON.put(Constants.JSONReport.KEY_CRASH_NUMBER, crashCount); 177 | } catch (JSONException e1) { 178 | // TODO Auto-generated catch block 179 | e1.printStackTrace(); 180 | } 181 | } 182 | 183 | /** 184 | * Whether the ArrayList of Abnormality has more than one entry 185 | * 186 | * @param abnormalities 187 | * all the abnormalities 188 | * @return whether the ArrayList of Abnormality has more than one 189 | * entry 190 | */ 191 | private static boolean hasMoreThanOne( 192 | final ArrayList abnormalities) { 193 | return abnormalities != null && abnormalities.size() > 0; 194 | } 195 | 196 | /** 197 | * handle Native Crashes 198 | * 199 | * @param abnormalities 200 | * @param abnormalitiesJsonArray 201 | */ 202 | private static void handleNativeCrashes( 203 | ArrayList abnormalities, 204 | JSONArray abnormalitiesJsonArray) { 205 | for (Abnormality abnormality : abnormalities) { 206 | if (abnormality.getType() != AbnormalityType.NATIVE) 207 | continue; 208 | 209 | JSONObject nativeCrash = new JSONObject(); 210 | try { 211 | nativeCrash.put(Constants.JSONReport.KEY_ABNORMALITIS_TYPE, "Native Crash"); 212 | 213 | String shortMessage = null; 214 | if (shortMessage == null || shortMessage == "") 215 | shortMessage = "-"; 216 | nativeCrash.put(Constants.JSONReport.KEY_ABNORMALITIS_SHORT_MSG, 217 | shortMessage.replace("\n", "
")); 218 | 219 | String message = abnormality.getMessage(); 220 | message = trimNewLine(message); 221 | String path = abnormality.getExtras().get(AnrOrCrashAbnormality.EXTRAS_KEY_PATH); 222 | nativeCrash.put(Constants.JSONReport.KEY_ABNORMALITIS_MSG, message); 223 | final String duration = abnormality.getExtras().get( 224 | Constants.AbnormalityProperties.KEY_DURATION_STRING); 225 | nativeCrash.put(Constants.JSONReport.KEY_ABNORMALITIS_COUNT, 1); 226 | nativeCrash.put(Constants.JSONReport.KEY_ABNORMALITIS_DURATIONS, 227 | trimNewLine(duration)); 228 | nativeCrash.put(Constants.JSONReport.KEY_MONKEY_LOG_URL, "-"); 229 | nativeCrash.put(Constants.JSONReport.KEY_ABNORMALITIS_TRACE_URL, "-"); 230 | nativeCrash.put(Constants.JSONReport.KEY_ABNORMALITIS_BUGREPORT_URL, 231 | "-"); 232 | nativeCrash.put(Constants.JSONReport.KEY_ABNORMALITIS_LOGCAT_URL, "-"); 233 | path = toHyperLink(path); 234 | nativeCrash.put(Constants.JSONReport.KEY_ABNORMALITIES_ZIP_URL, path); 235 | } catch (JSONException e) { 236 | e.printStackTrace(); 237 | } 238 | abnormalitiesJsonArray.put(nativeCrash); 239 | 240 | } 241 | } 242 | 243 | /** 244 | * handle ANRs 245 | * 246 | * @param abnormalities 247 | * @param abnormalitiesJsonArray 248 | */ 249 | private static void handleANRs( 250 | ArrayList abnormalities, 251 | JSONArray abnormalitiesJsonArray) { 252 | for (Abnormality abnormality : abnormalities) { 253 | if (abnormality.getType() != AbnormalityType.ANR) 254 | continue; 255 | 256 | JSONObject anr = new JSONObject(); 257 | try { 258 | anr.put(Constants.JSONReport.KEY_ABNORMALITIS_TYPE, "ANR"); 259 | 260 | String shortMessage = abnormality.getMessage(); 261 | if (shortMessage == null || shortMessage == "") 262 | shortMessage = "-"; 263 | anr.put(Constants.JSONReport.KEY_ABNORMALITIS_SHORT_MSG, 264 | shortMessage.replace("\n", "
")); 265 | 266 | String messageAfter = abnormality.getExtras().get( 267 | AnrOrCrashAbnormality.EXTRAS_KEY_AFTER_END_PATTERN); 268 | String message = shortMessage + messageAfter; 269 | message = trimNewLine(message); 270 | String path = abnormality.getExtras().get(AnrOrCrashAbnormality.EXTRAS_KEY_PATH); 271 | anr.put(Constants.JSONReport.KEY_ABNORMALITIS_MSG, message); 272 | final String duration = abnormality.getExtras().get( 273 | Constants.AbnormalityProperties.KEY_DURATION_STRING); 274 | anr.put(Constants.JSONReport.KEY_ABNORMALITIS_COUNT, 1); 275 | anr.put(Constants.JSONReport.KEY_ABNORMALITIS_DURATIONS, 276 | trimNewLine(duration)); 277 | anr.put(Constants.JSONReport.KEY_MONKEY_LOG_URL, "-"); 278 | anr.put(Constants.JSONReport.KEY_ABNORMALITIS_TRACE_URL, "-"); 279 | anr.put(Constants.JSONReport.KEY_ABNORMALITIS_BUGREPORT_URL, 280 | "-"); 281 | anr.put(Constants.JSONReport.KEY_ABNORMALITIS_LOGCAT_URL, "-"); 282 | path = toHyperLink(path); 283 | anr.put(Constants.JSONReport.KEY_ABNORMALITIES_ZIP_URL, path); 284 | } catch (JSONException e) { 285 | e.printStackTrace(); 286 | } 287 | abnormalitiesJsonArray.put(anr); 288 | } 289 | } 290 | 291 | /** 292 | * handle CRASH 293 | * 294 | * @param abnormalities 295 | * all the abnormalities 296 | * @param abnormalitiesJsonArray 297 | */ 298 | private static void handleCrashes( 299 | ArrayList abnormalities, 300 | JSONArray abnormalitiesJsonArray) { 301 | final HashMap crashesAfterDuplicatedRemoving = AbnormalitiesAnalyzerWrapper 302 | .removeDulplicatedCrashes(abnormalities); 303 | for (String key : crashesAfterDuplicatedRemoving.keySet()) { 304 | JSONObject crash = new JSONObject(); 305 | try { 306 | crash.put(Constants.JSONReport.KEY_ABNORMALITIS_TYPE, "crash"); 307 | String shortMessage = crashesAfterDuplicatedRemoving.get(key).crashAbnormalities 308 | .getShortMessage(); 309 | crash.put(Constants.JSONReport.KEY_ABNORMALITIS_SHORT_MSG, 310 | shortMessage.replace("\n", "
")); 311 | String messageItself = crashesAfterDuplicatedRemoving.get(key).abnormalities 312 | .get(0).get().getMessage(); 313 | String message = trimNewLine(messageItself); 314 | String durations = ""; 315 | String paths = ""; 316 | for (WeakReference abnormalityRef : crashesAfterDuplicatedRemoving 317 | .get(key).abnormalities) { 318 | String duration = "-"; 319 | String path = "-"; 320 | if (abnormalityRef 321 | .get() 322 | .getExtras() 323 | .containsKey( 324 | Constants.AbnormalityProperties.KEY_DURATION_STRING)) { 325 | duration = abnormalityRef 326 | .get() 327 | .getExtras() 328 | .get(Constants.AbnormalityProperties.KEY_DURATION_STRING); 329 | } 330 | // System.out.println(duration + "##\n"); 331 | durations += duration + "\n"; 332 | 333 | if (abnormalityRef 334 | .get() 335 | .getExtras() 336 | .containsKey( 337 | Constants.AbnormalityProperties.KEY_PATHS)) { 338 | path = abnormalityRef 339 | .get() 340 | .getExtras() 341 | .get(Constants.AbnormalityProperties.KEY_PATHS); 342 | } 343 | // System.out.println(duration + "##\n"); 344 | paths += path + "\n"; 345 | } 346 | durations = trimNewLine(durations); 347 | paths = toHyperLink(paths); 348 | // durations = "共出现" + 349 | // crashesAfterDuplicatedRemoving.get(key).abnormalities.size() 350 | // + "次.

每次持续时间为:
" + durations; 351 | crash.put(Constants.JSONReport.KEY_ABNORMALITIS_COUNT, 352 | crashesAfterDuplicatedRemoving.get(key).abnormalities 353 | .size()); 354 | crash.put(Constants.JSONReport.KEY_ABNORMALITIS_DURATIONS, 355 | durations); 356 | crash.put(Constants.JSONReport.KEY_ABNORMALITIS_MSG, message 357 | + ""); 358 | crash.put(Constants.JSONReport.KEY_MONKEY_LOG_URL, "-"); 359 | crash.put(Constants.JSONReport.KEY_ABNORMALITIS_TRACE_URL, "-"); 360 | crash.put(Constants.JSONReport.KEY_ABNORMALITIS_BUGREPORT_URL, 361 | "-"); 362 | crash.put(Constants.JSONReport.KEY_ABNORMALITIS_LOGCAT_URL, "-"); 363 | 364 | crash.put(Constants.JSONReport.KEY_ABNORMALITIES_ZIP_URL, paths); 365 | System.out.println(paths); 366 | } catch (JSONException e) { 367 | e.printStackTrace(); 368 | } 369 | abnormalitiesJsonArray.put(crash); 370 | } 371 | } 372 | 373 | /** 374 | * trim the source String to remove all {@code \n} 375 | * 376 | * @param source 377 | * a String to be handled 378 | * @return a String after handling 379 | */ 380 | private static String trimNewLine(final String source) { 381 | if (source == null || source == "") 382 | return "-"; 383 | String message = source.replace("\n", "
"); 384 | String[] messages = message.split("
"); 385 | if (messages == null || messages.length == 0) { 386 | message = "-"; 387 | } 388 | return message; 389 | } 390 | 391 | private static String toHyperLink(final String source) { 392 | if (source == null || source == "") 393 | return "-"; 394 | String[] messages = source.split("\\n"); 395 | if (messages == null || messages.length == 0) { 396 | return "-"; 397 | } 398 | StringBuilder sb = new StringBuilder(); 399 | for (String message : messages) { 400 | sb.append("下载
"); 401 | } 402 | return sb.toString(); 403 | } 404 | 405 | } --------------------------------------------------------------------------------