MINItest软件架构总结

————helloWen


1. Problem Description

  • Precondition:

    手机在试产过程中需要确认生产的机器硬件是否正常,而MINI软件就是这样一个测试手机硬件基本功能是否正常的精简系统(实际上就是一个内部测试软件)。MINI软件是提供给工厂用来测试硬件是否正常工作(如摄像头是否正常拍照,触摸屏是否灵敏等)的测试软件,它是包含MINI APK(也可叫做MMI)的最精简系统版本软件,贯穿手机整个生产流程,对生产非常重要。

    MINI测试软件的主要是包含“AUTO”测试和“MANU”测试,这些测试的规范都由NPI部门主导完成,MINI测试设计要严格按NPI的规范进行设计,这2个测试模式要求如下:

    • 自动测试模式,按照一定的顺序依次执行各个测试项,一旦有一个测试项FAIL,则中断测试,适用于工厂自动化对所有测试项进行测试。
    • 手动测试模式,所有测试项都显示在手动测试列表中,可任意选择测试项进行测试,适用于对单个测试项进行测试。

    不同版本对应的mini软件状态要求如下:

    • 在MINI软件的状态下,手机开机完成就会自动进入到MMI软件测试的界面。
    • 在客户化软件的情况下,我们通过在拨号界面输入暗码可以进入到MMI软件测试的界面。
  • REPRODUCING PROCEDURES:

    目前这个apk,整体架构比较简单,而且耦合性太高,代码有很多冗余, 格式不规范。其整体架构如下图所示:



    在整个架构中多用的继承,使得各个case之间其实耦合性很大,而且扩展性不强。因此期望重构过程中多用组合,少用继承,使系统更具弹性。

  • EXPECTED

    代码精简&&语法规范&&架构重构

2. Analysis

框架部分主要有如下几个Activity:

  • MiniActivity,主界面 activity,可选择2中测试方式,其一,是自动测试(即按着配置文件顺序测试完一项后紧接下一项继续测试);其二,手动测试(用户根据自己选择测试某个单独项)。
  • FailedActivity,自动测试失败后,可以选择退出自动测试、忽略错误继续下一项测、重新测试当前项等3个选项。
  • ManuListActivity ,当点击手动测试,显示试列表界面。
  • ResultActivity ,自动测试完毕后,显示测试结果。
  • ExecuteTestActivity ,执行测试项界面,所有测试项执行时均在此activity执行。
  • InterruptActivity ,中断activity,当在测试界面时,可以通过长按Back键进入该界面。

整个软件的入口Activity是MiNiActivity,业务流程如下图所示:



通过MiNiActivity来判断用户使用哪种测试方式执行测试,而实际的测试项是在ExecuteTestActivity中完成的。

根据自动测试模式和手动测试模式的不同情况,通过使用观察者模式的思想来设计自动测试模式,使用策略模式的思想来设计手动测试模式。

  • 自动测试的设计流程如下所示

  • 手动测试的设计流程如下所示

  • 在这个测试软件重构过程中,期望把测试的框架和测试的具体case分离开来,这样在以后的维护中可以分开维护,即如果需要增加、修改、删除某些测试case时,其他的case完全不受到影响,而且整个软件框架不需要去改动。对应的架构如下图所示:

  • 测试实际的Activity(在项目中是ExecuteTestActivity.java)并不需要改变,只需要替换不同的测试内容(在项目中表现为继承了Test.java的子类),就可以达到显示不同的测试case的目的。对于很多case中界面部分逻辑,将它分离出来作为一个单独的工具类(还有一些其他的工具类,如控制计时器的类、部分控件监听器等),当有需要用到它时,它将作为具体测试内容的一个属性被使用。
  • 本测试软件的设计是希望能够适用于不同项目的设备,而不同设备所具备的测试项是具有一定的差别的,所以mini软件中通过读取设备自身系统值来区分需要包含的case。

3. Solution

3.1. 通过读取设备信息来确定测试项信息

  1. 首先,将所有的case信息写入到./assets/Base.xml文件中,而Base.xml的结构如下所示:

    1. <root>
    2. <macros>
    3. <macro>.....</macro>
    4. </macros>
    5. <deviceNodes>
    6. <deviceNode>.....</deviceNode>
    7. </deviceNodes>
    8. </root>

    子元素macro存储各个case的详细信息,主要包括case的全限定名、是否加入auto测试、是否在eng中显示、显示名、是否在mini版中显示等信息,其结构如下:

    <macro name="com.jrdcom.mmitest.test.TraceabilityTest" auto="true" eng="true" listName="TRACEABILITY" mini="true" />

    • name: 表示该测试项的全限定名。
    • listName: 表示该测试项在MANU的list列表中显示的名字。
    • eng:true表示在eng版本出现该测试项,false表示不出现。
    • mini:true表示在mini版本中出现该测试项,false表示不出现。
    • auto:true表示在AUTO模式出现该测试项,false表示不出现。

    子元素deviceNode存储设备中各个驱动节点的信息,主要包括节点绝对路径和对应的变量名,其结构如下:

    <deviceNode name="FrontCameraNode" path="/sys/class/deviceinfo/device_info/CamNameF" />

    • name: 表示该驱动节点变量名。
    • path: 表示该驱动节点绝对路径。

    因为各个项目设备的驱动节点路径可能不同,将它从java代码中分离出来方便维护,在导入mini软件时,根据设备实际情况配置好各个驱动节点的路径即可。

  2. 配置项目自己的xml文件

    ./assets/Base.xml文件存储了所有可能用到的case信息,但是针对具体项目,可能只是需要部分的测试case,所以通过项目自己的xml文件来进行调整。

    ./assets/Base.xml中记录了如下case:

    1. <macros>
    2. <macro
    3. name="com.jrdcom.mmitest.test.A"
    4. auto="true"
    5. eng="true"
    6. listName="A"
    7. mini="true" />
    8. <macro
    9. name="com.jrdcom.mmitest.test.B"
    10. auto="true"
    11. eng="false"
    12. listName="B"
    13. mini="true" />
    14. <macro
    15. name="com.jrdcom.mmitest.test.C"
    16. auto="true"
    17. eng="true"
    18. listName="C"
    19. mini="true" />
    20. <macro
    21. name="com.jrdcom.mmitest.test.D"
    22. auto="true"
    23. eng="true"
    24. listName="D"
    25. mini="true" />
    26. <macro
    27. name="com.jrdcom.mmitest.test.E"
    28. auto="true"
    29. eng="true"
    30. listName="E"
    31. mini="true" />
    32. </macros>

    现有project_Aproject_B2个项目,然后project_A需要测试项A、B、D(只在自动测试模式出现)、E4个case,而project_B需要测试项A(在mini测试模式不出现)、B、C(在自动测试模式不出现)、D、E5个case,则需要在project_A项目设备上创建 ./assets/[ro.product.device].xml文件,其文件内容如下:

    1. <macros>
    2. <macro
    3. name="com.jrdcom.mmitest.test.C"
    4. auto="false"
    5. eng="false"
    6. listName="C"
    7. mini="false" />
    8. <macro
    9. name="com.jrdcom.mmitest.test.D"
    10. auto="true"
    11. eng="false"
    12. listName="D"
    13. mini="false" />
    14. </macros>

    project_B项目设备上创建 ./assets/[ro.product.device].xml文件,其文件内容如下:

    1. <macros>
    2. <macro
    3. name="com.jrdcom.mmitest.test.A"
    4. auto="true"
    5. eng="true"
    6. listName="A"
    7. mini="false" />
    8. <macro
    9. name="com.jrdcom.mmitest.test.C"
    10. auto="false"
    11. eng="true"
    12. listName="C"
    13. mini="true" />
    14. </macros>

    即项目自己的xml文件优先级高于./assets/Base.xml优先级,对于相同的macro节点,项目的macro会覆盖./assets/Base.xml中的macro,从而实现对各个case的调整。

  3. 将最终的测试项信息存储在ArrayList中

    • 首先,分别读取./assets/Base.xml中macro节点信息和./assets/[ro.product.device].xml文件中macro节点信息,并将macro节点信息存储在PullXmlParser对象的List<DeviceNode> mDeviceNodeList属性中

      1. private PullXmlParser parserXml() {
      2. PullXmlParser pxp = new PullXmlParser(this);
      3. InputStream baseIs = null;
      4. try {
      5. baseIs = getAssets().open(XML_FILE_PATH);
      6. } catch (IOException e) {
      7. Log.i(TAG, "Base.xml is not exist ");
      8. alertWarnDialog("Base.xml");
      9. return pxp;
      10. }
      11. pxp.parserBaseXml(baseIs);
      12. File sdcard = Environment.getExternalStorageDirectory();
      13. File testXml = new File(sdcard,"JrdMMITest.xml");
      14. if(testXml.exists()){
      15. try {
      16. InputStream testIs = new FileInputStream(testXml);
      17. pxp.parserModelXml(testIs);
      18. } catch (FileNotFoundException e) {
      19. Log.i(TAG, "JrdMMITest.xml is not exist ");
      20. Log.i(TAG, e.toString());
      21. }
      22. }else {
      23. String modelXmlPath = getModelXmlPath();
      24. if (!(TextUtils.isEmpty(modelXmlPath))) {
      25. InputStream modelIs = null;
      26. try {
      27. modelIs = getAssets().open(modelXmlPath);
      28. } catch (IOException e) {
      29. Log.i(TAG, modelXmlPath + ".xml is not exist ");
      30. alertWarnDialog(modelXmlPath);
      31. }
      32. pxp.parserModelXml(modelIs);
      33. }
      34. }
    • 然后,合并./assets/Base.xml中macro节点信息和./assets/[ro.product.device].xml文件中macro节点信息。

      1. public void parserModelXml(InputStream is) {
      2. // InputStream is = mContext.getClassLoader().getResourceAsStream(
      3. // xmlFilePath);
      4. if (is == null) {
      5. Log.i(TAG, "Couldn't find model xml !");
      6. return;
      7. }
      8. XmlPullParser xp = Xml.newPullParser();
      9. try {
      10. xp.setInput(is, "utf-8");
      11. int type = xp.getEventType();
      12. Macro macro = null;
      13. DeviceNode deviceNode = null;
      14. while (type != XmlPullParser.END_DOCUMENT) {
      15. switch (type) {
      16. case XmlPullParser.START_TAG:
      17. if (XML_MACRO.equals(xp.getName())) {
      18. String name = xp.getAttributeValue(null, XML_KEY_NAME);
      19. String listName = xp.getAttributeValue(null,
      20. XML_KEY_LIST_NAME);
      21. Boolean eng = Boolean.parseBoolean(xp
      22. .getAttributeValue(null, XML_KEY_ENG));
      23. Boolean mini = Boolean.parseBoolean(xp
      24. .getAttributeValue(null, XML_KEY_MINI));
      25. Boolean auto = Boolean.parseBoolean(xp
      26. .getAttributeValue(null, XML_KEY_AUTO));
      27. Log.i(TAG, "name=" + name + ";eng=" + eng + ";mini="
      28. + mini + ";auto=" + auto);
      29. macro = new Macro(name, listName, eng, mini, auto);
      30. mergeMacroToBase(macro);
      31. } else if (XML_DEVICE_NODE.equals(xp.getName())) {
      32. String name = xp.getAttributeValue(null, XML_KEY_NAME);
      33. String path = xp.getAttributeValue(null, XML_KEY_PATH);
      34. Log.i(TAG, "name=" + name + ";path=" + path);
      35. deviceNode = new DeviceNode(name, path);
      36. mergeDeviceNodeToBase(deviceNode);
      37. }
      38. break;
      39. }
      40. try {
      41. type = xp.next();
      42. } catch (IOException e) {
      43. e.printStackTrace();
      44. }
      45. }
      46. } catch (XmlPullParserException e) {
      47. e.printStackTrace();
      48. } finally {
      49. try {
      50. is.close();
      51. } catch (IOException e) {
      52. e.printStackTrace();
      53. }
      54. }
      55. }
      56. private void mergeMacroToBase(Macro macro) {
      57. for (Macro m : mMacroList) {
      58. if (m.getName().equals(macro.getName())) {
      59. int index = mMacroList.indexOf(m);
      60. m.setObject(macro);
      61. mMacroList.set(index, m);
      62. }
      63. }
      64. for (Macro m : mMacroList) {
      65. Log.i(TAG, "mergeMacroToBase: " + m.toString());
      66. }
      67. }
    • 最后,将最终的测试项信息存储在DeviceNodeList类的静态变量deviceNodeList中。

      1. public class DeviceNodeList {
      2. public static final String TAG = "com.jrdcom.mmitest.utils.DeviceNodeList";
      3. private List<DeviceNode> deviceNodeList;
      4. private static Map<String, String> deviceNodeMap;
      5. public DeviceNodeList(List<DeviceNode> deviceNodeList) {
      6. this.deviceNodeList = deviceNodeList;
      7. }
      8. public void init() {
      9. deviceNodeMap = new HashMap<String, String>();
      10. if (!deviceNodeList.isEmpty()) {
      11. for (DeviceNode deviceNode : deviceNodeList) {
      12. Log.i(TAG, deviceNode.toString());
      13. deviceNodeMap.put(deviceNode.getName(), deviceNode.getPath());
      14. }
      15. }
      16. }
      17. public static String getDeviceNodePath(String deviceNodeName) {
      18. return deviceNodeMap.get(deviceNodeName);
      19. }

3.2 将测试项通过Activity加载并显示出来

因为所有的测试项都是继承共同的基类Test.java,所以各个测试项将作为执行Activity(ExecuteTestActivity)的属性,通过执行Activity的生命周期创建出来,其效果如下图所示。



Test.java具体代码如下所示:

  1. public abstract class Test {
  2. private String mName;
  3. private Activity mCurrentActivity;
  4. private volatile boolean preventDoublePass;
  5. private static final String TAG = "com.jrdcom.mmitest.test.Test";
  6. public enum RESULT {
  7. PASS, FAIL, RETEST, NO, NEXT, YES, INTERRUPT
  8. }
  9. public static int MODE = 0;
  10. public static final int AUTO = 1;
  11. public static final int MANU = 2;
  12. public static final int TEST_RETURN = 3;
  13. public static final int FAIL_RETURN = 4;
  14. public static final int RESULT_RETURN = 5;
  15. public static final int INTERRUPT_RETURN = 6;
  16. public Test(String mName) {
  17. this.mName = mName;
  18. preventDoublePass=true;
  19. }
  20. public String getName() {
  21. return mName;
  22. }
  23. public void setName(String mName) {
  24. this.mName = mName;
  25. }
  26. public Activity getContext() {
  27. return mCurrentActivity;
  28. }
  29. public void setContext(Activity mCurrentActivity) {
  30. this.mCurrentActivity = mCurrentActivity;
  31. }
  32. @Override
  33. public String toString() {
  34. return mName;
  35. }
  36. public abstract void onCreate(Bundle savedInstanceState);
  37. public void onStart() {
  38. }
  39. public void onResume() {
  40. }
  41. public void onPause() {
  42. }
  43. public void onStop() {
  44. }
  45. public void onDestroy() {
  46. }
  47. public void onRestart() {
  48. }
  49. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  50. }
  51. public void onNewIntent(Intent intent) {
  52. }
  53. public boolean onKeyDown(int keyCode, KeyEvent event) {
  54. return true;
  55. }
  56. public boolean onKeyUp(int keyCode, KeyEvent event) {
  57. return true;
  58. }
  59. public boolean isNeedInterrupt(){
  60. return true;
  61. }
  62. public boolean isNeedPreventKeyInManu(){
  63. return false;
  64. }
  65. public void pass() {
  66. synchronized (Test.class)
  67. {
  68. // TODO Auto-generated method stub
  69. if (preventDoublePass)
  70. {
  71. preventDoublePass = false;
  72. Log.v(TAG, "Test'pass is involved,it will fininshed current activity! ");
  73. getContext().setResult(Test.RESULT.PASS.ordinal());
  74. getContext().finish();
  75. }
  76. }
  77. }
  78. public void fail() {
  79. synchronized (Test.class)
  80. {
  81. // TODO Auto-generated method stub
  82. if (preventDoublePass)
  83. {
  84. preventDoublePass = false;
  85. Log.v(TAG, "Test'fail is involved,it will fininshed current activity! ");
  86. Intent intent = new Intent();
  87. intent.putExtra("name", getName());
  88. getContext().setResult(Test.RESULT.FAIL.ordinal(), intent);
  89. getContext().finish();
  90. }
  91. }
  92. }
  93. }

其实Test.java就是根据Activity组件,创建了相对于它生命周期的方法,但是大部分方法都是空实现,而具体逻辑则是由继承Test.java的子类来实现。

ExecuteTestActivity.java的实现如下所示:

  1. public class ExecuteTestActivity extends Activity {
  2. private static final String TAG = "com.jrdcom.mmitest.activity.ExecuteTestActivity";
  3. private Test currentTest;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. int mask = WindowManager.LayoutParams.FLAG_FULLSCREEN;
  8. mask |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
  9. getWindow().setFlags(mask, mask);
  10. currentTest = getCurrentTest();
  11. currentTest.setContext(this);
  12. currentTest.onCreate(savedInstanceState);
  13. }
  14. @Override
  15. protected void onStart() {
  16. super.onStart();
  17. currentTest.onStart();
  18. }
  19. @Override
  20. protected void onResume() {
  21. super.onResume();
  22. currentTest.onResume();
  23. }
  24. @Override
  25. protected void onPause() {
  26. super.onPause();
  27. currentTest.onPause();
  28. }
  29. @Override
  30. protected void onStop() {
  31. super.onStop();
  32. currentTest.onStop();
  33. }
  34. @Override
  35. protected void onDestroy() {
  36. super.onDestroy();
  37. currentTest.onDestroy();
  38. }
  39. @Override
  40. protected void onRestart() {
  41. super.onRestart();
  42. currentTest.onRestart();
  43. }
  44. @Override
  45. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  46. super.onActivityResult(requestCode, resultCode, data);
  47. currentTest.onActivityResult(requestCode, resultCode, data);
  48. }
  49. @Override
  50. protected void onNewIntent(Intent intent) {
  51. super.onNewIntent(intent);
  52. currentTest.onNewIntent(intent);
  53. }
  54. @Override
  55. public boolean onKeyDown(int keyCode, KeyEvent event) {
  56. if ((getCurrentMode() == Test.AUTO) || isNeedPreventKeyInManu()) {
  57. return currentTest.onKeyDown(keyCode, event);
  58. }
  59. return super.onKeyDown(keyCode, event);
  60. }
  61. @Override
  62. public boolean onKeyUp(int keyCode, KeyEvent event) {
  63. if (getCurrentMode() == Test.AUTO) {
  64. if ((event.getEventTime() - event.getDownTime() > 500)
  65. && isNeedInterrupt()) {
  66. Intent intent = new Intent();
  67. intent.putExtra("name", currentTest.getName());
  68. setResult(Test.RESULT.INTERRUPT.ordinal(), intent);
  69. Log.i(TAG, "AUTO: KEYCODE_BACK to interrupt activity");
  70. finish();
  71. }
  72. return currentTest.onKeyUp(keyCode, event);
  73. }
  74. return super.onKeyUp(keyCode, event);
  75. }
  76. private boolean isNeedInterrupt() {
  77. return currentTest.isNeedInterrupt();
  78. }
  79. private boolean isNeedPreventKeyInManu(){
  80. return currentTest.isNeedPreventKeyInManu();
  81. }
  82. private Test getCurrentTest() {
  83. Intent intent = getIntent();
  84. int position = intent.getIntExtra("position", 0);
  85. Map<String, String> testMap = TestList.getTestList().get(position);
  86. return Utils.getClassFromName(testMap.get("name"), testMap.get("listName"));
  87. }
  88. private int getCurrentMode() {
  89. return getIntent().getIntExtra("mode", Test.MANU);
  90. }
  91. }

在ExecuteTestActivity中,通过getCurrentTest方法获取测试case对应的Test类对象(如前面描述的A.java、B.java对象),然后通过适配器模式,将具体case的逻辑通过ExecuteTestActivity生命周期展示出来。

3.3 控制各个Activity之间跳转

在整个mini软件中,MiniActivity作为核心的Activity在软件的运行过程中一直存在,由它控制各个Activity之间的跳转,具体情况如下:

  • 步骤一: 在进入mini软件后,会显示主界面MiniActivity,在该界面用户可以选择Auto测试和Manu测试,如果点击Auto测试,则进入步骤四;如果点击Manu测试,则进入步骤二。
  • 步骤二: 进入ManuListActivity界面,并显示所有eng=ture的macro节点的case,系统可根据用户点击的case项进入步骤三。
  • 步骤三: 开启ExecuteTestActivity,并且根据用户点击的case项,实例化对应的Test对象,将该对象作为参数传入ExecuteTestActivity中运行。
  • 步骤四: 判断ArrayList类型属性值mLastTestResult是否为空(每次Auto测试结束都会将测试失败结果保存到mLastTestResult中,默认第一次开机情况和上次测试全部通过情况mLastTestResult值为空),如果为空,进入步骤五,否则进入步骤
  • 步骤五: 跳转到ExecuteTestActivity中,并设置requestCode=Test.TEST_RETURN,将测试项的index作为参数传入ExecuteTestActivity中(第一次进入,默认测试项的index值为0),然后进入步骤六
  • 步骤六: 首先,启动ExecuteTestActivity,并且将传入的参数通过getCurrentTest方法构造出将要测试case的Test对象,然后通过ExecuteTestActivity将构造出的Test对象展示出来,最后根据用户的点击事件进行返回:

    如果用户点击pass按钮,则finish掉ExecuteTestActivity,并设置resultCode=Test.RESULT.PASS然后进入步骤七;

    如果用户点击fail按钮,则则finish掉ExecuteTestActivity,并设置resultCode=Test.RESULT.FAIL然后进入步骤七。
  • 步骤七: 回到MiniActivity中,根据resultCode和requestCode值进行跳转:

    如果requestCode=Test.TEST_RETURNresultCode=Test.RESULT.PASS,进入步骤六,测试项的index++,并将它作为参数传入目标Activity。

    如果requestCode=Test.TEST_RETURNresultCode=Test.RESULT.FAIL,进入步骤九。

    如果requestCode=Test.TEST_RETURNresultCode=Test.RESULT.INTERRUPT,进入步骤八。

    如果requestCode=Test.FAIL_RETURNresultCode=Test.RESULT.RETEST,进入进入步骤六,测试项的index依旧保持原来的值,然后将它作为参数传入目标Activity。

    如果requestCode=Test.FAIL_RETURNresultCode=Test.RESULT.NO,将测试失败的结果保存到mLastTestResult中。

    如果requestCode=Test.FAIL_RETURNresultCode=Test.Test.RESULT.NEXT,进入步骤六,测试项的index++,并将它作为参数传入目标Activity。

    如果requestCode=Test.RESULT_RETURNresultCode=Test.RESULT.YES,进入步骤五。

    如果requestCode=Test.INTERRUPT_RETURN且resultCode=Test.RESULT.RETEST,进入进入步骤六,测试项的index依旧保持原来的值,然后将它作为参数传入目标Activity。

    如果requestCode=Test.INTERRUPT_RETURNresultCode=Test.RESULT.FAIL,进入步骤九。
  • 步骤八: 设置requestCode=Test.INTERRUPT_RETURN,并启动InterruptActivity,然后根据用户点击事件进行跳转:

    如果点击RETEST按钮,则finish掉InterruptActivity,并设置resultCode=Test.RESULT.RETEST然后进入步骤七。

    如果点击FAIL按钮,则finish掉InterruptActivity,并设置resultCode=Test.RESULT.FAIL然后进入步骤七。
  • 步骤九: 设置requestCode=Test.FAIL_RETURN,并启动 FailedActivity,然后根据用户点击事件进行跳转:

    如果点击RETEST按钮,则finish掉FailedActivity,并设置resultCode=Test.RESULT.RETEST然后进入步骤七。

    如果点击NO按钮,则finish掉FailedActivity,并设置resultCode=Test.RESULT.NO然后进入步骤七。

    如果点击NEXT按钮,则finish掉FailedActivity,并设置resultCode=Test.RESULT.NEXT然后进入步骤七。

MiniActivity中Activity跳转设计如下所示:

  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  3. switch (requestCode) {
  4. case Test.TEST_RETURN:
  5. if (resultCode == Test.RESULT.PASS.ordinal()) {
  6. if (mTestIndex == (TestList.getTestListSize() - 1)) {
  7. if (!mCurrentTestResult.isEmpty()) {
  8. mLastTestResult = mCurrentTestResult;
  9. Log.i(TAG, "pass:mLastTestResult:" + mLastTestResult);
  10. Log.i(TAG, "pass: show result activity");
  11. startResultActivity(mLastTestResult);
  12. } else {
  13. startResultActivity(null);
  14. }
  15. break;
  16. }
  17. runTestAt(++mTestIndex);
  18. } else if (resultCode == Test.RESULT.FAIL.ordinal()) {
  19. String failedTestName = data.getStringExtra("name");
  20. Log.i(TAG, "test is fail, save fail case to latest result, "
  21. + failedTestName + " is failed !");
  22. saveFailedTestResult(failedTestName);
  23. Log.i(TAG, "test is fail, show fail activity");
  24. startFailedActivity(failedTestName);
  25. } else if (resultCode == Test.RESULT.INTERRUPT.ordinal()) {
  26. String interruptTestName = data.getStringExtra("name");
  27. Log.i(TAG, "start interrupt activity");
  28. startInterruptActivity(interruptTestName);
  29. break;
  30. }
  31. break;
  32. case Test.FAIL_RETURN:
  33. if (resultCode == Test.RESULT.RETEST.ordinal()) {
  34. String failedTestName = data.getStringExtra("name");
  35. Log.i(TAG, "retest, , " + failedTestName
  36. + " test result should be delete !");
  37. deleteFailedTestResult(failedTestName);
  38. runTestAt(mTestIndex);
  39. } else if (resultCode == Test.RESULT.NO.ordinal()) {
  40. if (!mCurrentTestResult.isEmpty()) {
  41. mLastTestResult = mCurrentTestResult;
  42. if (mTestIndex != (TestList.getTestListSize() - 1)) {
  43. String interruptResult = getInterruptTestResult();
  44. mLastTestResult.add(interruptResult);
  45. isNeedSaveToSP = true;
  46. }
  47. Log.i(TAG, "no:mLastTestResult:" + mLastTestResult);
  48. }
  49. } else if (resultCode == Test.RESULT.NEXT.ordinal()) {
  50. if (mTestIndex == (TestList.getTestListSize() - 1)) {
  51. if (!mCurrentTestResult.isEmpty()) {
  52. mLastTestResult = mCurrentTestResult;
  53. Log.i(TAG, "next:mLastTestResult:" + mLastTestResult);
  54. Log.i(TAG, "next: show result activity");
  55. startResultActivity(mLastTestResult);
  56. }
  57. break;
  58. }
  59. runTestAt(++mTestIndex);
  60. }
  61. break;
  62. case Test.RESULT_RETURN:
  63. if (resultCode == Test.RESULT.YES.ordinal()) {
  64. mLastTestResult = null;
  65. startAutoTest();
  66. }
  67. break;
  68. case Test.INTERRUPT_RETURN:
  69. if (resultCode == Test.RESULT.RETEST.ordinal()) {
  70. runTestAt(mTestIndex);
  71. } else if (resultCode == Test.RESULT.FAIL.ordinal()) {
  72. String failedTestName = data.getStringExtra("name");
  73. Log.i(TAG,
  74. "interrupt: test is fail, save fail case to latest result, "
  75. + failedTestName + " is failed !");
  76. saveFailedTestResult(failedTestName);
  77. Log.i(TAG, "test is fail, show fail activity");
  78. startFailedActivity(failedTestName);
  79. }
  80. break;
  81. }
  82. }

4. Summary

本次主要是对重构后mini软件进行总结,相对与前面一个本版,主要是对框架和具体测试case进行了分离,这样软件框架和具体测试case可以分开维护,即维护框架的人员只需要关注框架本身而不用了解具体测试case的内部逻辑,维护具体测试case人员只要知道框架提供的接口,就可以开发出和框架兼容的测试case。并在新的框架中使用了多种设计模式,优化了代码结构,类继承最多只到3层,更多的使用组合模式来连接不同的逻辑模块,相较前面的版本,可扩展性也有了较大的提高。但是发现重构后mini软件仍然有很多不足的地方,需要后面继续对其进行优化。

MINItest软件架构总结的更多相关文章

  1. Atitit 软件架构方法的进化与演进cs bs soa roa  msa  attilax总结

    Atitit 软件架构方法的进化与演进cs bs soa roa  msa  attilax总结 1.1. 软件体系架构是沿着单机到 CS 架构,再到 BS 的三层架构甚至多层架构逐步发展过来的,关于 ...

  2. Atitit.软件架构高扩展性and兼容性原理与概论实践attilax总结

    Atitit.软件架构高扩展性and兼容性原理与概论实践attilax总结 1. 什么是可扩展的应用程序?1 2. 松耦合(ioc)2 3. 接口的思考 2 4. 单一用途&模块化,小粒度化2 ...

  3. TID大会学习心得之敏捷软件架构-微服务

    敏捷微服务构建 王威: TW咨询师.架构转型教练.敏捷技术教练 敏捷的目标 敏捷的目标是提升效率?降低成本?减员增效? 敏捷:关注价值.快速反馈.快速响应.其的目标是提升响应力,响应力的提升不一定会提 ...

  4. [转]架构蓝图--软件架构 "4+1" 视图模型

    架构蓝图--软件架构 "4+1" 视图模型 本文基于多个并发视图的使用情况来说明描述软件密集型系统架构的模型.使用多重视图允许独立地处理各"风险承担人":最终用 ...

  5. 向架构师进军--->如何编写软件架构文档

    如果你对项目管理.系统架构有兴趣,请加微信订阅号"softjg",加入这个PM.架构师的大家庭 问:为什么要编写软件架构文档,它的好处是什么? 答: 有文档的架构有助于不同利益相关 ...

  6. wpa_supplicant软件架构分析

    wpa_supplicant软件架构分析 1. 启动命令 wpa supplicant 在启动时,启动命令可以带有很多参数,目前我们的启动命令如下: wpa_supplicant /system/bi ...

  7. YOUYOU深入学习Ganglia之三(gmetad的软件架构)

    Ganglia这个东西,目前的情况是测试的多,真正在数据中心部署过的人少:使用的多,真正能了解其代码架构的人少.这里根据我的经验,分解一下ganglia的gmetad的软件架构,欢迎大家交流. 上面的 ...

  8. 向架构师进军---&gt;怎样编写软件架构文档

    假设你对项目管理.系统架构有兴趣,请加微信订阅号"softjg",增加这个PM.架构师的大家庭 问:为什么要编写软件架构文档,它的优点是什么? 答: 有文档的架构有助于不同利益相关 ...

  9. Z-Stack 软件架构分析

    转自Z-Stack 软件架构分析 Z-Stack的main函数在Zmain.c中,总体上来说,它一共做了两件工作,一个是系统初始化,即有启动代码来初始化硬件系统和软件架构需要的各个模块,另一个作用就是 ...

随机推荐

  1. 当动态桌面遇上 HTML5

    DreamScene2 + HTML5 = 无限可能.时隔一周,DreamScene2 动态桌面经过几个小版本的迭代,修复了一些问题并且功能也得到了增强.欢迎 Star 和 Fork https:// ...

  2. android jni-dlerror报undefined symbol: JNI_OnLoad

    以下是很简单的一个官方的jni方法,在MainActivity的onCreate中调用 extern "C" JNIEXPORT jstring JNICALL Java_com_ ...

  3. Spring5 AOP编程:关于org.springframework.beans.factory.BeanNotOfRequiredTypeException报错

    Spring5 AOP编程:关于org.springframework.beans.factory.BeanNotOfRequiredTypeException报错 先上错误详细信息: org.spr ...

  4. xml数据结构处理

    <data> <country name="Liechtenstein"> <rank updated="yes">2< ...

  5. 分享自己做的一个指定进程以及线程长时间cpu监控的工具

    前言: 前面给大家分享过一个工作中用到的编译拷贝脚本,其实工作中还有一些其他工具的使用,今天再来分享一个自己纯手工的CPU监控的脚本.大家可以结合上篇文章与本篇文章一起学习shell. 主要实现功能: ...

  6. 【LeetCode】485. Max Consecutive Ones 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Java解法 Python解法 日期 [LeetCo ...

  7. C++输出控制小数点后位数的方法

    以C++输出小数点两位数为例 setprecision(n)使用方法总结 首先要记住写头文件 #include <iomanip> //不要忘了头文件 以下是控制小数位数的三种写法 //t ...

  8. <数据结构>XDOJ327.最短路径

    问题与解答 问题描述 求图中任意两个顶点之间的最短路径. 输入格式 输入数据第一行是一个正整数,表示图中的顶点个数n(顶点将分别按0,1,-,n-1进行编号).之后的n行每行都包含n个整数,第i行第j ...

  9. Java初学者作业——添加程序断点,以Debug模式运行程序,观察变量的交换

    返回本章节 返回作业目录 需求说明: 定义两个变量,分别用于存储学生的语文成绩和数学成绩,通过程序将这两个变量的值进行交换,输出变量之前的值和交换后变量中的值. 添加断点,观察变量值的交换过程. 实现 ...

  10. Android开发 ListView(垂直滚动列表项视图)的简单使用

    效果图: 使用方法: 1.在布局文件中加入ListView控件: <?xml version="1.0" encoding="utf-8"?> &l ...