3

我会提前为发布大量代码道歉,这个问题真的让我很着迷!

我有两个导致我出现问题的 Android JUnit 测试。单独运行每个并且它们工作正常,但是当一次运行时(PasswordEntryActivityTests 然后 CryptoKeystoreTests) CryptoKeystoreTests 无限期挂起。

我知道这不仅仅是模拟器速度慢,因为每个模拟器都在不到一秒的时间内完成,而且它可以挂起超过 20 分钟。我还在真实设备(Droid Razr)上对其进行了测试,它也做了同样的事情。

有问题的代码是PasswordEntryActivity.launchNewPasswordActivity(). 删除该功能会使一切正常。

在挂起时暂停调试器中的函数表示它在:

MessageQueue.nativePollOnce(int, int) line: not available [native method]

这是怎么回事?

我在下面复制了:

  • 密码输入活动
  • PasswordEntryActivity测试
  • CryptoManagerKeystoreTests

请让我知道发布您想查看的任何其他代码。谢谢!

    public class PasswordEntryActivity extends Activity 
    {
...
        private void launchNewPasswordActivity()
        {
            Intent launchNewPasswordIntent = new Intent(this, NewPasswordActivity.class);
            startActivity(launchNewPasswordIntent);
        }

        @Override
        protected void onCreate(Bundle savedInstanceState) 
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.password_entry_layout);
...
            //this code should be LAST in onCreate because it exits the Activity
            //CryptoManager.passwordIsRight returns 0 if no password has been set
            passwordExists = CryptoManager.passwordIsRight("x", this) != 0;
            if(!passwordExists)
                launchNewPasswordActivity();
        }
    }

该活动的测试:

    //supposed to make sure the application responds correctly when no password is set
    public class PasswordEntryActivityTests extends android.test.ActivityInstrumentationTestCase2<                          crypnote.controller.main.PasswordEntryActivity>{
    protected void setUp() throws Exception 
    {
        passwordEntryActivity = getActivity();

        //delete the database if it exists
        File file = passwordEntryActivity.getFileStreamPath(DBInterface.Constants.DatabaseName);
        if(file.exists())
            assertTrue(file.delete());
        file = passwordEntryActivity.getFileStreamPath(CryptoManager.Constants.KEYSTORE_PATH);
        if(file.exists())
            assertTrue(file.delete());
    }
    //allows us to access the interface
    @UiThreadTest
    public void testNoPassword() throws Exception
    {
        passwordEntryActivity  = getActivity();

        EditText passwordEntryEditText = 
                (EditText) passwordEntryActivity.findViewById(
                crypnote.controller.main.R.id.passwordentrylayout_passwordedittext);
        Button unlockButton = (Button) passwordEntryActivity.findViewById(
                                        crypnote.controller.main.R.id.passwordentrylayout_unlockbutton);


        int passwordResult = CryptoManager.passwordIsRight("x", getActivity());
        assertTrue(passwordResult == 0);

        //pass a wrong password to the edittext and click the unlock button
        passwordEntryEditText.setText("x");
        assertTrue(unlockButton.performClick());

        //get the foreground activity class name
        ActivityManager am = (ActivityManager) passwordEntryActivity.
                                                        getSystemService(Context.ACTIVITY_SERVICE);

        // get the info from the currently running task
        List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1); 

        ComponentName componentInfo = taskInfo.get(0).topActivity;
        String foregroundClassName = componentInfo.getShortClassName();

        //don't forget the leading '.'
        assertTrue(!foregroundClassName.equals(".PasswordEntryActivity"));
    }
}

CryptoKeystoreTests:

public class CryptoKeystoreTests extends android.test.ActivityInstrumentationTestCase2<
                                                                crypnote.controller.main.PasswordEntryActivity>
{
    public void testKeystore() throws Exception
    {
        Context context = getActivity();

        //delete the database if it exists
        File file = context.getFileStreamPath(DBInterface.Constants.DatabaseName);
        if(file.exists())
            assertTrue(file.delete());
        file = context.getFileStreamPath(CryptoManager.Constants.KEYSTORE_PATH);
        if(file.exists())
            assertTrue(file.delete());

        CryptoManager cryptoManager=null;
        String password = CryptoManager.Constants.DEBUG_PASSWORD;
        FileInputStream fis=null;

            //the cryptomanager will generate a new key and keystore
            cryptoManager = new CryptoManager(password, context);
            Key CRYPTOKEY = cryptoManager.getKey();
            cryptoManager.close();

            //initialize KeyStore
            KeyStore keystore = KeyStore.getInstance(Constants.KEYSTORE_INSTANCE_TYPE);
            fis = context.openFileInput(CryptoManager.Constants.KEYSTORE_PATH);
            keystore.load(fis, password.toCharArray());

            assertTrue(keystore.containsAlias(Constants.APP_ALIAS));    
            assertTrue(keystore.isKeyEntry(Constants.APP_ALIAS));

            Key key = keystore.getKey(CryptoManager.Constants.APP_ALIAS, 
                                        password.toCharArray());
            assertTrue(key.getAlgorithm().equals(CryptoManager.Constants.PROVIDER_NAME));

            assertTrue(key.getAlgorithm().equals(CRYPTOKEY.getAlgorithm()));
            assertTrue(key.getFormat().equals(CRYPTOKEY.getFormat()));

            if(fis != null)
                fis.close();
    }
}

编辑:NewPasswordActivity.onCreate:

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.new_password_layout);
}
4

1 回答 1

2

它挂起是因为 PasswordEntryActivityTests 没有释放/完成在其自己的运行生命周期中自己处理/创建的资源/UI 事件,更具体地说,是新打开的 NewPasswordActivity。

PasswordEntryActivityTests 首先测试 PasswordEntryActivity 的创建,即getActivity(),因此,根据条件,启动第二个 NewPasswordActivity,新打开的 NewPasswordActivity 占据前台窗口并永久保留,开发人员有责任在您完成后正确释放它测试。

在仪器测试中,从当前活动检测/监控第二个活动启动的正确方法是使用ActivityMonitor,请参见下面的伪代码:

// No password result starting a second activity.
public void testNoPassword() {
  // register NewPasswordActivity that need to be monitored.
  ActivityMonitor activityMonitor = getInstrumentation().addMonitor(NewPasswordActivity.class.getName(), null, false);

  // Get current activity, it will start NewPasswordActivity in consequence.
  PasswordEntryActivity currentActivity = getActivity();

  NewPasswordActivity nextActivity = getInstrumentation().waitForMonitorWithTimeout(activityMonitor, 5);
  // NewPasswordActivity is opened and captured.
  assertNotNull(nextActivity);
  // Don't forget to release/finish NewPasswordActivity after test finish.
  nextActivity.finish();
}
于 2013-02-02T12:00:20.517 回答