extends BaseObservable {
+ T mValue;
+
+ /**
+ * Creates an empty observable object
+ */
+ public BindableObject() {
+ }
+
+ /**
+ * Wraps the given object and creates an observable object
+ *
+ * @param value The value to be wrapped as an observable.
+ */
+ public BindableObject(T value) {
+ mValue = value;
+ }
+
+ /**
+ * Set the stored value.
+ */
+ public void set(T value) {
+ if (!equals(mValue, value)) {
+ mValue = value;
+ notifyChange();
+ }
+ }
+
+ public T get() {
+ return mValue;
+ }
+
+ /**
+ * Compare two objects of the same type. Supports null comparison as well as {@linkplain
+ * Object#equals(Object) Object.equals()} method.
+ *
+ * Override this method if you need additional comparison logic, for example custom string
+ * comparison.
+ *
+ * @param o1
+ * @param o2
+ * @return true if objects are equal, false otherwise
+ */
+ public boolean equals(T o1, T o2) {
+ return (o1 == o2) || (o1 != null && o1.equals(o2));
+ }
+
+ /**
+ * An action which stores a new value for this object.
+ */
+ public Action1 super T> asAction() {
+ return new Action1() {
+ @Override public void call(T value) {
+ set(value);
+ }
+ };
+ }
+ }
+
+ public interface Observable2 {
+
+ /**
+ * Adds a callback to listen for changes to the Observable.
+ *
+ * @param callback The callback to start listening.
+ */
+ void addOnPropertyChangedCallback(OnPropertyChangedCallback callback);
+
+ /**
+ * Removes a callback from those listening for changes.
+ *
+ * @param callback The callback that should stop listening.
+ */
+ void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback);
+
+ /**
+ * The callback that is called by Observable when an observable property has changed.
+ */
+ abstract class OnPropertyChangedCallback {
+
+ /**
+ * Called by an Observable whenever an observable property changes.
+ *
+ * @param sender The Observable that is changing.
+ * @param propertyId The BR identifier of the property that has changed. The getter
+ * for this property should be annotated with {Bindable}.
+ */
+ public abstract void onPropertyChanged(Observable2 sender, int propertyId);
+ }
+ }
+
+ public class BaseObservable implements Observable2 {
+ private transient PropertyChangeRegistry mCallbacks;
+
+ public BaseObservable() {
+ }
+
+ @Override
+ public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
+ if (mCallbacks == null) {
+ mCallbacks = new PropertyChangeRegistry();
+ }
+ mCallbacks.add(callback);
+ }
+
+ @Override
+ public synchronized void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
+ if (mCallbacks != null) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ /**
+ * Notifies listeners that all properties of this instance have changed.
+ */
+ public synchronized void notifyChange() {
+ if (mCallbacks != null) {
+ mCallbacks.notifyCallbacks(this, 0, null);
+ }
+ }
+
+ /**
+ * Notifies listeners that a specific property has changed. The getter for the property
+ * that changes should be marked with Bindable to generate a field in
+ * BR
to be used as fieldId
.
+ *
+ * @param fieldId The generated BR id for the Bindable field.
+ */
+ public void notifyPropertyChanged(int fieldId) {
+ if (mCallbacks != null) {
+ mCallbacks.notifyCallbacks(this, fieldId, null);
+ }
+ }
+ }
+
+ public static class PropertyChangeRegistry extends
+ CallbackRegistry {
+
+ private static final CallbackRegistry.NotifierCallback NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback() {
+ @Override
+ public void onNotifyCallback(Observable2.OnPropertyChangedCallback callback, Observable2 sender,
+ int arg, Void notUsed) {
+ callback.onPropertyChanged(sender, arg);
+ }
+ };
+
+ public PropertyChangeRegistry() {
+ super(NOTIFIER_CALLBACK);
+ }
+
+ /**
+ * Notifies registered callbacks that a specific property has changed.
+ *
+ * @param observable The Observable that has changed.
+ * @param propertyId The BR id of the property that has changed or BR._all if the entire
+ * Observable has changed.
+ */
+ public void notifyChange(Observable2 observable, int propertyId) {
+ notifyCallbacks(observable, propertyId, null);
+ }
+ }
+
+ public static class CallbackRegistry implements Cloneable {
+ private static final String TAG = "CallbackRegistry";
+
+ /**
+ * An ordered collection of listeners waiting to be notified.
+ */
+ private List mCallbacks = new ArrayList();
+
+ /**
+ * A bit flag for the first 64 listeners that are removed during notification.
+ * The lowest significant bit corresponds to the 0th index into mCallbacks.
+ * For a small number of callbacks, no additional array of objects needs to
+ * be allocated.
+ */
+ private long mFirst64Removed = 0x0;
+
+ /**
+ * Bit flags for the remaining callbacks that are removed during notification.
+ * When there are more than 64 callbacks and one is marked for removal, a dynamic
+ * array of bits are allocated for the callbacks.
+ */
+ private long[] mRemainderRemoved;
+
+ /**
+ * The recursion level of the notification
+ */
+ private int mNotificationLevel;
+
+ /**
+ * The notification mechanism for notifying an event.
+ */
+ private final NotifierCallback mNotifier;
+
+ /**
+ * Creates an EventRegistry that notifies the event with notifier.
+ *
+ * @param notifier The class to use to notify events.
+ */
+ public CallbackRegistry(NotifierCallback notifier) {
+ mNotifier = notifier;
+ }
+
+ /**
+ * Notify all callbacks.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ */
+ public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
+ mNotificationLevel++;
+ notifyRecurse(sender, arg, arg2);
+ mNotificationLevel--;
+ if (mNotificationLevel == 0) {
+ if (mRemainderRemoved != null) {
+ for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
+ final long removedBits = mRemainderRemoved[i];
+ if (removedBits != 0) {
+ removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
+ mRemainderRemoved[i] = 0;
+ }
+ }
+ }
+ if (mFirst64Removed != 0) {
+ removeRemovedCallbacks(0, mFirst64Removed);
+ mFirst64Removed = 0;
+ }
+ }
+ }
+
+ /**
+ * Notify up to the first Long.SIZE callbacks that don't have a bit set in removed
.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ */
+ private void notifyFirst64(T sender, int arg, A arg2) {
+ final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
+ notifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
+ }
+
+ /**
+ * Notify all callbacks using a recursive algorithm to avoid allocating on the heap.
+ * This part captures the callbacks beyond Long.SIZE that have no bits allocated for
+ * removal before it recurses into {@link #notifyRemainder(Object, int, A, int)}.
+ *
+ *
Recursion is used to avoid allocating temporary state on the heap.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ */
+ private void notifyRecurse(T sender, int arg, A arg2) {
+ final int callbackCount = mCallbacks.size();
+ final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;
+
+ // Now we've got all callbakcs that have no mRemainderRemoved value, so notify the
+ // others.
+ notifyRemainder(sender, arg, arg2, remainderIndex);
+
+ // notifyRemainder notifies all at maxIndex, so we'd normally start at maxIndex + 1
+ // However, we must also keep track of those in mFirst64Removed, so we add 2 instead:
+ final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;
+
+ // The remaining have no bit set
+ notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
+ }
+
+ /**
+ * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If
+ * remainderIndex is -1, the first 64 will be notified instead.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param remainderIndex The index into mRemainderRemoved that should be notified.
+ */
+ private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) {
+ if (remainderIndex < 0) {
+ notifyFirst64(sender, arg, arg2);
+ } else {
+ final long bits = mRemainderRemoved[remainderIndex];
+ final int startIndex = (remainderIndex + 1) * Long.SIZE;
+ final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE);
+ notifyRemainder(sender, arg, arg2, remainderIndex - 1);
+ notifyCallbacks(sender, arg, arg2, startIndex, endIndex, bits);
+ }
+ }
+
+ /**
+ * Notify callbacks from startIndex to endIndex, using bits as the bit status
+ * for whether they have been removed or not. bits should be from mRemainderRemoved or
+ * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to
+ * endIndex should be notified.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+ * @param startIndex The index into the mCallbacks to start notifying.
+ * @param endIndex One past the last index into mCallbacks to notify.
+ * @param bits A bit field indicating which callbacks have been removed and shouldn't
+ * be notified.
+ */
+ private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
+ final int endIndex, final long bits) {
+ long bitMask = 1;
+ for (int i = startIndex; i < endIndex; i++) {
+ if ((bits & bitMask) == 0) {
+ mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
+ }
+ bitMask <<= 1;
+ }
+ }
+
+ /**
+ * Add a callback to be notified. If the callback is already in the list, another won't
+ * be added. This does not affect current notifications.
+ *
+ * @param callback The callback to add.
+ */
+ public synchronized void add(C callback) {
+ int index = mCallbacks.lastIndexOf(callback);
+ if (index < 0 || isRemoved(index)) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Returns true if the callback at index has been marked for removal.
+ *
+ * @param index The index into mCallbacks to check.
+ * @return true if the callback at index has been marked for removal.
+ */
+ private boolean isRemoved(int index) {
+ if (index < Long.SIZE) {
+ // It is in the first 64 callbacks, just check the bit.
+ final long bitMask = 1L << index;
+ return (mFirst64Removed & bitMask) != 0;
+ } else if (mRemainderRemoved == null) {
+ // It is after the first 64 callbacks, but nothing else was marked for removal.
+ return false;
+ } else {
+ final int maskIndex = (index / Long.SIZE) - 1;
+ if (maskIndex >= mRemainderRemoved.length) {
+ // There are some items in mRemainderRemoved, but nothing at the given index.
+ return false;
+ } else {
+ // There is something marked for removal, so we have to check the bit.
+ final long bits = mRemainderRemoved[maskIndex];
+ final long bitMask = 1L << (index % Long.SIZE);
+ return (bits & bitMask) != 0;
+ }
+ }
+ }
+
+ /**
+ * Removes callbacks from startIndex to startIndex + Long.SIZE, based
+ * on the bits set in removed.
+ *
+ * @param startIndex The index into the mCallbacks to start removing callbacks.
+ * @param removed The bits indicating removal, where each bit is set for one callback
+ * to be removed.
+ */
+ private void removeRemovedCallbacks(int startIndex, long removed) {
+ // The naive approach should be fine. There may be a better bit-twiddling approach.
+ final int endIndex = startIndex + Long.SIZE;
+
+ long bitMask = 1L << (Long.SIZE - 1);
+ for (int i = endIndex - 1; i >= startIndex; i--) {
+ if ((removed & bitMask) != 0) {
+ mCallbacks.remove(i);
+ }
+ bitMask >>>= 1;
+ }
+ }
+
+ /**
+ * Remove a callback. This callback won't be notified after this call completes.
+ *
+ * @param callback The callback to remove.
+ */
+ public synchronized void remove(C callback) {
+ if (mNotificationLevel == 0) {
+ mCallbacks.remove(callback);
+ } else {
+ int index = mCallbacks.lastIndexOf(callback);
+ if (index >= 0) {
+ setRemovalBit(index);
+ }
+ }
+ }
+
+ private void setRemovalBit(int index) {
+ if (index < Long.SIZE) {
+ // It is in the first 64 callbacks, just check the bit.
+ final long bitMask = 1L << index;
+ mFirst64Removed |= bitMask;
+ } else {
+ final int remainderIndex = (index / Long.SIZE) - 1;
+ if (mRemainderRemoved == null) {
+ mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE];
+ } else if (mRemainderRemoved.length < remainderIndex) {
+ // need to make it bigger
+ long[] newRemainders = new long[mCallbacks.size() / Long.SIZE];
+ System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length);
+ mRemainderRemoved = newRemainders;
+ }
+ final long bitMask = 1L << (index % Long.SIZE);
+ mRemainderRemoved[remainderIndex] |= bitMask;
+ }
+ }
+
+ /**
+ * Makes a copy of the registered callbacks and returns it.
+ *
+ * @return a copy of the registered callbacks.
+ */
+ public synchronized ArrayList copyCallbacks() {
+ ArrayList callbacks = new ArrayList(mCallbacks.size());
+ int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemoved(i)) {
+ callbacks.add(mCallbacks.get(i));
+ }
+ }
+ return callbacks;
+ }
+
+ /**
+ * Modifies callbacks
to contain all callbacks in the CallbackRegistry.
+ *
+ * @param callbacks modified to contain all callbacks registered to receive events.
+ */
+ public synchronized void copyCallbacks(List callbacks) {
+ callbacks.clear();
+ int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemoved(i)) {
+ callbacks.add(mCallbacks.get(i));
+ }
+ }
+ }
+
+ /**
+ * Returns true if there are no registered callbacks or false otherwise.
+ *
+ * @return true if there are no registered callbacks or false otherwise.
+ */
+ public synchronized boolean isEmpty() {
+ if (mCallbacks.isEmpty()) {
+ return true;
+ } else if (mNotificationLevel == 0) {
+ return false;
+ } else {
+ int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemoved(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Removes all callbacks from the list.
+ */
+ public synchronized void clear() {
+ if (mNotificationLevel == 0) {
+ mCallbacks.clear();
+ } else if (!mCallbacks.isEmpty()) {
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ setRemovalBit(i);
+ }
+ }
+ }
+
+ /**
+ * @return A copy of the CallbackRegistry with all callbacks listening to both instances.
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized CallbackRegistry clone() {
+ CallbackRegistry clone = null;
+ try {
+ clone = (CallbackRegistry) super.clone();
+ clone.mFirst64Removed = 0;
+ clone.mRemainderRemoved = null;
+ clone.mNotificationLevel = 0;
+ clone.mCallbacks = new ArrayList();
+ final int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemoved(i)) {
+ clone.mCallbacks.add(mCallbacks.get(i));
+ }
+ }
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ return clone;
+ }
+
+ /**
+ * Class used to notify events from CallbackRegistry.
+ *
+ * @param The callback type.
+ * @param The notification sender type. Typically this is the containing class.
+ * @param An opaque argument to pass to the notifier
+ */
+ public abstract static class NotifierCallback {
+ /**
+ * Called by CallbackRegistry during
+ * {@link CallbackRegistry#notifyCallbacks(Object, int, Object)}} to notify the callback.
+ *
+ * @param callback The callback to notify.
+ * @param sender The opaque sender object.
+ * @param arg The opaque notification parameter.
+ * @param arg2 An opaque argument passed in
+ * {@link CallbackRegistry#notifyCallbacks}
+ * @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback)
+ */
+ public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/resources/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java b/src/resources/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java
new file mode 100644
index 0000000..e542ee4
--- /dev/null
+++ b/src/resources/DuplicateAssert/src/test/java/org/openbmap/utils/XmlSanitizerTest.java
@@ -0,0 +1,41 @@
+package org.openbmap.utils;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class XmlSanitizerTest {
+
+ @Test
+ public void testXmlSanitizer() {
+ boolean valid = XmlSanitizer.isValid("Fritzbox");
+ assertEquals("Fritzbox is valid", true, valid);
+ System.out.println("Pure ASCII test - passed");
+
+ valid = XmlSanitizer.isValid("Fritz Box");
+ assertEquals("Spaces are valid", true, valid);
+ System.out.println("Spaces test - passed");
+
+ valid = XmlSanitizer.isValid("Frützbüx");
+ assertEquals("Frützbüx is invalid", false, valid);
+ System.out.println("No ASCII test - passed");
+
+ valid = XmlSanitizer.isValid("Fritz!box");
+ assertEquals("Exclamation mark is valid", true, valid);
+ System.out.println("Exclamation mark test - passed");
+
+ valid = XmlSanitizer.isValid("Fritz.box");
+ assertEquals("Exclamation mark is valid", true, valid);
+ System.out.println("Dot test - passed");
+
+ valid = XmlSanitizer.isValid("Fritz-box");
+ assertEquals("Minus is valid", true, valid);
+ System.out.println("Minus test - passed");
+
+ valid = XmlSanitizer.isValid("Fritz-box");
+ assertEquals("Minus is valid", true, valid);
+ System.out.println("Minus test - passed");
+ }
+
+}
+
diff --git a/src/resources/EagerTest/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentence.java b/src/resources/EagerTest/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentence.java
new file mode 100644
index 0000000..f2f237f
--- /dev/null
+++ b/src/resources/EagerTest/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentence.java
@@ -0,0 +1,89 @@
+package com.mendhak.gpslogger.loggers.nmea;
+
+
+import com.mendhak.gpslogger.common.Strings;
+
+public class NmeaSentence {
+
+ String[] nmeaParts;
+
+ public NmeaSentence(String nmeaSentence){
+ if(Strings.isNullOrEmpty(nmeaSentence)){
+ nmeaParts = new String[]{""};
+ return;
+ }
+ nmeaParts = nmeaSentence.split(",");
+
+ }
+
+ public boolean isLocationSentence(){
+ return nmeaParts[0].equalsIgnoreCase("$GPGSA") || nmeaParts[0].equalsIgnoreCase("$GPGGA");
+ }
+
+ public String getLatestPdop(){
+ if (nmeaParts[0].equalsIgnoreCase("$GPGSA")) {
+
+ if (nmeaParts.length > 15 && !Strings.isNullOrEmpty(nmeaParts[15])) {
+ return nmeaParts[15];
+ }
+ }
+
+ return null;
+ }
+
+ public String getLatestVdop(){
+ if (nmeaParts[0].equalsIgnoreCase("$GPGSA")) {
+ if (nmeaParts.length > 17 &&!Strings.isNullOrEmpty(nmeaParts[17]) && !nmeaParts[17].startsWith("*")) {
+ return nmeaParts[17].split("\\*")[0];
+ }
+ }
+
+ return null;
+ }
+
+ public String getLatestHdop(){
+ if (nmeaParts[0].equalsIgnoreCase("$GPGGA")) {
+ if (nmeaParts.length > 8 &&!Strings.isNullOrEmpty(nmeaParts[8])) {
+ return nmeaParts[8];
+ }
+ }
+ else if (nmeaParts[0].equalsIgnoreCase("$GPGSA")) {
+ if (nmeaParts.length > 16 &&!Strings.isNullOrEmpty(nmeaParts[16])) {
+ return nmeaParts[16];
+ }
+ }
+
+ return null;
+ }
+
+ public String getGeoIdHeight(){
+ if (nmeaParts[0].equalsIgnoreCase("$GPGGA")) {
+ if (nmeaParts.length > 11 &&!Strings.isNullOrEmpty(nmeaParts[11])) {
+ return nmeaParts[11];
+ }
+ }
+
+ return null;
+ }
+
+ public String getAgeOfDgpsData(){
+ if (nmeaParts[0].equalsIgnoreCase("$GPGGA")) {
+ if (nmeaParts.length > 13 && !Strings.isNullOrEmpty(nmeaParts[13])) {
+ return nmeaParts[13];
+ }
+ }
+
+ return null;
+ }
+
+ public String getDgpsId(){
+ if (nmeaParts[0].equalsIgnoreCase("$GPGGA")) {
+ if (nmeaParts.length > 14 &&!Strings.isNullOrEmpty(nmeaParts[14]) && !nmeaParts[14].startsWith("*")) {
+ return nmeaParts[14].split("\\*")[0];
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/resources/EagerTest/src/test/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentenceTest.java b/src/resources/EagerTest/src/test/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentenceTest.java
new file mode 100644
index 0000000..4b24140
--- /dev/null
+++ b/src/resources/EagerTest/src/test/java/com/mendhak/gpslogger/loggers/nmea/NmeaSentenceTest.java
@@ -0,0 +1,85 @@
+package com.mendhak.gpslogger.loggers.nmea;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+
+@SmallTest
+@RunWith(MockitoJUnitRunner.class)
+public class NmeaSentenceTest {
+
+ @Test
+ public void NmeaSentence_EmptyNmeaSentence_VDOPIsNull(){
+ NmeaSentence nmeaSentence = new NmeaSentence("blahasdfasdf");
+ assertThat("VDOP null by default", nmeaSentence.getLatestVdop(), nullValue());
+ }
+
+ @Test
+ public void NmeaSentence_EmptyNmeaSentence_HDOPIsNull(){
+
+ NmeaSentence nmeaSentence = new NmeaSentence("$GPGGA,,,,,,,,,,,,,,*47");
+ assertThat("HDOP null by default", nmeaSentence.getLatestHdop(), nullValue());
+ }
+
+ @Test
+ public void NmeaSentence_EmptyNmeaSentence_DGPSIDIsNull(){
+
+ NmeaSentence nmeaSentence = new NmeaSentence("$GPGGA,,");
+ assertThat("DGPSID null by default", nmeaSentence.getDgpsId(), nullValue());
+
+ nmeaSentence = new NmeaSentence("");
+ assertThat("DGPSID null by default", nmeaSentence.getDgpsId(), nullValue());
+ }
+
+ @Test
+ public void NmeaSentence_GPGGA_ReadValidValues(){
+ NmeaSentence nmeaSentence = new NmeaSentence("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,27*47");
+ assertThat("GPGGA - read HDOP", nmeaSentence.getLatestHdop(), is("0.9"));
+ assertThat("GPGGA - read GeoIdHeight", nmeaSentence.getGeoIdHeight(), is("46.9"));
+ assertThat("GPGGA - read Last dgps update", nmeaSentence.getAgeOfDgpsData(), nullValue());
+ assertThat("GPGGA - read dgps station id", nmeaSentence.getDgpsId(), is("27"));
+ }
+
+ @Test
+ public void NmeaSentence_GPGSA_ReadValidValues(){
+
+ NmeaSentence nmeaSentence = new NmeaSentence("$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39");
+ assertThat("GPGSA - read PDOP", nmeaSentence.getLatestPdop(), is("2.5"));
+ assertThat("GPGSA - read HDOP", nmeaSentence.getLatestHdop(), is("1.3"));
+ assertThat("GPGSA - read VDOP", nmeaSentence.getLatestVdop(), is("2.1"));
+ }
+
+ @Test
+ public void NmeaSentence_Incomplete_ReadSomeValues(){
+
+ NmeaSentence nmeaSentence = new NmeaSentence("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545");
+ assertThat("GPGGA - Incomplete, read HDOP", nmeaSentence.getLatestHdop(), is("0.9"));
+ assertThat("GPGGA - Incomplete, no GeoIdHeight", nmeaSentence.getGeoIdHeight(), nullValue());
+ }
+
+ @Test
+ public void NmeaSentence_Null_NoValuesRead(){
+
+ NmeaSentence nmeaSentence = new NmeaSentence(null);
+ assertThat("Null NMEA string", nmeaSentence.getLatestHdop(), nullValue());
+ }
+
+ @Test
+ public void NmeaSentence_CheckForRelevantSentence(){
+
+ NmeaSentence nmeaSentence = new NmeaSentence(null);
+ assertThat("Null NMEA is not a valid location", nmeaSentence.isLocationSentence(), is(false));
+
+ nmeaSentence = new NmeaSentence("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545");
+ assertThat("GPGGA is a valid location", nmeaSentence.isLocationSentence(), is(true));
+
+ nmeaSentence = new NmeaSentence("$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39");
+ assertThat("GPGSA is a valid location", nmeaSentence.isLocationSentence(), is(true));
+ }
+}
\ No newline at end of file
diff --git a/src/resources/EmptyTest/src/androidTest/java/com/actisec/clipcaster/parser/LastPassParserTest.java b/src/resources/EmptyTest/src/androidTest/java/com/actisec/clipcaster/parser/LastPassParserTest.java
new file mode 100644
index 0000000..f2a77fb
--- /dev/null
+++ b/src/resources/EmptyTest/src/androidTest/java/com/actisec/clipcaster/parser/LastPassParserTest.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2014 Xiao Bao Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those
+ * of the authors and should not be interpreted as representing official policies,
+ * either expressed or implied, of the FreeBSD Project.
+ */
+
+package com.actisec.clipcaster.parser;
+
+import android.util.Log;
+
+import com.actisec.clipcaster.AbstractJavaScriptTestCase;
+import com.actisec.clipcaster.ScrapedCredentials;
+import com.actisec.clipcaster.ScrapedData;
+import com.actisec.clipcaster.ScrapedDataHandler;
+import com.actisec.clipcaster.Source;
+import com.actisec.clipcaster.util.JavaScript;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.codehaus.jackson.map.ObjectMapper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class LastPassParserTest extends AbstractJavaScriptTestCase {
+
+ private static final String FULL_SAMPLE_v1 = "script:(function(){var l_fs, l_bf=null, l_err=false;var l_bni=0, l_bnp=0;var l_bte=null, l_bpe=null, l_cpe=null;var l_w; var l_d; try { l_w=window.top; l_d=l_w.document;} catch (l_e){ l_w=window; l_d=document;}var l_iv=function(el, sf){ while (el&&(!sf||el.tagName != 'FORM')){ if (el.hasOwnProperty('style')&&(el.style['display']=='none'||el.style['visibility']=='hidden')) return false; else el=el.parentNode;} return true;};var l_cpp=/(?:existing|old|curr).*pass/i;for(var l_k=-1; l_k < l_w.frames.length; l_k++){ if(l_k==-1){ l_fs=l_d.getElementsByTagName('form');}else{ try{ l_w[l_k].document.domain } catch(e){console.log(e); l_err=true; continue;} l_fs=l_w[l_k].document.getElementsByTagName('form');} for (var l_i=0; l_i < l_fs.length; l_i++){ if (!l_iv(l_fs[l_i])) continue; var l_fe=l_fs[l_i].elements; var l_ni=0, l_np=0; var l_te=null, l_pe=null; for (var l_j=0; l_j < l_fe.length; l_j++){ var l_e=l_fe[l_j]; if ((l_e.type=='text'||l_e.type=='email'||l_e.type=='tel')&&l_iv(l_e, true)){ if (l_ni==0){ l_te=l_e;} l_ni++;} if (l_e.type=='password'&&l_iv(l_e, true)){ if (l_np==0){ l_pe=l_e;} l_np++; if (l_cpp.test(l_e.name)||l_cpp.test(l_e.id)){ l_cpe=l_e;} } } if (l_np==1){ if (!l_bf||(l_ni==1&&l_bni != 1)){ l_bni=l_ni; l_bnp=l_np; l_bf=l_fs[l_i]; l_bte=l_te; l_bpe=l_pe;} } else if (l_np > 1&&l_cpe){ l_bf=l_fs[l_i]; l_bpe=l_cpe;} }}var l_sfv=function(el, v){ try { var c=true; if (el.type=='select-one'&&el.value==v){ c=false;} el.value=v; if (c){ var evt=el.ownerDocument.createEvent('Events'); evt.initEvent('change', true, true); el.dispatchEvent(evt); evt=el.ownerDocument.createEvent('Events'); evt.initEvent('input', true, true); el.dispatchEvent(evt);} } catch(e){}};if (l_bf){ var do_fill=true; if (do_fill){ console.log('fill login form=' + (l_bf.id||l_bf.name)); if (l_bte){ l_sfv(l_bte, decodeURIComponent(escape(atob('dXNlckBleGFtcGxlLmNvbQ=='))));} l_sfv(l_bpe, decodeURIComponent(escape(atob('cDRzc3cwcmQ='))));}} else { console.log('no form');}})();////////////////////////////////////////////////////////////////////////////////////////////////////";
+
+ private LastPassParser mParser = new LastPassParser();
+
+ private String formatUserPassToJscript(String encodedUser, String encodedPass) {
+ return "(atob('" + encodedUser + "'))))." + "(atob('" + encodedPass + "'))))";
+ }
+
+ private class DummyCallback implements ScrapedDataHandler{
+
+ final List data = new ArrayList();
+ @Override
+ public void handleData(ScrapedData scrapedData) {
+ synchronized (data) {
+ data.add(scrapedData);
+ data.notifyAll();
+ }
+ }
+ }
+
+ public void testCredTmp() throws Throwable{
+ DummyCallback handler = new DummyCallback();
+ Source source = mTestUtils.readSource(com.actisec.clipcaster.test.R.raw.lastpass_v3);
+ JavaScript javaScript = new JavaScript(source.javascriptProgram);
+ final long injectedTime = source.timeOfNotification;
+
+ LastPassParser.Parser parser = new LastPassParser.Parser(getContext(),handler,injectedTime);
+ parser.getData(source.javascriptProgram);
+
+ synchronized (handler.data) {
+ while (handler.data.isEmpty()) {
+ handler.data.wait(2000);
+ }
+ ScrapedData data = handler.data.remove(0);
+ assertNotNull(data);
+ final ScrapedCredentials creds = data.creds;
+ assertNotNull(creds);
+
+ assertTrue(creds.user != null || creds.pass != null);
+ if(creds.user != null){
+ assertEquals("user@example.com",creds.user);
+ } else if (creds.pass != null){
+ assertEquals("p4ssw0rd", creds.pass);
+ }
+ }
+ }
+
+
+
+// private ScrapedCredentials innerCredTest(String content) throws Throwable{
+// final ScrapedData data = mParser.getScrapedData(mActivity, content);
+// assertNotNull(data);
+// final ScrapedCredentials creds = data.creds;
+// assertNotNull(creds);
+// assertNotNull(creds.user);
+// assertNotNull(creds.pass);
+// assertFalse(creds.user.isEmpty());
+// assertFalse(creds.pass.isEmpty());
+// return creds;
+// }
+
+
+ public void testCredGetJustBase64() throws Throwable{
+// innerCredTest(formatUserPassToJscript("czRmM3A0c3N3MHJk", "dW9hZmlyZWRyb2lk"));
+ }
+
+ public void testCredGetJustBase64WithEquals() throws Throwable{
+// innerCredTest(formatUserPassToJscript("bGVhc3VyZS4=", "c3VyZS4="));
+ }
+
+ public void testCredGetFullSampleV1() throws Throwable{
+// ScrapedCredentials credentials = innerCredTest(FULL_SAMPLE_v1);
+// assertEquals("p4ssw0rd", credentials.pass);
+// assertEquals("user@example.com",credentials.user);
+
+ }
+
+ public void testTestReadingTimeFromFile() throws Throwable{
+ Source source = mTestUtils.readSource(com.actisec.clipcaster.test.R.raw.lastpass_v3);
+ assertNotNull(source);
+ assertEquals(1418693814750L,source.timeOfNotification);
+ }
+
+ public void testGetCredsFromJs() throws Throwable {
+ Source source = mTestUtils.readSource(com.actisec.clipcaster.test.R.raw.lastpass_v3);
+// ScrapedCredentials credentials = LastPassParser.getCredsFromJs(mActivity, source.javascriptProgram, source.timeOfNotification);
+// assertNotNull(credentials);
+// assertEquals("user@example.com", credentials.user);
+// assertEquals("p4ssw0rd", credentials.pass);
+
+ }
+
+ public void testTestEscapedChars() throws Throwable {
+ String source = mTestUtils.readString(com.actisec.clipcaster.test.R.raw.escapedprotocol);
+ assertTrue(StringEscapeUtils.escapeJava(source), source.contains("/https?:\\/\\//"));
+ }
+ public void testSourceEscapedChars() throws Throwable {
+ String raw = mTestUtils.readString(com.actisec.clipcaster.test.R.raw.lastpass_v3);
+ Source source = new ObjectMapper().readValue(raw,Source.class);
+
+ assertTrue(StringEscapeUtils.escapeJava(source.javascriptProgram),source.javascriptProgram.contains("/https?:\\/\\//"));
+ }
+
+ public void testGetFunction() throws Throwable {
+ Source source = mTestUtils.readSource(com.actisec.clipcaster.test.R.raw.lastpass_v3);
+ JavaScript javaScript = new JavaScript(source.javascriptProgram);
+ String lxfunc = javaScript.getFunction("l_x");
+ assertNotNull(lxfunc);
+ assertEquals("l_x=function(t,l,m){ var o=[]; var b=''; var p=document.location.href.replace(/https?:\\/\\//, '').substring(0,l); p=l_s(''+l_f(m)+p); for (z=1; z<=255; z++){o[String.fromCharCode(z)]=z;} for (j=z=0; z
+ * Test values are from NK document, ex 1.1.5.2.
+ */
+ @Test
+ public void withoutDistanceInMeasures() {
+ Point p1136 = new Point("1136", 649.0, 780.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1137 = new Point("1137", 615.0, 740.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1138 = new Point("1138", 615.0, 810.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1139 = new Point("1139", 687.0, 804.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1140 = new Point("1140", 676.0, 743.0, MathUtils.IGNORE_DOUBLE, true);
+
+ Abriss a = new Abriss(p1136, false);
+ a.removeDAO(CalculationsDataSource.getInstance());
+ a.getMeasures().add(new Measure(p1137, 0.0, MathUtils.IGNORE_DOUBLE, MathUtils.IGNORE_DOUBLE));
+ a.getMeasures().add(new Measure(p1138, 101.218, MathUtils.IGNORE_DOUBLE, MathUtils.IGNORE_DOUBLE));
+ a.getMeasures().add(new Measure(p1139, 219.3067, MathUtils.IGNORE_DOUBLE, MathUtils.IGNORE_DOUBLE));
+ a.getMeasures().add(new Measure(p1140, 315.113, MathUtils.IGNORE_DOUBLE, MathUtils.IGNORE_DOUBLE));
+
+ try {
+ a.compute();
+ } catch (CalculationException e) {
+ Assert.fail(e.getMessage());
+ }
+
+ Assert.assertEquals("386", this.df0.format(a.getResults().get(0).getErrAngle()));
+ Assert.assertEquals("3.2", this.df1.format(a.getResults().get(0).getErrTrans()));
+ Assert.assertTrue(MathUtils.isIgnorable(a.getResults().get(0).getErrLong()));
+
+ Assert.assertEquals("-26", this.df0.format(a.getResults().get(1).getErrAngle()));
+ Assert.assertEquals("-0.2", this.df1.format(a.getResults().get(1).getErrTrans()));
+ Assert.assertTrue(MathUtils.isIgnorable(a.getResults().get(1).getErrLong()));
+
+ Assert.assertEquals("206", this.df0.format(a.getResults().get(2).getErrAngle()));
+ Assert.assertEquals("1.5", this.df1.format(a.getResults().get(2).getErrTrans()));
+ Assert.assertTrue(MathUtils.isIgnorable(a.getResults().get(2).getErrLong()));
+
+ Assert.assertEquals("-565", this.df0.format(a.getResults().get(3).getErrAngle()));
+ Assert.assertEquals("-4.1", this.df1.format(a.getResults().get(3).getErrTrans()));
+ Assert.assertTrue(MathUtils.isIgnorable(a.getResults().get(3).getErrLong()));
+
+ // test final results
+ Assert.assertEquals("244.8109", this.df4.format(a.getMean()));
+ Assert.assertEquals("413", this.df0.format(a.getMSE()));
+ Assert.assertEquals("206", this.df0.format(a.getMeanErrComp()));
+ }
+
+ // This is a regression test for bug #830
+ // The calculation must change the zenithal angle of measures to 100.0 if not provided.
+ @Test
+ public void zenithalAngleNotProvided() {
+ Point p9000 = new Point("9000", 529117.518, 182651.404, 925.059, true);
+ Point p9001 = new Point("9001", 529137.864, 182649.391, 919.810, true);
+ Point p9002 = new Point("9002", 529112.403, 182631.705, 924.720, true);
+ Point p9003 = new Point("9003", 529139.385, 182648.989, MathUtils.IGNORE_DOUBLE, true);
+ Point p9004 = new Point("9004", 529112.544, 182632.033, MathUtils.IGNORE_DOUBLE, true);
+
+ Abriss a = new Abriss(p9000, false);
+ a.removeDAO(CalculationsDataSource.getInstance());
+ a.getMeasures().add(new Measure(p9001, 106.3770, 112.4151, 20.890));
+ a.getMeasures().add(new Measure(p9002, 216.0699, 97.2887, 20.360));
+
+ try {
+ a.compute();
+ } catch (CalculationException e) {
+ Assert.fail(e.getMessage());
+ }
+
+ // test intermediate values for point 9001
+ Assert.assertEquals("399.9012",
+ this.df4.format(a.getResults().get(0).getUnknownOrientation()));
+ Assert.assertEquals("106.3792",
+ this.df4.format(a.getResults().get(0).getOrientedDirection()));
+ Assert.assertEquals("-1010.4",
+ this.df1.format(a.getResults().get(0).getErrAngle()));
+ Assert.assertEquals("-3.2",
+ this.df1.format(a.getResults().get(0).getErrTrans()));
+
+ // test intermediate values for point 9002
+ Assert.assertEquals("0.1033",
+ this.df4.format(a.getResults().get(1).getUnknownOrientation()));
+ Assert.assertEquals("216.0721",
+ this.df4.format(a.getResults().get(1).getOrientedDirection()));
+ Assert.assertEquals("1010.4",
+ this.df1.format(a.getResults().get(1).getErrAngle()));
+ Assert.assertEquals("3.2",
+ this.df1.format(a.getResults().get(1).getErrTrans()));
+
+ // test final results
+ Assert.assertEquals("0.0022", this.df4.format(a.getMean()));
+
+ // test with more measures
+ a = new Abriss(p9000, false);
+ a.removeDAO(CalculationsDataSource.getInstance());
+ a.getMeasures().add(new Measure(p9002, 216.0600, 97.2887, 20.360));
+ a.getMeasures().add(new Measure(p9003, 107.000, MathUtils.IGNORE_DOUBLE, 22.00));
+ a.getMeasures().add(new Measure(p9001, 106.3770, 112.4151, 20.890));
+ a.getMeasures().add(new Measure(p9004, 215.700, MathUtils.IGNORE_DOUBLE, 20.00));
+ try {
+ a.compute();
+ } catch (CalculationException e) {
+ Assert.fail(e.getMessage());
+ }
+
+ Assert.assertEquals("0.0795", this.df4.format(a.getMean()));
+
+ // test with a different order
+ a = new Abriss(p9000, false);
+ a.removeDAO(CalculationsDataSource.getInstance());
+ a.getMeasures().add(new Measure(p9002, 216.0600, 97.2887, 20.360));
+ a.getMeasures().add(new Measure(p9003, 107.000, MathUtils.IGNORE_DOUBLE, 22.00));
+ a.getMeasures().add(new Measure(p9004, 215.700, MathUtils.IGNORE_DOUBLE, 20.00));
+ a.getMeasures().add(new Measure(p9001, 106.3770, 112.4151, 20.890));
+
+ try {
+ a.compute();
+ } catch (CalculationException e) {
+ Assert.fail(e.getMessage());
+ }
+
+ Assert.assertEquals("0.0795", this.df4.format(a.getMean()));
+
+ // another order
+ a = new Abriss(p9000, false);
+ a.removeDAO(CalculationsDataSource.getInstance());
+ a.getMeasures().add(new Measure(p9001, 106.3770, 112.4151, 20.890));
+ a.getMeasures().add(new Measure(p9002, 216.0600, 97.2887, 20.360));
+ a.getMeasures().add(new Measure(p9003, 107.000, MathUtils.IGNORE_DOUBLE, 22.00));
+ a.getMeasures().add(new Measure(p9004, 215.700, MathUtils.IGNORE_DOUBLE, 20.00));
+
+ try {
+ a.compute();
+ } catch (CalculationException e) {
+ Assert.fail(e.getMessage());
+ }
+
+ Assert.assertEquals("0.0795", this.df4.format(a.getMean()));
+ }
+
+ // TD-NK_1.1.1
+ @Test
+ public void tdNK111() {
+ Point p1101 = new Point("1101", 649.0, 780.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1102 = new Point("1102", 615.0, 740.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1103 = new Point("1103", 615.0, 810.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1104 = new Point("1104", 687.0, 804.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1105 = new Point("1105", 676.0, 743.0, MathUtils.IGNORE_DOUBLE, true);
+
+ Abriss a = new Abriss(p1101, false);
+ a.removeDAO(CalculationsDataSource.getInstance());
+ a.getMeasures().add(new Measure(p1102, 0.0, MathUtils.IGNORE_DOUBLE, MathUtils.IGNORE_DOUBLE));
+ a.getMeasures().add(new Measure(p1103, 101.1768, MathUtils.IGNORE_DOUBLE, MathUtils.IGNORE_DOUBLE));
+ a.getMeasures().add(new Measure(p1104, 219.2887, MathUtils.IGNORE_DOUBLE, MathUtils.IGNORE_DOUBLE));
+ a.getMeasures().add(new Measure(p1105, 315.0179, MathUtils.IGNORE_DOUBLE, MathUtils.IGNORE_DOUBLE));
+
+ try {
+ a.compute();
+ } catch (CalculationException e) {
+ Assert.fail(e.getMessage());
+ }
+
+ Assert.assertEquals("-0", this.df0.format(a.getResults().get(0).getErrAngle()));
+ Assert.assertEquals("-0.0", this.df1.format(a.getResults().get(0).getErrTrans()));
+ Assert.assertEquals("0.0", this.df1.format(a.getResults().get(0).getErrLong()));
+
+ Assert.assertEquals("0", this.df0.format(a.getResults().get(1).getErrAngle()));
+ Assert.assertEquals("0.0", this.df1.format(a.getResults().get(1).getErrTrans()));
+ Assert.assertEquals("0.0", this.df1.format(a.getResults().get(1).getErrLong()));
+
+ Assert.assertEquals("-0", this.df0.format(a.getResults().get(2).getErrAngle()));
+ Assert.assertEquals("-0.0", this.df1.format(a.getResults().get(2).getErrTrans()));
+ Assert.assertEquals("0.0", this.df1.format(a.getResults().get(2).getErrLong()));
+
+ Assert.assertEquals("0", this.df0.format(a.getResults().get(3).getErrAngle()));
+ Assert.assertEquals("0.0", this.df1.format(a.getResults().get(3).getErrTrans()));
+ Assert.assertEquals("0.0", this.df1.format(a.getResults().get(3).getErrLong()));
+
+ // test final results
+ Assert.assertEquals("244.8495", this.df4.format(a.getMean()));
+ Assert.assertEquals("0", this.df0.format(a.getMSE()));
+ Assert.assertEquals("0", this.df0.format(a.getMeanErrComp()));
+ }
+
+ // TD-NK_1.1.2
+ @Test
+ public void tdNK112() {
+ Point p1106 = new Point("1106", 649.0, 780.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1107 = new Point("1107", 615.0, 740.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1108 = new Point("1108", 615.0, 810.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1109 = new Point("1109", 687.0, 804.0, MathUtils.IGNORE_DOUBLE, true);
+ Point p1110 = new Point("1110", 676.0, 743.0, MathUtils.IGNORE_DOUBLE, true);
+
+ Abriss a = new Abriss(p1106, false);
+ a.removeDAO(CalculationsDataSource.getInstance());
+ a.getMeasures().add(new Measure(p1107, 0.0, MathUtils.IGNORE_DOUBLE, 52.618));
+ a.getMeasures().add(new Measure(p1108, 101.2180, MathUtils.IGNORE_DOUBLE, 45.353));
+ a.getMeasures().add(new Measure(p1109, 219.3067, MathUtils.IGNORE_DOUBLE, 44.984));
+ a.getMeasures().add(new Measure(p1110, 315.1130, MathUtils.IGNORE_DOUBLE, 45.924));
+
+ try {
+ a.compute();
+ } catch (CalculationException e) {
+ Assert.fail(e.getMessage());
+ }
+
+ Assert.assertEquals("386", this.df0.format(a.getResults().get(0).getErrAngle()));
+ Assert.assertEquals("3.2", this.df1.format(a.getResults().get(0).getErrTrans()));
+ Assert.assertEquals("-12.0", this.df1.format(a.getResults().get(0).getErrLong()));
+
+ Assert.assertEquals("-26", this.df0.format(a.getResults().get(1).getErrAngle()));
+ Assert.assertEquals("-0.2", this.df1.format(a.getResults().get(1).getErrTrans()));
+ Assert.assertEquals("-1.0", this.df1.format(a.getResults().get(1).getErrLong()));
+
+ Assert.assertEquals("206", this.df0.format(a.getResults().get(2).getErrAngle()));
+ Assert.assertEquals("1.5", this.df1.format(a.getResults().get(2).getErrTrans()));
+ Assert.assertEquals("-4.0", this.df1.format(a.getResults().get(2).getErrLong()));
+
+ Assert.assertEquals("-565", this.df0.format(a.getResults().get(3).getErrAngle()));
+ Assert.assertEquals("-4.1", this.df1.format(a.getResults().get(3).getErrTrans()));
+ Assert.assertEquals("-12.0", this.df1.format(a.getResults().get(3).getErrLong()));
+
+ // test final results
+ Assert.assertEquals("244.8109", this.df4.format(a.getMean()));
+ Assert.assertEquals("413", this.df0.format(a.getMSE()));
+ Assert.assertEquals("206", this.df0.format(a.getMeanErrComp()));
+ }
+}
\ No newline at end of file
diff --git a/src/resources/GeneralFixture/src/androidTest/java/at/bitfire/cadroid/test/CertificateInfoTest.java b/src/resources/GeneralFixture/src/androidTest/java/at/bitfire/cadroid/test/CertificateInfoTest.java
new file mode 100644
index 0000000..a2c4b90
--- /dev/null
+++ b/src/resources/GeneralFixture/src/androidTest/java/at/bitfire/cadroid/test/CertificateInfoTest.java
@@ -0,0 +1,69 @@
+package at.bitfire.cadroid.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import android.content.res.AssetManager;
+import android.test.InstrumentationTestCase;
+import at.bitfire.cadroid.CertificateInfo;
+import at.bitfire.cadroid.ConnectionInfo;
+import lombok.Cleanup;
+
+public class CertificateInfoTest extends InstrumentationTestCase {
+ private AssetManager assetManager;
+ private CertificateFactory certificateFactory;
+
+ CertificateInfo
+ infoDebianTestCA,
+ infoDebianTestNoCA,
+ infoGTECyberTrust,
+ infoMehlMX;
+
+ protected void setUp() throws Exception {
+ assetManager = getInstrumentation().getContext().getAssets();
+ certificateFactory = CertificateFactory.getInstance("X.509");
+
+ infoDebianTestCA = loadCertificateInfo("DebianTestCA.pem");
+ infoDebianTestNoCA = loadCertificateInfo("DebianTestNoCA.pem");
+ infoGTECyberTrust = loadCertificateInfo("GTECyberTrustGlobalRoot.pem");
+
+ // user-submitted test cases
+ infoMehlMX = loadCertificateInfo("mehl.mx.pem");
+ }
+
+ protected CertificateInfo loadCertificateInfo(String assetFileName) throws IOException, CertificateException {
+ @Cleanup InputStream is = assetManager.open(assetFileName);
+ X509Certificate rootGTECyberTrust = (X509Certificate)certificateFactory.generateCertificate(is);
+ return new CertificateInfo(rootGTECyberTrust);
+ }
+
+
+ public void testIsCA() {
+ assertTrue(infoDebianTestCA.isCA());
+ assertFalse(infoDebianTestNoCA.isCA());
+ assertNull(infoGTECyberTrust.isCA());
+
+ assertFalse(infoMehlMX.isCA());
+ }
+
+
+ public void testPreinstalledCertificate() throws Exception {
+ ConnectionInfo result = ConnectionInfo.fetch(new URL("https://sni.velox.ch/"));
+ assertEquals("sni.velox.ch", result.getHostName());
+ assertTrue(result.isHostNameMatching());
+ assertTrue(result.isTrusted());
+ }
+
+ public void testSelfSignedUntrustedCertificate() throws Exception {
+ ConnectionInfo result = ConnectionInfo.fetch(new URL("https://www.pcwebshop.co.uk/"));
+ assertEquals("www.pcwebshop.co.uk", result.getHostName());
+ assertFalse(result.isHostNameMatching());
+ assertFalse(result.isTrusted());
+ }
+
+
+}
diff --git a/src/resources/IgnoredTest/src/test/java/org/bitcoinj/core/PeerGroupTest.java b/src/resources/IgnoredTest/src/test/java/org/bitcoinj/core/PeerGroupTest.java
new file mode 100644
index 0000000..92db0b8
--- /dev/null
+++ b/src/resources/IgnoredTest/src/test/java/org/bitcoinj/core/PeerGroupTest.java
@@ -0,0 +1,856 @@
+/*
+ * Copyright 2011 Google Inc.
+ * Copyright 2014 Andreas Schildbach
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bitcoinj.core;
+
+import org.bitcoinj.net.discovery.PeerDiscovery;
+import org.bitcoinj.net.discovery.PeerDiscoveryException;
+import org.bitcoinj.params.UnitTestParams;
+import org.bitcoinj.testing.FakeTxBuilder;
+import org.bitcoinj.testing.InboundMessageQueuer;
+import org.bitcoinj.testing.TestWithPeerGroup;
+import org.bitcoinj.utils.Threading;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.net.InetAddresses;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.net.BindException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.util.*;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.bitcoinj.core.Coin.COIN;
+import static org.bitcoinj.core.Coin.valueOf;
+import static org.junit.Assert.*;
+
+
+// TX announcement and broadcast is tested in TransactionBroadcastTest.
+
+@RunWith(value = Parameterized.class)
+public class PeerGroupTest extends TestWithPeerGroup {
+ static final NetworkParameters params = UnitTestParams.get();
+ private BlockingQueue connectedPeers;
+ private BlockingQueue disconnectedPeers;
+ private PeerEventListener listener;
+ private Map peerToMessageCount;
+
+ @Parameterized.Parameters
+ public static Collection parameters() {
+ return Arrays.asList(new ClientType[] {ClientType.NIO_CLIENT_MANAGER},
+ new ClientType[] {ClientType.BLOCKING_CLIENT_MANAGER});
+ }
+
+ public PeerGroupTest(ClientType clientType) {
+ super(clientType);
+ }
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ peerToMessageCount = new HashMap();
+ connectedPeers = new LinkedBlockingQueue();
+ disconnectedPeers = new LinkedBlockingQueue();
+ listener = new AbstractPeerEventListener() {
+ @Override
+ public void onPeerConnected(Peer peer, int peerCount) {
+ connectedPeers.add(peer);
+ }
+
+ @Override
+ public void onPeerDisconnected(Peer peer, int peerCount) {
+ disconnectedPeers.add(peer);
+ }
+
+ @Override
+ public Message onPreMessageReceived(Peer peer, Message m) {
+ AtomicInteger messageCount = peerToMessageCount.get(peer);
+ if (messageCount == null) {
+ messageCount = new AtomicInteger(0);
+ peerToMessageCount.put(peer, messageCount);
+ }
+ messageCount.incrementAndGet();
+ // Just pass the message right through for further processing.
+ return m;
+ }
+ };
+ }
+
+ @Override
+ @After
+ public void tearDown() {
+ super.tearDown();
+ }
+
+ @Test
+ public void listener() throws Exception {
+ peerGroup.start();
+ peerGroup.addEventListener(listener);
+
+ // Create a couple of peers.
+ InboundMessageQueuer p1 = connectPeer(1);
+ InboundMessageQueuer p2 = connectPeer(2);
+ connectedPeers.take();
+ connectedPeers.take();
+
+ pingAndWait(p1);
+ pingAndWait(p2);
+ Threading.waitForUserCode();
+ assertEquals(0, disconnectedPeers.size());
+
+ p1.close();
+ disconnectedPeers.take();
+ assertEquals(0, disconnectedPeers.size());
+ p2.close();
+ disconnectedPeers.take();
+ assertEquals(0, disconnectedPeers.size());
+
+ assertTrue(peerGroup.removeEventListener(listener));
+ assertFalse(peerGroup.removeEventListener(listener));
+ }
+
+ @Test
+ public void peerDiscoveryPolling() throws InterruptedException {
+ // Check that if peer discovery fails, we keep trying until we have some nodes to talk with.
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicBoolean result = new AtomicBoolean();
+ peerGroup.addPeerDiscovery(new PeerDiscovery() {
+ @Override
+ public InetSocketAddress[] getPeers(long unused, TimeUnit unused2) throws PeerDiscoveryException {
+ if (!result.getAndSet(true)) {
+ // Pretend we are not connected to the internet.
+ throw new PeerDiscoveryException("test failure");
+ } else {
+ // Return a bogus address.
+ latch.countDown();
+ return new InetSocketAddress[]{new InetSocketAddress("localhost", 1)};
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ }
+ });
+ peerGroup.start();
+ latch.await();
+ // Check that we did indeed throw an exception. If we got here it means we threw and then PeerGroup tried
+ // again a bit later.
+ assertTrue(result.get());
+ }
+
+ // Utility method to create a PeerDiscovery with a certain number of addresses.
+ private PeerDiscovery createPeerDiscovery(int nrOfAddressesWanted, int port) {
+ final InetSocketAddress[] addresses = new InetSocketAddress[nrOfAddressesWanted];
+ for (int addressNr = 0; addressNr < nrOfAddressesWanted; addressNr++) {
+ // make each address unique by using the counter to increment the port.
+ addresses[addressNr] = new InetSocketAddress("localhost", port + addressNr);
+ }
+ return new PeerDiscovery() {
+ public InetSocketAddress[] getPeers(long unused, TimeUnit unused2) throws PeerDiscoveryException {
+ return addresses;
+ }
+ public void shutdown() {
+ }
+ };
+ }
+
+ @Test
+ public void multiplePeerDiscovery() throws InterruptedException {
+ peerGroup.setMaxPeersToDiscoverCount(98);
+ peerGroup.addPeerDiscovery(createPeerDiscovery(1, 0));
+ peerGroup.addPeerDiscovery(createPeerDiscovery(2, 100));
+ peerGroup.addPeerDiscovery(createPeerDiscovery(96, 200));
+ peerGroup.addPeerDiscovery(createPeerDiscovery(3, 300));
+ peerGroup.addPeerDiscovery(createPeerDiscovery(1, 400));
+ peerGroup.addEventListener(new AbstractPeerEventListener() {
+ @Override
+ public void onPeersDiscovered(Set peerAddresses) {
+ assertEquals(99, peerAddresses.size());
+ }
+ });
+ peerGroup.start();
+ }
+
+ @Test
+ public void receiveTxBroadcast() throws Exception {
+ // Check that when we receive transactions on all our peers, we do the right thing.
+ peerGroup.start();
+
+ // Create a couple of peers.
+ InboundMessageQueuer p1 = connectPeer(1);
+ InboundMessageQueuer p2 = connectPeer(2);
+
+ // Check the peer accessors.
+ assertEquals(2, peerGroup.numConnectedPeers());
+ Set tmp = new HashSet(peerGroup.getConnectedPeers());
+ Set expectedPeers = new HashSet();
+ expectedPeers.add(peerOf(p1));
+ expectedPeers.add(peerOf(p2));
+ assertEquals(tmp, expectedPeers);
+
+ Coin value = COIN;
+ Transaction t1 = FakeTxBuilder.createFakeTx(unitTestParams, value, address);
+ InventoryMessage inv = new InventoryMessage(unitTestParams);
+ inv.addTransaction(t1);
+
+ // Note: we start with p2 here to verify that transactions are downloaded from whichever peer announces first
+ // which does not have to be the same as the download peer (which is really the "block download peer").
+ inbound(p2, inv);
+ assertTrue(outbound(p2) instanceof GetDataMessage);
+ inbound(p1, inv);
+ assertNull(outbound(p1)); // Only one peer is used to download.
+ inbound(p2, t1);
+ assertNull(outbound(p1));
+ // Asks for dependency.
+ GetDataMessage getdata = (GetDataMessage) outbound(p2);
+ assertNotNull(getdata);
+ inbound(p2, new NotFoundMessage(unitTestParams, getdata.getItems()));
+ pingAndWait(p2);
+ assertEquals(value, wallet.getBalance(Wallet.BalanceType.ESTIMATED));
+ }
+
+
+ @Test
+ public void receiveTxBroadcastOnAddedWallet() throws Exception {
+ // Check that when we receive transactions on all our peers, we do the right thing.
+ peerGroup.start();
+
+ // Create a peer.
+ InboundMessageQueuer p1 = connectPeer(1);
+
+ Wallet wallet2 = new Wallet(unitTestParams);
+ ECKey key2 = wallet2.freshReceiveKey();
+ Address address2 = key2.toAddress(unitTestParams);
+
+ peerGroup.addWallet(wallet2);
+ blockChain.addWallet(wallet2);
+
+ assertEquals(BloomFilter.class, waitForOutbound(p1).getClass());
+ assertEquals(MemoryPoolMessage.class, waitForOutbound(p1).getClass());
+
+ Coin value = COIN;
+ Transaction t1 = FakeTxBuilder.createFakeTx(unitTestParams, value, address2);
+ InventoryMessage inv = new InventoryMessage(unitTestParams);
+ inv.addTransaction(t1);
+
+ inbound(p1, inv);
+ assertTrue(outbound(p1) instanceof GetDataMessage);
+ inbound(p1, t1);
+ // Asks for dependency.
+ GetDataMessage getdata = (GetDataMessage) outbound(p1);
+ assertNotNull(getdata);
+ inbound(p1, new NotFoundMessage(unitTestParams, getdata.getItems()));
+ pingAndWait(p1);
+ assertEquals(value, wallet2.getBalance(Wallet.BalanceType.ESTIMATED));
+ }
+
+ @Test
+ public void singleDownloadPeer1() throws Exception {
+ // Check that we don't attempt to retrieve blocks on multiple peers.
+ peerGroup.start();
+
+ // Create a couple of peers.
+ InboundMessageQueuer p1 = connectPeer(1);
+ InboundMessageQueuer p2 = connectPeer(2);
+ assertEquals(2, peerGroup.numConnectedPeers());
+
+ // Set up a little block chain. We heard about b1 but not b2 (it is pending download). b3 is solved whilst we
+ // are downloading the chain.
+ Block b1 = FakeTxBuilder.createFakeBlock(blockStore).block;
+ blockChain.add(b1);
+ Block b2 = FakeTxBuilder.makeSolvedTestBlock(b1);
+ Block b3 = FakeTxBuilder.makeSolvedTestBlock(b2);
+
+ // Peer 1 and 2 receives an inv advertising a newly solved block.
+ InventoryMessage inv = new InventoryMessage(params);
+ inv.addBlock(b3);
+ // Only peer 1 tries to download it.
+ inbound(p1, inv);
+ pingAndWait(p1);
+
+ assertTrue(outbound(p1) instanceof GetDataMessage);
+ assertNull(outbound(p2));
+ // Peer 1 goes away, peer 2 becomes the download peer and thus queries the remote mempool.
+ final SettableFuture p1CloseFuture = SettableFuture.create();
+ peerOf(p1).addEventListener(new AbstractPeerEventListener() {
+ @Override
+ public void onPeerDisconnected(Peer peer, int peerCount) {
+ p1CloseFuture.set(null);
+ }
+ });
+ closePeer(peerOf(p1));
+ p1CloseFuture.get();
+ // Peer 2 fetches it next time it hears an inv (should it fetch immediately?).
+ inbound(p2, inv);
+ assertTrue(outbound(p2) instanceof GetDataMessage);
+ }
+
+ @Test
+ public void singleDownloadPeer2() throws Exception {
+ // Check that we don't attempt multiple simultaneous block chain downloads, when adding a new peer in the
+ // middle of an existing chain download.
+ // Create a couple of peers.
+ peerGroup.start();
+
+ // Create a couple of peers.
+ InboundMessageQueuer p1 = connectPeer(1);
+
+ // Set up a little block chain.
+ Block b1 = FakeTxBuilder.createFakeBlock(blockStore).block;
+ Block b2 = FakeTxBuilder.makeSolvedTestBlock(b1);
+ Block b3 = FakeTxBuilder.makeSolvedTestBlock(b2);
+
+ // Expect a zero hash getblocks on p1. This is how the process starts.
+ peerGroup.startBlockChainDownload(new AbstractPeerEventListener() {
+ });
+ GetBlocksMessage getblocks = (GetBlocksMessage) outbound(p1);
+ assertEquals(Sha256Hash.ZERO_HASH, getblocks.getStopHash());
+ // We give back an inv with some blocks in it.
+ InventoryMessage inv = new InventoryMessage(params);
+ inv.addBlock(b1);
+ inv.addBlock(b2);
+ inv.addBlock(b3);
+
+ inbound(p1, inv);
+ assertTrue(outbound(p1) instanceof GetDataMessage);
+ // We hand back the first block.
+ inbound(p1, b1);
+ // Now we successfully connect to another peer. There should be no messages sent.
+ InboundMessageQueuer p2 = connectPeer(2);
+ Message message = (Message)outbound(p2);
+ assertNull(message == null ? "" : message.toString(), message);
+ }
+
+ @Test
+ public void transactionConfidence() throws Exception {
+ // Checks that we correctly count how many peers broadcast a transaction, so we can establish some measure of
+ // its trustworthyness assuming an untampered with internet connection.
+ peerGroup.start();
+
+ final Transaction[] event = new Transaction[1];
+ final TransactionConfidence[] confEvent = new TransactionConfidence[1];
+ peerGroup.addEventListener(new AbstractPeerEventListener() {
+ @Override
+ public void onTransaction(Peer peer, Transaction t) {
+ event[0] = t;
+ }
+ }, Threading.SAME_THREAD);
+
+ InboundMessageQueuer p1 = connectPeer(1);
+ InboundMessageQueuer p2 = connectPeer(2);
+ InboundMessageQueuer p3 = connectPeer(3);
+
+ Transaction tx = FakeTxBuilder.createFakeTx(params, valueOf(20, 0), address);
+ InventoryMessage inv = new InventoryMessage(params);
+ inv.addTransaction(tx);
+
+ // Peer 2 advertises the tx but does not receive it yet.
+ inbound(p2, inv);
+ assertTrue(outbound(p2) instanceof GetDataMessage);
+ assertEquals(0, tx.getConfidence().numBroadcastPeers());
+ assertTrue(blockChain.getContext().getConfidenceTable().maybeWasSeen(tx.getHash()));
+ assertNull(event[0]);
+ // Peer 1 advertises the tx, we don't do anything as it's already been requested.
+ inbound(p1, inv);
+ assertNull(outbound(p1));
+ // Peer 2 gets sent the tx and requests the dependency.
+ inbound(p2, tx);
+ assertTrue(outbound(p2) instanceof GetDataMessage);
+ tx = event[0]; // We want to use the canonical copy delivered by the PeerGroup from now on.
+ assertNotNull(tx);
+ event[0] = null;
+
+ // Peer 1 (the download peer) advertises the tx, we download it.
+ inbound(p1, inv); // returns getdata
+ inbound(p1, tx); // returns nothing after a queue drain.
+ // Two peers saw this tx hash.
+ assertEquals(2, tx.getConfidence().numBroadcastPeers());
+ assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p1).getAddress()));
+ assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p2).getAddress()));
+
+ tx.getConfidence().addEventListener(new TransactionConfidence.Listener() {
+ @Override
+ public void onConfidenceChanged(TransactionConfidence confidence, TransactionConfidence.Listener.ChangeReason reason) {
+ confEvent[0] = confidence;
+ }
+ });
+ // A straggler reports in.
+ inbound(p3, inv);
+ pingAndWait(p3);
+ Threading.waitForUserCode();
+ assertEquals(tx.getHash(), confEvent[0].getTransactionHash());
+ assertEquals(3, tx.getConfidence().numBroadcastPeers());
+ assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p3).getAddress()));
+ }
+
+ @Test
+ public void testWalletCatchupTime() throws Exception {
+ // Check the fast catchup time was initialized to something around the current runtime minus a week.
+ // The wallet was already added to the peer in setup.
+ final int WEEK = 86400 * 7;
+ final long now = Utils.currentTimeSeconds();
+ peerGroup.start();
+ assertTrue(peerGroup.getFastCatchupTimeSecs() > now - WEEK - 10000);
+ Wallet w2 = new Wallet(params);
+ ECKey key1 = new ECKey();
+ key1.setCreationTimeSeconds(now - 86400); // One day ago.
+ w2.importKey(key1);
+ peerGroup.addWallet(w2);
+ peerGroup.waitForJobQueue();
+ assertEquals(peerGroup.getFastCatchupTimeSecs(), now - 86400 - WEEK);
+ // Adding a key to the wallet should update the fast catchup time, but asynchronously and in the background
+ // due to the need to avoid complicated lock inversions.
+ ECKey key2 = new ECKey();
+ key2.setCreationTimeSeconds(now - 100000);
+ w2.importKey(key2);
+ peerGroup.waitForJobQueue();
+ assertEquals(peerGroup.getFastCatchupTimeSecs(), now - WEEK - 100000);
+ }
+
+ @Test
+ public void noPings() throws Exception {
+ peerGroup.start();
+ peerGroup.setPingIntervalMsec(0);
+ VersionMessage versionMessage = new VersionMessage(params, 2);
+ versionMessage.clientVersion = FilteredBlock.MIN_PROTOCOL_VERSION;
+ versionMessage.localServices = VersionMessage.NODE_NETWORK;
+ connectPeer(1, versionMessage);
+ peerGroup.waitForPeers(1).get();
+ assertFalse(peerGroup.getConnectedPeers().get(0).getLastPingTime() < Long.MAX_VALUE);
+ }
+
+ @Test
+ public void pings() throws Exception {
+ peerGroup.start();
+ peerGroup.setPingIntervalMsec(100);
+ VersionMessage versionMessage = new VersionMessage(params, 2);
+ versionMessage.clientVersion = FilteredBlock.MIN_PROTOCOL_VERSION;
+ versionMessage.localServices = VersionMessage.NODE_NETWORK;
+ InboundMessageQueuer p1 = connectPeer(1, versionMessage);
+ Ping ping = (Ping) waitForOutbound(p1);
+ inbound(p1, new Pong(ping.getNonce()));
+ pingAndWait(p1);
+ assertTrue(peerGroup.getConnectedPeers().get(0).getLastPingTime() < Long.MAX_VALUE);
+ // The call to outbound should block until a ping arrives.
+ ping = (Ping) waitForOutbound(p1);
+ inbound(p1, new Pong(ping.getNonce()));
+ assertTrue(peerGroup.getConnectedPeers().get(0).getLastPingTime() < Long.MAX_VALUE);
+ }
+
+ @Test
+ public void downloadPeerSelection() throws Exception {
+ peerGroup.start();
+ VersionMessage versionMessage2 = new VersionMessage(params, 2);
+ versionMessage2.clientVersion = FilteredBlock.MIN_PROTOCOL_VERSION;
+ versionMessage2.localServices = VersionMessage.NODE_NETWORK;
+ VersionMessage versionMessage3 = new VersionMessage(params, 3);
+ versionMessage3.clientVersion = FilteredBlock.MIN_PROTOCOL_VERSION;
+ versionMessage3.localServices = VersionMessage.NODE_NETWORK;
+ assertNull(peerGroup.getDownloadPeer());
+ Peer a = connectPeer(1, versionMessage2).peer;
+ assertEquals(2, peerGroup.getMostCommonChainHeight());
+ assertEquals(a, peerGroup.getDownloadPeer());
+ connectPeer(2, versionMessage2);
+ assertEquals(2, peerGroup.getMostCommonChainHeight());
+ assertEquals(a, peerGroup.getDownloadPeer()); // No change.
+ Peer c = connectPeer(3, versionMessage3).peer;
+ assertEquals(2, peerGroup.getMostCommonChainHeight());
+ assertEquals(a, peerGroup.getDownloadPeer()); // No change yet.
+ connectPeer(4, versionMessage3);
+ assertEquals(3, peerGroup.getMostCommonChainHeight());
+ assertEquals(a, peerGroup.getDownloadPeer()); // Still no change.
+
+ // New peer with a higher protocol version but same chain height.
+ // TODO: When PeerGroup.selectDownloadPeer.PREFERRED_VERSION is not equal to vMinRequiredProtocolVersion,
+ // reenable this test
+ /*VersionMessage versionMessage4 = new VersionMessage(params, 3);
+ versionMessage4.clientVersion = 100000;
+ versionMessage4.localServices = VersionMessage.NODE_NETWORK;
+ InboundMessageQueuer d = connectPeer(5, versionMessage4);
+ assertEquals(d.peer, peerGroup.getDownloadPeer());*/
+ }
+
+ @Test
+ public void peerTimeoutTest() throws Exception {
+ peerGroup.start();
+ peerGroup.setConnectTimeoutMillis(100);
+
+ final SettableFuture peerConnectedFuture = SettableFuture.create();
+ final SettableFuture peerDisconnectedFuture = SettableFuture.create();
+ peerGroup.addEventListener(new AbstractPeerEventListener() {
+ @Override
+ public void onPeerConnected(Peer peer, int peerCount) {
+ peerConnectedFuture.set(null);
+ }
+
+ @Override
+ public void onPeerDisconnected(Peer peer, int peerCount) {
+ peerDisconnectedFuture.set(null);
+ }
+ }, Threading.SAME_THREAD);
+ connectPeerWithoutVersionExchange(0);
+ Thread.sleep(50);
+ assertFalse(peerConnectedFuture.isDone() || peerDisconnectedFuture.isDone());
+ Thread.sleep(60);
+ assertTrue(!peerConnectedFuture.isDone());
+ assertTrue(!peerConnectedFuture.isDone() && peerDisconnectedFuture.isDone());
+ }
+
+ @Test
+ @Ignore("disabled for now as this test is too flaky")
+ public void peerPriority() throws Exception {
+ final List addresses = Lists.newArrayList(
+ new InetSocketAddress("localhost", 2000),
+ new InetSocketAddress("localhost", 2001),
+ new InetSocketAddress("localhost", 2002)
+ );
+ peerGroup.addEventListener(listener);
+ peerGroup.addPeerDiscovery(new PeerDiscovery() {
+ @Override
+ public InetSocketAddress[] getPeers(long unused, TimeUnit unused2) throws PeerDiscoveryException {
+ return addresses.toArray(new InetSocketAddress[addresses.size()]);
+ }
+
+ @Override
+ public void shutdown() {
+ }
+ });
+ peerGroup.setMaxConnections(3);
+
+ Utils.setMockSleep(true);
+ blockJobs = true;
+
+ jobBlocks.release(2); // startup + first peer discovery
+ peerGroup.start();
+
+ jobBlocks.release(3); // One for each peer.
+ handleConnectToPeer(0);
+ handleConnectToPeer(1);
+ handleConnectToPeer(2);
+ connectedPeers.take();
+ connectedPeers.take();
+ connectedPeers.take();
+ addresses.clear();
+ addresses.addAll(Lists.newArrayList(new InetSocketAddress("localhost", 2003)));
+ stopPeerServer(2);
+ assertEquals(2002, disconnectedPeers.take().getAddress().getPort()); // peer died
+
+ // discovers, connects to new peer
+ jobBlocks.release(1);
+ handleConnectToPeer(3);
+ assertEquals(2003, connectedPeers.take().getAddress().getPort());
+
+ stopPeerServer(1);
+ assertEquals(2001, disconnectedPeers.take().getAddress().getPort()); // peer died
+
+ // Alternates trying two offline peers
+ jobBlocks.release(10);
+ assertEquals(2001, disconnectedPeers.take().getAddress().getPort());
+ assertEquals(2002, disconnectedPeers.take().getAddress().getPort());
+ assertEquals(2001, disconnectedPeers.take().getAddress().getPort());
+ assertEquals(2002, disconnectedPeers.take().getAddress().getPort());
+ assertEquals(2001, disconnectedPeers.take().getAddress().getPort());
+
+ // Peer 2 comes online
+ startPeerServer(2);
+ jobBlocks.release(1);
+ handleConnectToPeer(2);
+ assertEquals(2002, connectedPeers.take().getAddress().getPort());
+
+ jobBlocks.release(6);
+ stopPeerServer(2);
+ assertEquals(2002, disconnectedPeers.take().getAddress().getPort()); // peer died
+
+ // Peer 2 is tried before peer 1, since it has a lower backoff due to recent success
+ assertEquals(2002, disconnectedPeers.take().getAddress().getPort());
+ assertEquals(2001, disconnectedPeers.take().getAddress().getPort());
+ }
+
+ @Test
+ public void testBloomOnP2Pubkey() throws Exception {
+ // Cover bug 513. When a relevant transaction with a p2pubkey output is found, the Bloom filter should be
+ // recalculated to include that transaction hash but not re-broadcast as the remote nodes should have followed
+ // the same procedure. However a new node that's connected should get the fresh filter.
+ peerGroup.start();
+ final ECKey key = wallet.currentReceiveKey();
+ // Create a couple of peers.
+ InboundMessageQueuer p1 = connectPeer(1);
+ InboundMessageQueuer p2 = connectPeer(2);
+ // Create a pay to pubkey tx.
+ Transaction tx = FakeTxBuilder.createFakeTx(params, COIN, key);
+ Transaction tx2 = new Transaction(params);
+ tx2.addInput(tx.getOutput(0));
+ TransactionOutPoint outpoint = tx2.getInput(0).getOutpoint();
+ assertTrue(p1.lastReceivedFilter.contains(key.getPubKey()));
+ assertFalse(p1.lastReceivedFilter.contains(tx.getHash().getBytes()));
+ inbound(p1, tx);
+ // p1 requests dep resolution, p2 is quiet.
+ assertTrue(outbound(p1) instanceof GetDataMessage);
+ final Sha256Hash dephash = tx.getInput(0).getOutpoint().getHash();
+ final InventoryItem inv = new InventoryItem(InventoryItem.Type.Transaction, dephash);
+ inbound(p1, new NotFoundMessage(params, ImmutableList.of(inv)));
+ assertNull(outbound(p1));
+ assertNull(outbound(p2));
+ peerGroup.waitForJobQueue();
+ // Now we connect p3 and there is a new bloom filter sent, that DOES match the relevant outpoint.
+ InboundMessageQueuer p3 = connectPeer(3);
+ assertTrue(p3.lastReceivedFilter.contains(key.getPubKey()));
+ assertTrue(p3.lastReceivedFilter.contains(outpoint.bitcoinSerialize()));
+ }
+
+ @Test
+ public void testBloomResendOnNewKey() throws Exception {
+ // Check that when we add a new key to the wallet, the Bloom filter is re-calculated and re-sent but only once
+ // we exceed the lookahead threshold.
+ wallet.setKeychainLookaheadSize(5);
+ wallet.setKeychainLookaheadThreshold(4);
+ peerGroup.start();
+ // Create a couple of peers.
+ InboundMessageQueuer p1 = connectPeer(1);
+ InboundMessageQueuer p2 = connectPeer(2);
+ peerGroup.waitForJobQueue();
+ BloomFilter f1 = p1.lastReceivedFilter;
+ ECKey key = null;
+ // We have to run ahead of the lookahead zone for this test. There should only be one bloom filter recalc.
+ for (int i = 0; i < wallet.getKeychainLookaheadSize() + wallet.getKeychainLookaheadThreshold() + 1; i++) {
+ key = wallet.freshReceiveKey();
+ }
+ peerGroup.waitForJobQueue();
+ BloomFilter bf, f2 = null;
+ while ((bf = (BloomFilter) outbound(p1)) != null) {
+ assertEquals(MemoryPoolMessage.class, outbound(p1).getClass());
+ f2 = bf;
+ }
+ assertNotNull(key);
+ assertNotNull(f2);
+ assertNull(outbound(p1));
+ // Check the last filter received.
+ assertNotEquals(f1, f2);
+ assertTrue(f2.contains(key.getPubKey()));
+ assertTrue(f2.contains(key.getPubKeyHash()));
+ assertFalse(f1.contains(key.getPubKey()));
+ assertFalse(f1.contains(key.getPubKeyHash()));
+ }
+
+ @Test
+ public void waitForNumPeers1() throws Exception {
+ ListenableFuture> future = peerGroup.waitForPeers(3);
+ peerGroup.start();
+ assertFalse(future.isDone());
+ connectPeer(1);
+ assertFalse(future.isDone());
+ connectPeer(2);
+ assertFalse(future.isDone());
+ assertTrue(peerGroup.waitForPeers(2).isDone()); // Immediate completion.
+ connectPeer(3);
+ future.get();
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ public void waitForPeersOfVersion() throws Exception {
+ final int baseVer = peerGroup.getMinRequiredProtocolVersion() + 3000;
+ final int newVer = baseVer + 1000;
+
+ ListenableFuture> future = peerGroup.waitForPeersOfVersion(2, newVer);
+
+ VersionMessage ver1 = new VersionMessage(params, 10);
+ ver1.clientVersion = baseVer;
+ ver1.localServices = VersionMessage.NODE_NETWORK;
+ VersionMessage ver2 = new VersionMessage(params, 10);
+ ver2.clientVersion = newVer;
+ ver2.localServices = VersionMessage.NODE_NETWORK;
+ peerGroup.start();
+ assertFalse(future.isDone());
+ connectPeer(1, ver1);
+ assertFalse(future.isDone());
+ connectPeer(2, ver2);
+ assertFalse(future.isDone());
+ assertTrue(peerGroup.waitForPeersOfVersion(1, newVer).isDone()); // Immediate completion.
+ connectPeer(3, ver2);
+ future.get();
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ public void waitForPeersWithServiceFlags() throws Exception {
+ ListenableFuture> future = peerGroup.waitForPeersWithServiceMask(2, 3);
+
+ VersionMessage ver1 = new VersionMessage(params, 10);
+ ver1.clientVersion = 70000;
+ ver1.localServices = VersionMessage.NODE_NETWORK;
+ VersionMessage ver2 = new VersionMessage(params, 10);
+ ver2.clientVersion = 70000;
+ ver2.localServices = VersionMessage.NODE_NETWORK | 2;
+ peerGroup.start();
+ assertFalse(future.isDone());
+ connectPeer(1, ver1);
+ assertTrue(peerGroup.findPeersWithServiceMask(3).isEmpty());
+ assertFalse(future.isDone());
+ connectPeer(2, ver2);
+ assertFalse(future.isDone());
+ assertEquals(1, peerGroup.findPeersWithServiceMask(3).size());
+ assertTrue(peerGroup.waitForPeersWithServiceMask(1, 0x3).isDone()); // Immediate completion.
+ connectPeer(3, ver2);
+ future.get();
+ assertTrue(future.isDone());
+ peerGroup.stop();
+ }
+
+ @Test
+ public void preferLocalPeer() throws IOException {
+ // Because we are using the same port (8333 or 18333) that is used by Satoshi client
+ // We have to consider 2 cases:
+ // 1. Test are executed on the same machine that is running full node / Satoshi client
+ // 2. Test are executed without any full node running locally
+ // We have to avoid to connecting to real and external services in unit tests
+ // So we skip this test in case we have already something running on port params.getPort()
+
+ // Check that if we have a localhost port 8333 or 18333 then it's used instead of the p2p network.
+ ServerSocket local = null;
+ try {
+ local = new ServerSocket(params.getPort(), 100, InetAddresses.forString("127.0.0.1"));
+ }
+ catch(BindException e) { // Port already in use, skipping this test.
+ return;
+ }
+
+ try {
+ peerGroup.setUseLocalhostPeerWhenPossible(true);
+ peerGroup.start();
+ local.accept().close(); // Probe connect
+ local.accept(); // Real connect
+ // If we get here it used the local peer. Check no others are in use.
+ assertEquals(1, peerGroup.getMaxConnections());
+ assertEquals(PeerAddress.localhost(params), peerGroup.getPendingPeers().get(0).getAddress());
+ } finally {
+ local.close();
+ }
+ }
+
+ private T assertNextMessageIs(InboundMessageQueuer q, Class klass) throws Exception {
+ Message outbound = waitForOutbound(q);
+ assertEquals(klass, outbound.getClass());
+ return (T) outbound;
+ }
+
+ @Test
+ public void autoRescanOnKeyExhaustion() throws Exception {
+ // Check that if the last key that was inserted into the bloom filter is seen in some requested blocks,
+ // that the exhausting block is discarded, a new filter is calculated and sent, and then the download resumes.
+
+ final int NUM_KEYS = 9;
+
+ // First, grab a load of keys from the wallet, and then recreate it so it forgets that those keys were issued.
+ Wallet shadow = Wallet.fromSeed(wallet.getParams(), wallet.getKeyChainSeed());
+ List keys = new ArrayList(NUM_KEYS);
+ for (int i = 0; i < NUM_KEYS; i++) {
+ keys.add(shadow.freshReceiveKey());
+ }
+ // Reduce the number of keys we need to work with to speed up this test.
+ wallet.setKeychainLookaheadSize(4);
+ wallet.setKeychainLookaheadThreshold(2);
+
+ peerGroup.start();
+ InboundMessageQueuer p1 = connectPeer(1);
+ assertTrue(p1.lastReceivedFilter.contains(keys.get(0).getPubKey()));
+ assertTrue(p1.lastReceivedFilter.contains(keys.get(5).getPubKeyHash()));
+ assertFalse(p1.lastReceivedFilter.contains(keys.get(keys.size() - 1).getPubKey()));
+ peerGroup.startBlockChainDownload(null);
+ assertNextMessageIs(p1, GetBlocksMessage.class);
+
+ // Make some transactions and blocks that send money to the wallet thus using up all the keys.
+ List blocks = Lists.newArrayList();
+ Coin expectedBalance = Coin.ZERO;
+ Block prev = blockStore.getChainHead().getHeader();
+ for (ECKey key1 : keys) {
+ Address addr = key1.toAddress(params);
+ Block next = FakeTxBuilder.makeSolvedTestBlock(prev, FakeTxBuilder.createFakeTx(params, Coin.FIFTY_COINS, addr));
+ expectedBalance = expectedBalance.add(next.getTransactions().get(2).getOutput(0).getValue());
+ blocks.add(next);
+ prev = next;
+ }
+
+ // Send the chain that doesn't have all the transactions in it. The blocks after the exhaustion point should all
+ // be ignored.
+ int epoch = wallet.keychain.getCombinedKeyLookaheadEpochs();
+ BloomFilter filter = new BloomFilter(params, p1.lastReceivedFilter.bitcoinSerialize());
+ filterAndSend(p1, blocks, filter);
+ Block exhaustionPoint = blocks.get(3);
+ pingAndWait(p1);
+
+ assertNotEquals(epoch, wallet.keychain.getCombinedKeyLookaheadEpochs());
+ // 4th block was end of the lookahead zone and thus was discarded, so we got 3 blocks worth of money (50 each).
+ assertEquals(Coin.FIFTY_COINS.multiply(3), wallet.getBalance());
+ assertEquals(exhaustionPoint.getPrevBlockHash(), blockChain.getChainHead().getHeader().getHash());
+
+ // Await the new filter.
+ peerGroup.waitForJobQueue();
+ BloomFilter newFilter = assertNextMessageIs(p1, BloomFilter.class);
+ assertNotEquals(filter, newFilter);
+ assertNextMessageIs(p1, MemoryPoolMessage.class);
+ Ping ping = assertNextMessageIs(p1, Ping.class);
+ inbound(p1, new Pong(ping.getNonce()));
+
+ // Await restart of the chain download.
+ GetDataMessage getdata = assertNextMessageIs(p1, GetDataMessage.class);
+ assertEquals(exhaustionPoint.getHash(), getdata.getHashOf(0));
+ assertEquals(InventoryItem.Type.FilteredBlock, getdata.getItems().get(0).type);
+ List newBlocks = blocks.subList(3, blocks.size());
+ filterAndSend(p1, newBlocks, newFilter);
+ assertNextMessageIs(p1, Ping.class);
+
+ // It happened again.
+ peerGroup.waitForJobQueue();
+ newFilter = assertNextMessageIs(p1, BloomFilter.class);
+ assertNextMessageIs(p1, MemoryPoolMessage.class);
+ inbound(p1, new Pong(assertNextMessageIs(p1, Ping.class).getNonce()));
+ assertNextMessageIs(p1, GetDataMessage.class);
+ newBlocks = blocks.subList(6, blocks.size());
+ filterAndSend(p1, newBlocks, newFilter);
+ // Send a non-tx message so the peer knows the filtered block is over and force processing.
+ inbound(p1, new Ping());
+ pingAndWait(p1);
+
+ assertEquals(expectedBalance, wallet.getBalance());
+ assertEquals(blocks.get(blocks.size() - 1).getHash(), blockChain.getChainHead().getHeader().getHash());
+ }
+
+ private void filterAndSend(InboundMessageQueuer p1, List blocks, BloomFilter filter) {
+ for (Block block : blocks) {
+ FilteredBlock fb = filter.applyAndUpdate(block);
+ inbound(p1, fb);
+ for (Transaction tx : fb.getAssociatedTransactions().values())
+ inbound(p1, tx);
+ }
+ }
+}
diff --git a/src/resources/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java b/src/resources/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java
new file mode 100644
index 0000000..5ba905f
--- /dev/null
+++ b/src/resources/LazyTest/src/main/java/com/github/marmaladesky/Cryptographer.java
@@ -0,0 +1,157 @@
+package com.github.marmaladesky;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.security.Key;
+import java.security.MessageDigest;
+import java.security.spec.KeySpec;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.zip.DataFormatException;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.Inflater;
+
+public class Cryptographer {
+
+ final static byte[] MAGIC_STRING_DATA_VERSION_2 = new byte[] {'r', 'v', 'l', 0, 2, 0};
+ final static byte[] VERSION_0_4_7 = new byte[] {0, 4, 7};
+
+ public static String decrypt(byte[] fileData, String password) throws Exception {
+ byte[] header;
+ header = Arrays.copyOfRange(fileData, 0, 36);
+
+ byte[] iv = null;
+ byte[] salt = null;
+
+ if(Arrays.equals(Arrays.copyOfRange(header, 0, 6), MAGIC_STRING_DATA_VERSION_2)) {
+ if(Arrays.equals(VERSION_0_4_7, Arrays.copyOfRange(header, 6, 9))) {
+ salt = Arrays.copyOfRange(header, 12, 20);
+ iv = Arrays.copyOfRange(header, 20, 36);
+ Cipher cypher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ SecretKeyFactory scf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
+ KeySpec ks = new PBEKeySpec(password.toCharArray(), salt, 12000, 256);
+ SecretKey s = scf.generateSecret(ks); // Bottleneck (12k)
+ Key k = new SecretKeySpec(s.getEncoded(),"AES");
+ cypher.init(Cipher.DECRYPT_MODE, k, new IvParameterSpec(iv));
+
+ byte[] input = Arrays.copyOfRange(fileData, 36, fileData.length);
+ byte[] compressedData = cypher.doFinal(input);
+
+ byte[] hash256 = Arrays.copyOfRange(compressedData, 0, 32);
+
+ compressedData = Arrays.copyOfRange(compressedData, 32, compressedData.length);
+
+ compressedData = addPadding(compressedData);
+
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(compressedData);
+ byte[] computedHash = md.digest();
+
+ if(!Arrays.equals(computedHash, hash256)) {
+ throw new Exception("Invalid data");
+ }
+
+ byte[] result = decompress(compressedData);
+ return new String(result, Charset.forName("UTF-8"));
+ }
+ }
+
+ throw new Exception("Unknown file format");
+ }
+
+ public static byte[] encrypt(String xmlData, String password) throws Exception {
+ if(password == null || password.equals(""))
+ throw new Exception("Password cannot be empty");
+
+ Random r = new Random();
+
+ byte[] salt = new byte[8];
+ r.nextBytes(salt);
+
+ byte[] compressedData = compress(xmlData);
+
+ byte[] compressedDataWithPadlen = addPadding(compressedData);
+
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(compressedDataWithPadlen);
+ byte[] computedHash = md.digest();
+
+ byte[] hashAndData = concatenateByteArrays(computedHash, compressedData);
+
+ byte[] iv = new byte[16];
+ r.nextBytes(iv);
+
+ Cipher cypher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+
+ SecretKeyFactory scf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
+
+ KeySpec ks = new PBEKeySpec(password.toCharArray(), salt, 12000, 256);
+ SecretKey s = scf.generateSecret(ks);
+ Key k = new SecretKeySpec(s.getEncoded(), "AES");
+
+ cypher.init(Cipher.ENCRYPT_MODE, k, new IvParameterSpec(iv));
+
+ byte[] encrypted= new byte[cypher.getOutputSize(hashAndData.length)];
+
+ int enc_len = cypher.update(hashAndData, 0, hashAndData.length, encrypted, 0);
+ cypher.doFinal(encrypted, enc_len);
+
+ byte[] a1 = concatenateByteArrays(MAGIC_STRING_DATA_VERSION_2, VERSION_0_4_7);
+ a1 = concatenateByteArrays(a1, new byte[] {0,0,0});
+ a1 = concatenateByteArrays(a1, salt);
+ a1 = concatenateByteArrays(a1, iv);
+ a1 = concatenateByteArrays(a1, encrypted);
+
+ return a1;
+ }
+
+ private static byte[] decompress(byte[] inputData) throws DataFormatException, IOException {
+ Inflater decompressor = new Inflater();
+ decompressor.setInput(inputData);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(inputData.length);
+ byte[] buf = new byte[1024];
+ while (!decompressor.finished()) {
+ int count = decompressor.inflate(buf);
+ bos.write(buf, 0, count);
+ }
+ bos.close();
+ return bos.toByteArray();
+ }
+
+ private static byte[] compress(String text) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ OutputStream out = new DeflaterOutputStream(baos);
+ out.write(text.getBytes("UTF-8"));
+ out.close();
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ return baos.toByteArray();
+ }
+
+ private static byte[] concatenateByteArrays(byte[] a, byte[] b) {
+ byte[] result = new byte[a.length + b.length];
+ System.arraycopy(a, 0, result, 0, a.length);
+ System.arraycopy(b, 0, result, a.length, b.length);
+ return result;
+ }
+
+ private static byte[] addPadding (byte[] a) {
+ byte padlen = (byte)(16 - (a.length % 16));
+ if (padlen == 0)
+ padlen = 16;
+
+ byte[] b = new byte[a.length + padlen];
+ System.arraycopy(a, 0, b, 0, a.length);
+ for (int i = 0; i < padlen; i++)
+ b[a.length + i] = padlen;
+ return b;
+ }
+}
diff --git a/src/resources/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java b/src/resources/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java
new file mode 100644
index 0000000..fe091b0
--- /dev/null
+++ b/src/resources/LazyTest/src/test/java/com/github/marmaladesky/tests/CryptographerTest.java
@@ -0,0 +1,51 @@
+package com.github.marmaladesky.tests;
+
+import com.github.marmaladesky.Cryptographer;
+
+import org.junit.Test;
+
+import java.io.*;
+
+import static org.junit.Assert.assertEquals;
+
+public class CryptographerTest {
+
+ private static final String DECRYPTED_DATA_FILE_4_14 = "src/test/res/rvl_test-0.4.14.xml";
+ private static final String ENCRYPTED_DATA_FILE_4_14 = "src/test/res/rvl_test-0.4.14";
+
+ @Test
+ public void testDecrypt() throws Exception {
+ FileInputStream file = new FileInputStream(ENCRYPTED_DATA_FILE_4_14);
+ byte[] enfileData = new byte[file.available()];
+ FileInputStream input = new FileInputStream(DECRYPTED_DATA_FILE_4_14);
+ byte[] fileData = new byte[input.available()];
+ input.read(fileData);
+ input.close();
+ file.read(enfileData);
+ file.close();
+ String expectedResult = new String(fileData, "UTF-8");
+ assertEquals("Testing simple decrypt",expectedResult, Cryptographer.decrypt(enfileData, "test"));
+ }
+
+ @Test
+ public void testEncrypt() throws Exception {
+ String xml = readFileAsString(DECRYPTED_DATA_FILE_4_14);
+ byte[] encrypted = Cryptographer.encrypt(xml, "test");
+ String decrypt = Cryptographer.decrypt(encrypted, "test");
+ assertEquals(xml, decrypt);
+ }
+
+ private String readFileAsString(String filePath) throws IOException {
+ StringBuffer fileData = new StringBuffer();
+ BufferedReader reader = new BufferedReader(
+ new FileReader(filePath));
+ char[] buf = new char[1024];
+ int numRead;
+ while((numRead=reader.read(buf)) != -1){
+ String readData = String.valueOf(buf, 0, numRead);
+ fileData.append(readData);
+ }
+ reader.close();
+ return fileData.toString();
+ }
+}
diff --git a/src/resources/META-INF/MANIFEST.MF b/src/resources/META-INF/MANIFEST.MF
index 5ee19cb..6711b7a 100644
--- a/src/resources/META-INF/MANIFEST.MF
+++ b/src/resources/META-INF/MANIFEST.MF
@@ -1,3 +1,3 @@
-Manifest-Version: 1.0
-Main-Class: Main
-
+Manifest-Version: 1.0
+Main-Class: edu.rit.se.testsmells.Main
+
diff --git a/src/resources/MagicNumberTest/src/test/java/com/luckycatlabs/sunrisesunset/calculator/SolarEventCalculatorTest.java b/src/resources/MagicNumberTest/src/test/java/com/luckycatlabs/sunrisesunset/calculator/SolarEventCalculatorTest.java
new file mode 100644
index 0000000..9423880
--- /dev/null
+++ b/src/resources/MagicNumberTest/src/test/java/com/luckycatlabs/sunrisesunset/calculator/SolarEventCalculatorTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2008-2009 Mike Reedell / LuckyCatLabs.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.luckycatlabs.sunrisesunset.calculator;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigDecimal;
+import java.util.Calendar;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.luckycatlabs.sunrisesunset.Zenith;
+import com.luckycatlabs.sunrisesunset.util.BaseTestCase;
+
+public class SolarEventCalculatorTest extends BaseTestCase {
+
+ private SolarEventCalculator calc;
+
+ @Before
+ public void setupCalculator() {
+ super.setup(10, 1, 2008);
+ calc = new SolarEventCalculator(location, "America/New_York");
+ }
+
+ @Test
+ public void testComputeSunriseTime() {
+ String localSunriseTime = "07:05";
+ assertEquals(localSunriseTime, calc.computeSunriseTime(Zenith.CIVIL, eventDate));
+ }
+
+ @Test
+ public void testComputeSunsetTime() {
+ String localSunsetTime = "18:28";
+ assertEquals(localSunsetTime, calc.computeSunsetTime(Zenith.CIVIL, eventDate));
+ }
+
+ @Test
+ public void testGetLocalTimeAsCalendar() {
+ Calendar localTime = calc.getLocalTimeAsCalendar(BigDecimal.valueOf(15.5D), Calendar.getInstance());
+ assertEquals(15, localTime.get(Calendar.HOUR_OF_DAY));
+ assertEquals(30, localTime.get(Calendar.MINUTE));
+ }
+
+ @Test
+ public void testGetLocalTimeAsCalendarForZero() {
+ Calendar localTime = calc.getLocalTimeAsCalendar(BigDecimal.valueOf(0.0D), Calendar.getInstance());
+ assertEquals(0, localTime.get(Calendar.HOUR_OF_DAY));
+ assertEquals(0, localTime.get(Calendar.MINUTE));
+ }
+
+ @Test
+ public void testGetLocalTimeAsCalendarForNegative() {
+ Calendar localTime = calc.getLocalTimeAsCalendar(BigDecimal.valueOf(-10.0D), Calendar.getInstance());
+ assertEquals(14, localTime.get(Calendar.HOUR_OF_DAY));
+ assertEquals(0, localTime.get(Calendar.MINUTE));
+ }
+}
diff --git a/src/resources/MysteryGuest/src/androidTest/java/com/gmail/walles/johan/batterylogger/SystemStateTest.java b/src/resources/MysteryGuest/src/androidTest/java/com/gmail/walles/johan/batterylogger/SystemStateTest.java
new file mode 100644
index 0000000..b9a3d2f
--- /dev/null
+++ b/src/resources/MysteryGuest/src/androidTest/java/com/gmail/walles/johan/batterylogger/SystemStateTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2016 Johan Walles
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.gmail.walles.johan.batterylogger;
+
+import android.test.AndroidTestCase;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+
+public class SystemStateTest extends AndroidTestCase {
+ private final Date now = new Date();
+ private final Date then = new Date(now.getTime() - History.FIVE_MINUTES_MS);
+ private final Date bootTimestamp = new Date(then.getTime() - History.FIVE_MINUTES_MS);
+
+ public void testConstructor() {
+ try {
+ new SystemState(then, 27, false, now);
+ fail("Expected IAE when boot is in the future");
+ } catch (IllegalArgumentException ignored) {
+ // Expected exception intentionally ignored
+ }
+ }
+
+ public void testEquals() {
+ assertTrue(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(now, 27, true, bootTimestamp)));
+ assertFalse(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(then, 27, true, bootTimestamp)));
+ assertFalse(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(now, 36, true, bootTimestamp)));
+ assertFalse(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(now, 27, false, bootTimestamp)));
+ assertFalse(new SystemState(now, 27, true, bootTimestamp).equals(new SystemState(now, 27, false, then)));
+
+ SystemState a = new SystemState(now, 27, false, bootTimestamp);
+ a.addInstalledApp("a.b.c", "Griseknoa", "1.2.3");
+
+ SystemState b = new SystemState(now, 27, false, bootTimestamp);
+ b.addInstalledApp("a.b.c", "Griseknoa", "1.2.3");
+ assertEquals(a, b);
+
+ SystemState c = new SystemState(now, 27, false, bootTimestamp);
+ c.addInstalledApp("x.y.z", "Griseknoa", "1.2.3");
+ assertFalse(a.equals(c));
+
+ SystemState d = new SystemState(now, 27, false, bootTimestamp);
+ d.addInstalledApp("a.b.c", "Charles-Ingvar", "1.2.3");
+ assertFalse(a.equals(d));
+
+ SystemState e = new SystemState(now, 27, false, bootTimestamp);
+ e.addInstalledApp("a.b.c", "Griseknoa", "4.5.6");
+ assertFalse(a.equals(e));
+ }
+
+ public void testUnorderedEquals() {
+ SystemState a = new SystemState(now, 27, false, bootTimestamp);
+ a.addInstalledApp("a.b.c", "Griseknoa", "1.2.3");
+ a.addInstalledApp("d.e.f", "Snickarboa", "4.5.6");
+
+ SystemState b = new SystemState(now, 27, false, bootTimestamp);
+ b.addInstalledApp("d.e.f", "Snickarboa", "4.5.6");
+ b.addInstalledApp("a.b.c", "Griseknoa", "1.2.3");
+
+ assertEquals(a, b);
+ }
+
+ private void assertEvents(Collection testMe, HistoryEvent ... expected) {
+ assertNotNull("Events must be non-null, were null", testMe);
+ String expectedString = Arrays.toString(expected);
+ String actualString = Arrays.toString(testMe.toArray());
+ assertEquals(expectedString, actualString);
+ }
+
+ private void assertNoEvents(Collection testMe) {
+ assertEvents(testMe);
+ }
+
+ public void testBatteryEvent() {
+ SystemState a = new SystemState(then, 27, false, bootTimestamp);
+
+ SystemState b = new SystemState(now, 26, false, bootTimestamp);
+ assertEvents(b.getEventsSince(a), HistoryEvent.createBatteryLevelEvent(now, 26));
+
+ SystemState c = new SystemState(now, 28, true, bootTimestamp);
+ assertEvents(c.getEventsSince(a),
+ HistoryEvent.createStartChargingEvent(between(then, now)),
+ HistoryEvent.createBatteryLevelEvent(now, 28));
+
+ SystemState d = new SystemState(now, 29, true, bootTimestamp);
+ assertNoEvents(d.getEventsSince(c));
+ }
+
+ public void testStartChargingEvent() {
+ SystemState a = new SystemState(then, 27, false, bootTimestamp);
+ SystemState b = new SystemState(now, 27, true, bootTimestamp);
+
+ assertEvents(b.getEventsSince(a), HistoryEvent.createStartChargingEvent(between(then, now)));
+ }
+
+ public void testStopChargingEvent() {
+ SystemState a = new SystemState(then, 27, true, bootTimestamp);
+ SystemState b = new SystemState(now, 27, false, bootTimestamp);
+
+ assertEvents(b.getEventsSince(a), HistoryEvent.createStopChargingEvent(between(then, now)));
+ }
+
+ private static Date between(Date t0, Date t1) {
+ return new Date((t0.getTime() + t1.getTime()) / 2);
+ }
+
+ public void testInstallEvent() {
+ SystemState a = new SystemState(then, 27, false, bootTimestamp);
+
+ SystemState b = new SystemState(now, 27, false, bootTimestamp);
+ b.addInstalledApp("a.b.c", "ABC", "1.2.3");
+
+ assertEvents(b.getEventsSince(a), HistoryEvent.createInfoEvent(between(then, now), "ABC 1.2.3 installed"));
+ }
+
+ public void testUninstallEvent() {
+ SystemState a = new SystemState(then, 27, false, bootTimestamp);
+ a.addInstalledApp("a.b.c", "ABC", "1.2.3");
+
+ SystemState b = new SystemState(now, 27, false, bootTimestamp);
+
+ assertEvents(b.getEventsSince(a), HistoryEvent.createInfoEvent(between(then, now), "ABC 1.2.3 uninstalled"));
+ }
+
+ public void testUpgradeEvent() {
+ SystemState a = new SystemState(then, 27, false, bootTimestamp);
+ a.addInstalledApp("a.b.c", "ABC", "1.2.3");
+
+ SystemState b = new SystemState(now, 27, false, bootTimestamp);
+ b.addInstalledApp("a.b.c", "ABC", "2.3.4");
+
+ assertEvents(b.getEventsSince(a), HistoryEvent.createInfoEvent(between(then, now), "ABC upgraded from 1.2.3 to 2.3.4"));
+ }
+
+ public void testPersistence() throws Exception {
+ File tempFile = File.createTempFile("systemstate-", ".txt");
+ try {
+ SystemState a = new SystemState(then, 27, false, bootTimestamp);
+ a.addInstalledApp("a.b.c", "ABC", "1.2.3");
+
+ a.writeToFile(tempFile);
+ SystemState b = SystemState.readFromFile(tempFile);
+
+ assertEquals(a, b);
+ } finally {
+ //noinspection ConstantConditions
+ if (tempFile != null) {
+ //noinspection ResultOfMethodCallIgnored
+ tempFile.delete();
+ }
+ }
+ }
+
+ public void testHaltAndBootEvents() {
+ Date boot1 = new Date(0);
+ Date sample1 = new Date(100000);
+ Date boot2 = new Date(200000);
+ Date sample2 = new Date(300000);
+
+ SystemState beforeReboot = new SystemState(sample1, 27, false, boot1);
+ SystemState afterReboot = new SystemState(sample2, 27, false, boot2);
+
+ assertEvents(afterReboot.getEventsSince(beforeReboot),
+ HistoryEvent.createSystemHaltingEvent(new Date(sample1.getTime() + 1)),
+ HistoryEvent.createSystemBootingEvent(boot2, false));
+ }
+
+ public void testHaltAndBootAndChargeEvents() {
+ Date boot1 = new Date(0);
+ Date sample1 = new Date(100000);
+ Date boot2 = new Date(200000);
+ Date sample2 = new Date(300000);
+
+ SystemState beforeReboot = new SystemState(sample1, 27, false, boot1);
+ SystemState afterReboot = new SystemState(sample2, 27, true, boot2);
+
+ assertEvents(afterReboot.getEventsSince(beforeReboot),
+ HistoryEvent.createSystemHaltingEvent(new Date(sample1.getTime() + 1)),
+ HistoryEvent.createSystemBootingEvent(boot2, true));
+ }
+
+ /**
+ * Verify that different events resulting in text in the graph don't overlap each other.
+ */
+ public void testPreventOverlappingEvents() {
+ SystemState a = new SystemState(then, 27, true, bootTimestamp);
+ a.addInstalledApp("a.b.c", "Upgrader", "1.2.3");
+ a.addInstalledApp("d.e.f", "Remover", "2.3.4");
+
+ SystemState b = new SystemState(now, 26, false, bootTimestamp);
+ b.addInstalledApp("a.b.c", "Upgrader", "1.2.5");
+ b.addInstalledApp("g.h.i", "Adder", "5.6.7");
+
+ Date datesBetween[] = SystemState.between(then, now, 4);
+ // Note that the actual order here is arbitrary
+ assertEvents(b.getEventsSince(a),
+ HistoryEvent.createStopChargingEvent(datesBetween[0]),
+ HistoryEvent.createInfoEvent(datesBetween[1], "Adder 5.6.7 installed"),
+ HistoryEvent.createInfoEvent(datesBetween[2], "Remover 2.3.4 uninstalled"),
+ HistoryEvent.createInfoEvent(datesBetween[3], "Upgrader upgraded from 1.2.3 to 1.2.5"),
+ HistoryEvent.createBatteryLevelEvent(now, 26));
+ }
+
+ public void testBetween() {
+ Date dates[] = SystemState.between(then, now, 1);
+ assertEquals(1, dates.length);
+ assertEquals(between(then, now), dates[0]);
+ }
+
+ /**
+ * Two SystemStates should be equal even if their boot timestamps are a little bit off. Since the boot timestamps
+ * are calculated will millisecond precision we need some margin of error.
+ */
+ public void testBootTimeLeniency() {
+ SystemState a = new SystemState(now, 27, false, new Date(0));
+ SystemState b = new SystemState(now, 27, false, new Date(10 * 1000));
+ assertEquals(a, b);
+
+ SystemState c = new SystemState(now, 27, false, new Date(100 * 1000));
+ assertFalse(a.equals(c));
+ }
+
+ /**
+ * Sanity check the current system state.
+ */
+ public void testReadFromSystem() throws Exception {
+ //noinspection ConstantConditions
+ SystemState testMe = SystemState.readFromSystem(getContext());
+
+ int appCount = testMe.getAppCount();
+ assertTrue("App count is " + appCount, testMe.getAppCount() > 10);
+
+ int batteryPercentage = testMe.getBatteryPercentage();
+ assertTrue("Battery percentage is " + batteryPercentage + "%", testMe.getBatteryPercentage() >= 0);
+
+ File tempFile = File.createTempFile("systemstate-", ".txt");
+ try {
+ testMe.writeToFile(tempFile);
+ assertEquals(testMe, SystemState.readFromFile(tempFile));
+ } finally {
+ //noinspection ConstantConditions
+ if (tempFile != null) {
+ //noinspection ResultOfMethodCallIgnored
+ tempFile.delete();
+ }
+ }
+ }
+
+ public void testGetBootTimestamp() throws Exception {
+ SystemState.getBootTimestamp();
+ Date b0 = SystemState.getBootTimestamp();
+ Thread.sleep(1000, 0);
+ Date b1 = SystemState.getBootTimestamp();
+
+ long dt = Math.abs(b1.getTime() - b0.getTime());
+ assertTrue("Too much drift over one second: " + dt + "ms", dt < 200);
+
+ assertTrue("Boot timestamp can't be in the future", b0.before(new Date()));
+ }
+}
diff --git a/src/resources/Queue/pom.xml b/src/resources/Queue/pom.xml
new file mode 100644
index 0000000..2d3f5ab
--- /dev/null
+++ b/src/resources/Queue/pom.xml
@@ -0,0 +1,110 @@
+
+
+ 4.0.0
+
+ br.ufv.aserg.victorveloso
+ Queue
+ 1.0-SNAPSHOT
+
+