diff --git a/MasterPassword/Java/masterpassword-algorithm/pom.xml b/MasterPassword/Java/masterpassword-algorithm/pom.xml
index daee227c..757fa0d3 100644
--- a/MasterPassword/Java/masterpassword-algorithm/pom.xml
+++ b/MasterPassword/Java/masterpassword-algorithm/pom.xml
@@ -23,7 +23,7 @@
com.lyndir.lhunath.opal
opal-system
- 1.6-p8
+ 1.6-p9
joda-time
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
index 6e152180..f4faaf43 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
@@ -1,6 +1,7 @@
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Arrays;
@@ -74,7 +75,7 @@ public abstract class MasterKey {
return idForBytes( getKey() );
}
- public abstract String encode(@Nonnull final String siteName, final MPSiteType siteType, int siteCounter,
+ public abstract String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull final UnsignedInteger siteCounter,
final MPSiteVariant siteVariant, @Nullable final String siteContext);
public boolean isValid() {
@@ -106,7 +107,9 @@ public abstract class MasterKey {
return this;
}
- protected abstract byte[] bytesForInt(final int integer);
+ protected abstract byte[] bytesForInt(final int number);
+
+ protected abstract byte[] bytesForInt(@Nonnull final UnsignedInteger number);
protected abstract byte[] idForBytes(final byte[] bytes);
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
index c1efe212..4ece61c3 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
@@ -3,6 +3,7 @@ package com.lyndir.masterpassword;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
+import com.google.common.primitives.UnsignedInteger;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
@@ -10,14 +11,15 @@ import java.nio.*;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.Arrays;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* bugs:
- * - does math with chars whose signedness was platform-dependent.
- * - miscounted the byte-length fromInt multi-byte site names.
- * - miscounted the byte-length fromInt multi-byte full names.
+ * - V2: miscounted the byte-length fromInt multi-byte full names.
+ * - V1: miscounted the byte-length fromInt multi-byte site names.
+ * - V0: does math with chars whose signedness was platform-dependent.
*
* @author lhunath, 2014-08-30
*/
@@ -75,18 +77,19 @@ public class MasterKeyV0 extends MasterKey {
}
}
- public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
- @Nullable final String siteContext) {
+ @Override
+ public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
+ final MPSiteVariant siteVariant, @Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
- logger.trc( "siteCounter: %d", siteCounter );
+ logger.trc( "siteCounter: %d", siteCounter.longValue() );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
- if (siteCounter == 0)
- siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
+ if (siteCounter.longValue() == 0)
+ siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (300 * 1000)) * 300 );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MP_charset );
@@ -108,7 +111,7 @@ public class MasterKeyV0 extends MasterKey {
int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
- Arrays.fill( buf.array(), sitePasswordSeedBytes[i] > 0? (byte)0x00: (byte) 0xFF );
+ Arrays.fill( buf.array(), sitePasswordSeedBytes[i] > 0? (byte) 0x00: (byte) 0xFF );
buf.position( 2 );
buf.put( sitePasswordSeedBytes[i] ).rewind();
sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
@@ -135,8 +138,13 @@ public class MasterKeyV0 extends MasterKey {
}
@Override
- protected byte[] bytesForInt(final int integer) {
- return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
+ protected byte[] bytesForInt(final int number) {
+ return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( number ).array();
+ }
+
+ @Override
+ protected byte[] bytesForInt(@Nonnull final UnsignedInteger number) {
+ return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( number.intValue() ).array();
}
@Override
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
index bcb9d58d..d58dcf4e 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
@@ -2,15 +2,17 @@ package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* bugs:
- * - miscounted the byte-length fromInt multi-byte site names.
- * - miscounted the byte-length fromInt multi-byte full names.
+ * - V2: miscounted the byte-length fromInt multi-byte full names.
+ * - V1: miscounted the byte-length fromInt multi-byte site names.
*
* @author lhunath, 2014-08-30
*/
@@ -29,18 +31,19 @@ public class MasterKeyV1 extends MasterKeyV0 {
return Version.V1;
}
- public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
- @Nullable final String siteContext) {
+ @Override
+ public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
+ final MPSiteVariant siteVariant, @Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
- logger.trc( "siteCounter: %d", siteCounter );
+ logger.trc( "siteCounter: %d", siteCounter.longValue() );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
- if (siteCounter == 0)
- siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
+ if (siteCounter.longValue() == 0)
+ siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (300 * 1000)) * 300 );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MP_charset );
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
index 7da2856a..e4019d52 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
@@ -2,14 +2,16 @@ package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* bugs:
- * - miscounted the byte-length fromInt multi-byte full names.
+ * - V2: miscounted the byte-length fromInt multi-byte full names.
*
* @author lhunath, 2014-08-30
*/
@@ -28,18 +30,19 @@ public class MasterKeyV2 extends MasterKeyV1 {
return Version.V2;
}
- public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
- @Nullable final String siteContext) {
+ @Override
+ public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
+ final MPSiteVariant siteVariant, @Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
- logger.trc( "siteCounter: %d", siteCounter );
+ logger.trc( "siteCounter: %d", siteCounter.longValue() );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
- if (siteCounter == 0)
- siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
+ if (siteCounter.longValue() == 0)
+ siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (300 * 1000)) * 300 );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MP_charset );
diff --git a/MasterPassword/Java/masterpassword-android/AndroidManifest.xml b/MasterPassword/Java/masterpassword-android/AndroidManifest.xml
index bf60405d..2f9c1f5f 100644
--- a/MasterPassword/Java/masterpassword-android/AndroidManifest.xml
+++ b/MasterPassword/Java/masterpassword-android/AndroidManifest.xml
@@ -12,13 +12,13 @@
android:icon="@drawable/icon"
android:label="@string/app_name"
android:allowBackup="true">
-
+
-
+
diff --git a/MasterPassword/Java/masterpassword-android/pom.xml b/MasterPassword/Java/masterpassword-android/pom.xml
index bb16bc0e..906bbfbe 100644
--- a/MasterPassword/Java/masterpassword-android/pom.xml
+++ b/MasterPassword/Java/masterpassword-android/pom.xml
@@ -107,6 +107,13 @@
GIT-SNAPSHOT
+
+ com.lyndir.masterpassword
+ masterpassword-tests
+ GIT-SNAPSHOT
+
+
+
com.jakewharton
butterknife
diff --git a/MasterPassword/Java/masterpassword-android/res/layout/activity_test.xml b/MasterPassword/Java/masterpassword-android/res/layout/activity_test.xml
new file mode 100644
index 00000000..81170812
--- /dev/null
+++ b/MasterPassword/Java/masterpassword-android/res/layout/activity_test.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MasterPassword/Java/masterpassword-android/res/values/strings.xml b/MasterPassword/Java/masterpassword-android/res/values/strings.xml
index 82556446..e01c7f06 100644
--- a/MasterPassword/Java/masterpassword-android/res/values/strings.xml
+++ b/MasterPassword/Java/masterpassword-android/res/values/strings.xml
@@ -11,4 +11,12 @@
Password #
Algorithm
+ Test suite unavailable.
+ Exit
+ Testing device\'s password generation integrity…
+ Please Stand By…
+ Incompatible device or OS.
+ Exit
+ Integrity checks passed!
+ Continue
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
index dd5c62db..cd738aa2 100644
--- a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
+++ b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
@@ -1,5 +1,6 @@
package com.lyndir.masterpassword;
+import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import android.app.*;
@@ -16,9 +17,9 @@ import android.widget.*;
import butterknife.ButterKnife;
import butterknife.InjectView;
import com.google.common.base.Throwables;
+import com.google.common.primitives.UnsignedInteger;
import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
-import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import java.util.*;
import java.util.concurrent.*;
import javax.annotation.Nullable;
@@ -87,6 +88,10 @@ public class EmergencyActivity extends Activity {
private int hc_masterPassword;
private String sitePassword;
+ public static void start(Context context) {
+ context.startActivity( new Intent( context, EmergencyActivity.class ) );
+ }
+
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
@@ -261,7 +266,7 @@ public class EmergencyActivity extends Activity {
private void updateSitePassword() {
final String siteName = siteNameField.getText().toString();
final MPSiteType type = (MPSiteType) siteTypeField.getSelectedItem();
- final int counter = ConversionUtils.toIntegerNN( counterField.getText() );
+ final UnsignedInteger counter = UnsignedInteger.valueOf( ifNotNullElse( counterField.getText(), "1" ).toString() );
if (masterKeyFuture == null || siteName.isEmpty() || type == null) {
sitePasswordField.setText( "" );
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/MainThreadExecutor.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/MainThreadExecutor.java
new file mode 100644
index 00000000..98b59437
--- /dev/null
+++ b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/MainThreadExecutor.java
@@ -0,0 +1,82 @@
+package com.lyndir.masterpassword;
+
+import android.os.Handler;
+import android.os.Looper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import java.util.*;
+import java.util.concurrent.*;
+
+
+/**
+ * @author lhunath, 2015-12-22
+ */
+public class MainThreadExecutor extends AbstractExecutorService {
+
+ private final Handler mHandler = new Handler( Looper.getMainLooper() );
+ private final Set commands = Sets.newLinkedHashSet();
+ private boolean shutdown;
+
+ @Override
+ public void execute(final Runnable command) {
+ if (shutdown)
+ throw new RejectedExecutionException( "This executor has been shut down" );
+
+ synchronized (commands) {
+ commands.add( command );
+
+ mHandler.post( new Runnable() {
+ @Override
+ public void run() {
+ synchronized (commands) {
+ if (!commands.remove( command ))
+ // Command was removed, not executing.
+ return;
+ }
+
+ command.run();
+ }
+ } );
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ shutdown = true;
+ }
+
+ @Override
+ public List shutdownNow() {
+ shutdown = true;
+ mHandler.removeCallbacksAndMessages( null );
+
+ synchronized (commands) {
+ ImmutableList pendingTasks = ImmutableList.copyOf( commands );
+ commands.clear();
+ commands.notify();
+ return pendingTasks;
+ }
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return shutdown;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ synchronized (commands) {
+ return shutdown && commands.isEmpty();
+ }
+ }
+
+ @Override
+ public boolean awaitTermination(final long timeout, final TimeUnit unit)
+ throws InterruptedException {
+ if (isTerminated())
+ return true;
+
+ commands.wait( unit.toMillis( timeout ) );
+ return isTerminated();
+ }
+}
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/TestActivity.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/TestActivity.java
new file mode 100644
index 00000000..62625fac
--- /dev/null
+++ b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/TestActivity.java
@@ -0,0 +1,171 @@
+package com.lyndir.masterpassword;
+
+import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
+
+import android.app.*;
+import android.os.*;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.*;
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+import com.google.common.base.*;
+import com.google.common.collect.*;
+import com.google.common.util.concurrent.*;
+import com.lyndir.lhunath.opal.system.logging.Logger;
+import java.util.Set;
+import java.util.concurrent.*;
+import javax.annotation.Nullable;
+
+
+public class TestActivity extends Activity implements MPTestSuite.Listener {
+
+ @SuppressWarnings("UnusedDeclaration")
+ private static final Logger logger = Logger.get( TestActivity.class );
+
+ private final ListeningExecutorService backgroundExecutor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
+ private final ListeningExecutorService mainExecutor = MoreExecutors.listeningDecorator( new MainThreadExecutor() );
+
+ @InjectView(R.id.progressView)
+ ProgressBar progressView;
+
+ @InjectView(R.id.statusView)
+ TextView statusView;
+
+ @InjectView(R.id.logView)
+ TextView logView;
+
+ @InjectView(R.id.actionButton)
+ Button actionButton;
+
+ private MPTestSuite testSuite;
+ private ListenableFuture testFuture;
+ private Runnable action;
+ private ImmutableSet testNames;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate( savedInstanceState );
+ Res.init( getResources() );
+
+ getWindow().setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE );
+ setContentView( R.layout.activity_test );
+ ButterKnife.inject( this );
+
+ try {
+ setStatus( 0, 0, null );
+ testSuite = new MPTestSuite();
+ testSuite.setListener( this );
+ testNames = FluentIterable.from( testSuite.getTests().getCases() ).transform(
+ new Function() {
+ @Nullable
+ @Override
+ public String apply(@Nullable final MPTests.Case input) {
+ return input == null? null: input.identifier;
+ }
+ } ).filter( Predicates.notNull() ).toSet();
+ }
+ catch (MPTestSuite.UnavailableException e) {
+ logger.err( e, "While loading test suite" );
+ setStatus( R.string.tests_unavailable, R.string.tests_btn_unavailable, new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ } );
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ final Set integrityTestsPassed = getPreferences( MODE_PRIVATE ).getStringSet( "integrityTestsPassed",
+ ImmutableSet.of() );
+ if (!FluentIterable.from( testNames ).anyMatch( new Predicate() {
+ @Override
+ public boolean apply(@Nullable final String testName) {
+ return !integrityTestsPassed.contains( testName );
+ }
+ } )) {
+ // None of the tests we need to perform were missing from the tests that have already been passed on this device.
+ finish();
+ EmergencyActivity.start( TestActivity.this );
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (testFuture == null) {
+ setStatus( R.string.tests_testing, R.string.tests_btn_testing, null );
+ Futures.addCallback( testFuture = backgroundExecutor.submit( testSuite ), new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable final Boolean result) {
+ if (result != null && result)
+ setStatus( R.string.tests_passed, R.string.tests_btn_passed, new Runnable() {
+ @Override
+ public void run() {
+ getPreferences( MODE_PRIVATE ).edit().putStringSet( "integrityTestsPassed", testNames ).apply();
+ finish();
+ EmergencyActivity.start( TestActivity.this );
+ }
+ } );
+ else
+ setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ } );
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ logger.err( t, "While running test suite" );
+ setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ } );
+ }
+ }, mainExecutor );
+ }
+ }
+
+ public void onAction(View v) {
+ if (action != null)
+ action.run();
+ }
+
+ private void setStatus(int statusId, int buttonId, @Nullable Runnable action) {
+ this.action = action;
+
+ if (statusId == 0)
+ statusView.setText( null );
+ else
+ statusView.setText( statusId );
+
+ if (buttonId == 0)
+ actionButton.setText( null );
+ else
+ actionButton.setText( buttonId );
+ actionButton.setEnabled( action != null );
+ }
+
+ @Override
+ public void progress(final int current, final int max, final String messageFormat, final Object... args) {
+ runOnUiThread( new Runnable() {
+ @Override
+ public void run() {
+ logView.append( strf( '\n' + messageFormat, args ) );
+
+ progressView.setMax( max );
+ progressView.setProgress( current );
+ }
+ } );
+ }
+}
+
diff --git a/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java b/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java
index b818c988..de42d2f0 100644
--- a/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java
+++ b/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
@@ -23,6 +22,7 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.common.io.LineReader;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.lhunath.opal.system.util.StringUtils;
import java.io.*;
@@ -52,7 +52,7 @@ public class CLI {
MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName );
MPSiteVariant variant = MPSiteVariant.Password;
String siteCounterName = ifNotNullElse( System.getenv( ENV_SITECOUNTER ), "" );
- int siteCounter = siteCounterName.isEmpty()? 1: Integer.parseInt( siteCounterName );
+ UnsignedInteger siteCounter = siteCounterName.isEmpty()? UnsignedInteger.valueOf( 1 ): UnsignedInteger.valueOf( siteCounterName );
// Parse information from option arguments.
boolean userNameArg = false, typeArg = false, counterArg = false, variantArg = false, contextArg = false;
@@ -77,7 +77,7 @@ public class CLI {
else if ("-c".equals( arg ) || "--counter".equals( arg ))
counterArg = true;
else if (counterArg) {
- siteCounter = ConversionUtils.toIntegerNN( arg );
+ siteCounter = UnsignedInteger.valueOf( arg );
counterArg = false;
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoSite.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoSite.java
index 9e018ac1..493e6ade 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoSite.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoSite.java
@@ -1,5 +1,6 @@
package com.lyndir.masterpassword.gui;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPSiteType;
import com.lyndir.masterpassword.MasterKey;
@@ -11,10 +12,10 @@ public class IncognitoSite extends Site {
private String siteName;
private MPSiteType siteType;
- private int siteCounter;
+ private UnsignedInteger siteCounter;
private MasterKey.Version algorithmVersion;
- public IncognitoSite(final String siteName, final MPSiteType siteType, final int siteCounter,
+ public IncognitoSite(final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter,
final MasterKey.Version algorithmVersion) {
this.siteName = siteName;
this.siteType = siteType;
@@ -48,11 +49,11 @@ public class IncognitoSite extends Site {
this.algorithmVersion = algorithmVersion;
}
- public int getSiteCounter() {
+ public UnsignedInteger getSiteCounter() {
return siteCounter;
}
- public void setSiteCounter(final int siteCounter) {
+ public void setSiteCounter(final UnsignedInteger siteCounter) {
this.siteCounter = siteCounter;
}
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelSite.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelSite.java
index 614e1456..53e1f3ea 100755
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelSite.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelSite.java
@@ -1,5 +1,6 @@
package com.lyndir.masterpassword.gui;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPSiteType;
import com.lyndir.masterpassword.MasterKey;
import com.lyndir.masterpassword.model.*;
@@ -55,13 +56,13 @@ public class ModelSite extends Site {
}
}
- public int getSiteCounter() {
+ public UnsignedInteger getSiteCounter() {
return model.getSiteCounter();
}
@Override
- public void setSiteCounter(final int siteCounter) {
- if (siteCounter != getSiteCounter()) {
+ public void setSiteCounter(final UnsignedInteger siteCounter) {
+ if (siteCounter.equals( getSiteCounter() )) {
model.setSiteCounter( siteCounter );
MPUserFileManager.get().save();
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
index 2bb87fa8..d3d1d144 100755
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
@@ -4,6 +4,7 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
+import com.google.common.primitives.UnsignedInteger;
import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.util.PredicateNN;
import com.lyndir.masterpassword.*;
@@ -26,7 +27,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
private final User user;
private final Components.GradientPanel root;
private final JTextField siteNameField;
- private final JButton siteActionButton;
+ private final JButton siteActionButton;
private final JComboBox siteTypeField;
private final JComboBox siteVersionField;
private final JSpinner siteCounterField;
@@ -37,8 +38,8 @@ public class PasswordFrame extends JFrame implements DocumentListener {
private final Font passwordEchoFont;
@Nullable
- private Site currentSite;
- private boolean updatingUI;
+ private Site currentSite;
+ private boolean updatingUI;
public PasswordFrame(User user)
throws HeadlessException {
@@ -62,70 +63,70 @@ public class PasswordFrame extends JFrame implements DocumentListener {
sitePanel.add( Components.stud() );
// Site Name
- sitePanel.add(Components.label("Site Name:"));
+ sitePanel.add( Components.label( "Site Name:" ) );
JComponent siteControls = Components.boxLayout( BoxLayout.LINE_AXIS, //
siteNameField = Components.textField(), Components.stud(),
siteActionButton = Components.button( "Add Site" ) );
- siteNameField.getDocument().addDocumentListener(this);
- siteNameField.addActionListener(new ActionListener() {
+ siteNameField.getDocument().addDocumentListener( this );
+ siteNameField.addActionListener( new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
- Futures.addCallback(updatePassword(true), new FutureCallback() {
+ Futures.addCallback( updatePassword( true ), new FutureCallback() {
@Override
public void onSuccess(@Nullable final String sitePassword) {
- StringSelection clipboardContents = new StringSelection(sitePassword);
- Toolkit.getDefaultToolkit().getSystemClipboard().setContents(clipboardContents, null);
+ StringSelection clipboardContents = new StringSelection( sitePassword );
+ Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
- SwingUtilities.invokeLater(new Runnable() {
+ SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
- passwordField.setText(null);
- siteNameField.setText(null);
+ passwordField.setText( null );
+ siteNameField.setText( null );
- dispatchEvent(new WindowEvent(PasswordFrame.this, WindowEvent.WINDOW_CLOSING));
+ dispatchEvent( new WindowEvent( PasswordFrame.this, WindowEvent.WINDOW_CLOSING ) );
}
- });
+ } );
}
@Override
public void onFailure(final Throwable t) {
}
- });
+ } );
}
- });
- siteActionButton.addActionListener(new ActionListener() {
+ } );
+ siteActionButton.addActionListener( new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (currentSite == null)
return;
else if (currentSite instanceof ModelSite)
- PasswordFrame.this.user.deleteSite(currentSite);
+ PasswordFrame.this.user.deleteSite( currentSite );
else
- PasswordFrame.this.user.addSite(currentSite);
+ PasswordFrame.this.user.addSite( currentSite );
siteNameField.requestFocus();
updatePassword( true );
}
- });
+ } );
sitePanel.add( siteControls );
sitePanel.add( Components.stud() );
// Site Type & Counter
MPSiteType[] types = Iterables.toArray( MPSiteType.forClass( MPSiteTypeClass.Generated ), MPSiteType.class );
- JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
- siteTypeField = Components.comboBox( types ), //
- Components.stud(), //
+ JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
+ siteTypeField = Components.comboBox( types ), //
+ Components.stud(), //
siteVersionField = Components.comboBox( MasterKey.Version.values() ), //
- Components.stud(), //
+ Components.stud(), //
siteCounterField = Components.spinner(
- new SpinnerNumberModel( 1, 1, Integer.MAX_VALUE, 1 ) ) );
+ new SpinnerNumberModel( 1L, 1L, UnsignedInteger.MAX_VALUE, 1L ) ) );
sitePanel.add( siteSettings );
siteTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
siteTypeField.setSelectedItem( MPSiteType.GeneratedLong );
siteTypeField.addItemListener( new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent e) {
- updatePassword(true);
+ updatePassword( true );
}
} );
@@ -135,7 +136,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
siteVersionField.addItemListener( new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent e) {
- updatePassword(true);
+ updatePassword( true );
}
} );
@@ -144,7 +145,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
siteCounterField.addChangeListener( new ChangeListener() {
@Override
public void stateChanged(final ChangeEvent e) {
- updatePassword(true);
+ updatePassword( true );
}
} );
@@ -161,11 +162,11 @@ public class PasswordFrame extends JFrame implements DocumentListener {
// Password
passwordField = Components.passwordField();
- passwordField.setAlignmentX(Component.CENTER_ALIGNMENT);
- passwordField.setHorizontalAlignment(JTextField.CENTER);
- passwordField.putClientProperty("JPasswordField.cutCopyAllowed", true);
- passwordField.setEditable(false);
- passwordField.setBackground(null);
+ passwordField.setAlignmentX( Component.CENTER_ALIGNMENT );
+ passwordField.setHorizontalAlignment( JTextField.CENTER );
+ passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true );
+ passwordField.setEditable( false );
+ passwordField.setBackground( null );
passwordField.setBorder( null );
passwordEchoChar = passwordField.getEchoChar();
passwordEchoFont = passwordField.getFont().deriveFont( 40f );
@@ -174,7 +175,8 @@ public class PasswordFrame extends JFrame implements DocumentListener {
// Tip
tipLabel = Components.label( " ", SwingConstants.CENTER );
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
- JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, Box.createGlue(), passwordField, Box.createGlue(), tipLabel );
+ JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, Box.createGlue(), passwordField,
+ Box.createGlue(), tipLabel );
passwordContainer.setOpaque( true );
passwordContainer.setBackground( Color.white );
passwordContainer.setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 ) );
@@ -202,27 +204,27 @@ public class PasswordFrame extends JFrame implements DocumentListener {
if (updatingUI)
return Futures.immediateCancelledFuture();
if (siteNameQuery == null || siteNameQuery.isEmpty() || !user.isKeyAvailable()) {
- siteActionButton.setVisible(false);
+ siteActionButton.setVisible( false );
tipLabel.setText( null );
passwordField.setText( null );
return Futures.immediateCancelledFuture();
}
- final MPSiteType siteType = siteTypeField.getModel().getElementAt(siteTypeField.getSelectedIndex());
- final MasterKey.Version siteVersion = siteVersionField.getItemAt(siteVersionField.getSelectedIndex());
- final int siteCounter = (Integer) siteCounterField.getValue();
+ final MPSiteType siteType = siteTypeField.getModel().getElementAt( siteTypeField.getSelectedIndex() );
+ final MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
+ final UnsignedInteger siteCounter = UnsignedInteger.valueOf( ((Number) siteCounterField.getValue()).longValue() );
- Iterable siteResults = user.findSitesByName(siteNameQuery);
+ Iterable siteResults = user.findSitesByName( siteNameQuery );
if (!allowNameCompletion)
- siteResults = FluentIterable.from(siteResults).filter(new PredicateNN() {
+ siteResults = FluentIterable.from( siteResults ).filter( new PredicateNN() {
@Override
public boolean apply(Site input) {
- return siteNameQuery.equals(input.getSiteName());
+ return siteNameQuery.equals( input.getSiteName() );
}
- });
- final Site site = Iterables.getFirst(siteResults,
- new IncognitoSite(siteNameQuery, siteType, siteCounter, siteVersion) );
- if (currentSite != null && site.getSiteName().equals(currentSite.getSiteName())) {
+ } );
+ final Site site = Iterables.getFirst( siteResults,
+ new IncognitoSite( siteNameQuery, siteType, siteCounter, siteVersion ) );
+ if (currentSite != null && currentSite.getSiteName().equals( site.getSiteName() )) {
site.setSiteType( siteType );
site.setAlgorithmVersion( siteVersion );
site.setSiteCounter( siteCounter );
@@ -244,12 +246,12 @@ public class PasswordFrame extends JFrame implements DocumentListener {
public void run() {
updatingUI = true;
currentSite = site;
- siteActionButton.setVisible(user instanceof ModelUser);
+ siteActionButton.setVisible( user instanceof ModelUser );
if (currentSite instanceof ModelSite)
- siteActionButton.setText("Delete Site");
+ siteActionButton.setText( "Delete Site" );
else
- siteActionButton.setText("Add Site");
- siteTypeField.setSelectedItem(currentSite.getSiteType());
+ siteActionButton.setText( "Add Site" );
+ siteTypeField.setSelectedItem( currentSite.getSiteType() );
siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() );
siteCounterField.setValue( currentSite.getSiteCounter() );
siteNameField.setText( currentSite.getSiteName() );
@@ -273,16 +275,16 @@ public class PasswordFrame extends JFrame implements DocumentListener {
@Override
public void insertUpdate(final DocumentEvent e) {
- updatePassword(true);
+ updatePassword( true );
}
@Override
public void removeUpdate(final DocumentEvent e) {
- updatePassword(false);
+ updatePassword( false );
}
@Override
public void changedUpdate(final DocumentEvent e) {
- updatePassword(true);
+ updatePassword( true );
}
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Site.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Site.java
index 9cee01e2..b950ab38 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Site.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Site.java
@@ -2,6 +2,7 @@ package com.lyndir.masterpassword.gui;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPSiteType;
import com.lyndir.masterpassword.MasterKey;
@@ -23,9 +24,9 @@ public abstract class Site {
public abstract void setAlgorithmVersion(final MasterKey.Version algorithmVersion);
- public abstract int getSiteCounter();
+ public abstract UnsignedInteger getSiteCounter();
- public abstract void setSiteCounter(final int siteCounter);
+ public abstract void setSiteCounter(final UnsignedInteger siteCounter);
@Override
public String toString() {
diff --git a/MasterPassword/Java/masterpassword-model/pom.xml b/MasterPassword/Java/masterpassword-model/pom.xml
index 34ec8ae2..8b22d94c 100644
--- a/MasterPassword/Java/masterpassword-model/pom.xml
+++ b/MasterPassword/Java/masterpassword-model/pom.xml
@@ -26,6 +26,11 @@
GIT-SNAPSHOT
+
+
+ joda-time
+ joda-time
+
com.google.auto.value
auto-value
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
index 71d8edb2..9c8ce291 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
@@ -2,6 +2,7 @@ package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*;
import java.util.Objects;
import javax.annotation.Nullable;
@@ -13,15 +14,15 @@ import org.joda.time.Instant;
*/
public class MPSite {
- public static final MPSiteType DEFAULT_TYPE = MPSiteType.GeneratedLong;
- public static final int DEFAULT_COUNTER = 1;
+ public static final MPSiteType DEFAULT_TYPE = MPSiteType.GeneratedLong;
+ public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 );
private final MPUser user;
private MasterKey.Version algorithmVersion;
private Instant lastUsed;
private String siteName;
private MPSiteType siteType;
- private int siteCounter;
+ private UnsignedInteger siteCounter;
private int uses;
private String loginName;
@@ -29,7 +30,7 @@ public class MPSite {
this( user, siteName, DEFAULT_TYPE, DEFAULT_COUNTER );
}
- public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final int siteCounter) {
+ public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter) {
this.user = user;
this.algorithmVersion = MasterKey.Version.CURRENT;
this.lastUsed = new Instant();
@@ -39,7 +40,7 @@ public class MPSite {
}
protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName,
- final MPSiteType siteType, final int siteCounter, final int uses, @Nullable final String loginName,
+ final MPSiteType siteType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName,
@Nullable final String importContent) {
this.user = user;
this.algorithmVersion = algorithmVersion;
@@ -101,11 +102,11 @@ public class MPSite {
this.siteType = siteType;
}
- public int getSiteCounter() {
+ public UnsignedInteger getSiteCounter() {
return siteCounter;
}
- public void setSiteCounter(final int siteCounter) {
+ public void setSiteCounter(final UnsignedInteger siteCounter) {
this.siteCounter = siteCounter;
}
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java
index 8652e7ca..6e0e5edc 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java
@@ -5,6 +5,7 @@ import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.io.CharStreams;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
@@ -139,7 +140,7 @@ public class MPSiteUnmarshaller {
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
siteMatcher.group( 7 ), //
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
- ConversionUtils.toIntegerNN( siteMatcher.group( 5 ).replace( ":", "" ) ), //
+ UnsignedInteger.valueOf( siteMatcher.group( 5 ).replace( ":", "" ) ), //
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
siteMatcher.group( 6 ), //
siteMatcher.group( 8 ) );
diff --git a/MasterPassword/Java/masterpassword-tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java b/MasterPassword/Java/masterpassword-tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java
index 690195c1..d6bca428 100644
--- a/MasterPassword/Java/masterpassword-tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java
+++ b/MasterPassword/Java/masterpassword-tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java
@@ -1,24 +1,33 @@
package com.lyndir.masterpassword;
-import com.google.common.io.Resources;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger;
+import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.lhunath.opal.system.util.NNFunctionNN;
-import java.net.URL;
+import java.io.IOException;
+import java.util.Deque;
+import java.util.List;
+import java.util.concurrent.Callable;
import javax.annotation.Nonnull;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
+import javax.xml.parsers.*;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.DefaultHandler2;
/**
* @author lhunath, 2015-12-22
*/
-public class MPTestSuite {
+public class MPTestSuite implements Callable {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPTestSuite.class );
private static final String DEFAULT_RESOURCE_NAME = "mpw_tests.xml";
- private MPTests tests;
+ private MPTests tests;
+ private Listener listener;
public MPTestSuite()
throws UnavailableException {
@@ -28,15 +37,78 @@ public class MPTestSuite {
public MPTestSuite(String resourceName)
throws UnavailableException {
try {
- URL testCasesResource = Resources.getResource( resourceName );
- tests = (MPTests) JAXBContext.newInstance( MPTests.class ).createUnmarshaller().unmarshal( testCasesResource );
+ tests = new MPTests();
+ tests.cases = Lists.newLinkedList();
+ SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+ parser.parse( Thread.currentThread().getContextClassLoader().getResourceAsStream( resourceName ), new DefaultHandler2() {
+ private Deque currentTags = Lists.newLinkedList();
+ private Deque currentTexts = Lists.newLinkedList();
+ private MPTests.Case currentCase;
- for (MPTests.Case testCase : tests.getCases())
- testCase.initializeParentHierarchy( tests );
+ @Override
+ public void startElement(final String uri, final String localName, final String qName, final Attributes attributes)
+ throws SAXException {
+ super.startElement( uri, localName, qName, attributes );
+ currentTags.push( qName );
+ currentTexts.push( new StringBuilder() );
+
+ if ("case".equals( qName )) {
+ currentCase = new MPTests.Case();
+ currentCase.identifier = attributes.getValue( "id" );
+ currentCase.parent = attributes.getValue( "parent" );
+ }
+ }
+
+ @Override
+ public void endElement(final String uri, final String localName, final String qName)
+ throws SAXException {
+ super.endElement( uri, localName, qName );
+ Preconditions.checkState( qName.equals( currentTags.pop() ) );
+ String text = currentTexts.pop().toString();
+
+ if ("case".equals( qName ))
+ tests.cases.add( currentCase );
+ if ("algorithm".equals( qName ))
+ currentCase.algorithm = ConversionUtils.toInteger( text ).orNull();
+ if ("fullName".equals( qName ))
+ currentCase.fullName = text;
+ if ("masterPassword".equals( qName ))
+ currentCase.masterPassword = text;
+ if ("keyID".equals( qName ))
+ currentCase.keyID = text;
+ if ("siteName".equals( qName ))
+ currentCase.siteName = text;
+ if ("siteCounter".equals( qName ))
+ currentCase.siteCounter = text.isEmpty()? null: UnsignedInteger.valueOf( text );
+ if ("siteType".equals( qName ))
+ currentCase.siteType = text;
+ if ("siteVariant".equals( qName ))
+ currentCase.siteVariant = text;
+ if ("siteContext".equals( qName ))
+ currentCase.siteContext = text;
+ if ("result".equals( qName ))
+ currentCase.result = text;
+ }
+
+ @Override
+ public void characters(final char[] ch, final int start, final int length)
+ throws SAXException {
+ super.characters( ch, start, length );
+
+ currentTexts.peek().append( ch, start, length );
+ }
+ } );
}
- catch (IllegalArgumentException | JAXBException e) {
+ catch (IllegalArgumentException | ParserConfigurationException | SAXException | IOException e) {
throw new UnavailableException( e );
}
+
+ for (MPTests.Case testCase : tests.getCases())
+ testCase.initializeParentHierarchy( tests );
+ }
+
+ public void setListener(final Listener listener) {
+ this.listener = listener;
}
public MPTests getTests() {
@@ -44,22 +116,39 @@ public class MPTestSuite {
}
public boolean forEach(String testName, NNFunctionNN testFunction) {
- for (MPTests.Case testCase : tests.getCases()) {
+ List cases = tests.getCases();
+ for (int c = 0; c < cases.size(); c++) {
+ MPTests.Case testCase = cases.get( c );
if (testCase.getResult().isEmpty())
continue;
- logger.inf( "[%s] on %s...", testName, testCase.getIdentifier() );
+ progress( Logger.Target.INFO, c, cases.size(), //
+ "[%s] on %s...", testName, testCase.getIdentifier() );
+
if (!testFunction.apply( testCase )) {
- logger.err( "[%s] on %s: FAILED!", testName, testCase.getIdentifier() );
+ progress( Logger.Target.ERROR, cases.size(), cases.size(), //
+ "[%s] on %s: FAILED!", testName, testCase.getIdentifier() );
+
return false;
}
- logger.inf( "[%s] on %s: passed!", testName, testCase.getIdentifier() );
+
+ progress( Logger.Target.INFO, c + 1, cases.size(), //
+ "[%s] on %s: passed!", testName, testCase.getIdentifier() );
}
return true;
}
- public boolean run() {
+ private void progress(final Logger.Target target, final int current, final int max, final String format, final Object... args) {
+ logger.log( target, format, args );
+
+ if (listener != null)
+ listener.progress( current, max, format, args );
+ }
+
+ @Override
+ public Boolean call()
+ throws Exception {
return forEach( "mpw", new NNFunctionNN() {
@Nonnull
@Override
@@ -79,4 +168,10 @@ public class MPTestSuite {
super( cause );
}
}
+
+
+ public interface Listener {
+
+ void progress(int current, int max, String messageFormat, Object... args);
+ }
}
diff --git a/MasterPassword/Java/masterpassword-tests/src/main/java/com/lyndir/masterpassword/MPTests.java b/MasterPassword/Java/masterpassword-tests/src/main/java/com/lyndir/masterpassword/MPTests.java
index 151d49f7..dcc1091d 100644
--- a/MasterPassword/Java/masterpassword-tests/src/main/java/com/lyndir/masterpassword/MPTests.java
+++ b/MasterPassword/Java/masterpassword-tests/src/main/java/com/lyndir/masterpassword/MPTests.java
@@ -3,18 +3,17 @@ package com.lyndir.masterpassword;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.*;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import javax.xml.bind.annotation.*;
/**
* @author lhunath, 14-12-05
*/
-@XmlRootElement(name = "tests")
public class MPTests {
private static final String ID_DEFAULT = "default";
@@ -22,8 +21,7 @@ public class MPTests {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPTests.class );
- @XmlElement(name = "case")
- private List cases;
+ List cases;
@Nonnull
public List getCases() {
@@ -47,33 +45,20 @@ public class MPTests {
}
}
- @XmlRootElement(name = "case")
public static class Case {
- @XmlAttribute(name = "id")
- private String identifier;
- @XmlAttribute
- private String parent;
- @XmlElement
- private String algorithm;
- @XmlElement
- private String fullName;
- @XmlElement
- private String masterPassword;
- @XmlElement
- private String keyID;
- @XmlElement
- private String siteName;
- @XmlElement
- private Integer siteCounter;
- @XmlElement
- private String siteType;
- @XmlElement
- private String siteVariant;
- @XmlElement
- private String siteContext;
- @XmlElement
- private String result;
+ String identifier;
+ String parent;
+ Integer algorithm;
+ String fullName;
+ String masterPassword;
+ String keyID;
+ String siteName;
+ UnsignedInteger siteCounter;
+ String siteType;
+ String siteVariant;
+ String siteContext;
+ String result;
private transient Case parentCase;
@@ -84,10 +69,10 @@ public class MPTests {
parentCase.initializeParentHierarchy( tests );
}
- algorithm = ifNotNullElse( algorithm, new NNSupplier() {
+ algorithm = ifNotNullElse( algorithm, new NNSupplier() {
@Nonnull
@Override
- public String get() {
+ public Integer get() {
return checkNotNull( parentCase.algorithm );
}
} );
@@ -119,10 +104,10 @@ public class MPTests {
return checkNotNull( parentCase.siteName );
}
} );
- siteCounter = ifNotNullElse( siteCounter, new NNSupplier() {
+ siteCounter = ifNotNullElse( siteCounter, new NNSupplier() {
@Nonnull
@Override
- public Integer get() {
+ public UnsignedInteger get() {
return checkNotNull( parentCase.siteCounter );
}
} );
@@ -168,7 +153,7 @@ public class MPTests {
@Nonnull
public MasterKey.Version getAlgorithm() {
- return MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( algorithm ) );
+ return MasterKey.Version.fromInt( checkNotNull( algorithm ) );
}
@Nonnull
@@ -191,8 +176,8 @@ public class MPTests {
return checkNotNull( siteName );
}
- public int getSiteCounter() {
- return ifNotNullElse( siteCounter, 1 );
+ public UnsignedInteger getSiteCounter() {
+ return ifNotNullElse( siteCounter, UnsignedInteger.valueOf( 1 ) );
}
@Nonnull
diff --git a/MasterPassword/Java/masterpassword-tests/src/main/resources/mpw_tests.xml b/MasterPassword/Java/masterpassword-tests/src/main/resources/mpw_tests.xml
index 86d24fc7..34bb460e 100644
--- a/MasterPassword/Java/masterpassword-tests/src/main/resources/mpw_tests.xml
+++ b/MasterPassword/Java/masterpassword-tests/src/main/resources/mpw_tests.xml
@@ -1,7 +1,7 @@
-
+ -1
Robert Lee Mitchell
banana colored duckling
98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302