kopia lustrzana https://github.com/ge0rg/aprsdroid
Merge 936f588e4a into 859033e0d7
commit
49ece3a7f6
|
|
@ -0,0 +1,188 @@
|
|||
name: Android CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '**' ]
|
||||
pull_request:
|
||||
branches: [ '**' ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Compile all sources"
|
||||
|
||||
steps:
|
||||
- name: Checkout project
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
|
||||
- name: Load build outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: build
|
||||
key: build-${{ github.sha }}
|
||||
|
||||
- name: Create properties file with empty API key
|
||||
run: echo mapsApiKey="\"${{ secrets.mapsApiKey }}\"" >> local.properties
|
||||
|
||||
- name: Build App
|
||||
run: ./gradlew assemble --stacktrace
|
||||
|
||||
- name: Build unit tests
|
||||
run: ./gradlew assembleDebugUnitTest assembleReleaseUnitTest --stacktrace
|
||||
|
||||
- name: Build instrumentation tests
|
||||
run: ./gradlew assembleAndroidTest --stacktrace
|
||||
|
||||
unit-test:
|
||||
name: "Run all unit tests"
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout project
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
|
||||
- name: Load build outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: build
|
||||
key: build-${{ github.sha }}
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: ./gradlew test --stacktrace
|
||||
|
||||
- name: Run Linter
|
||||
run: ./gradlew lint --stacktrace
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload reports
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Unit Test Reports
|
||||
path: build/reports
|
||||
if: failure()
|
||||
|
||||
instrumentation:
|
||||
name: "Testing on API ${{ matrix.api-level }} for ${{ matrix.target }}"
|
||||
needs: compile
|
||||
# macOS provided hardware-accelerated emulator
|
||||
# but now so does Linux
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
api-level: [ 15, 21, 24, 31 ]
|
||||
target: [ default, google_apis, google_apis_playstore ]
|
||||
exclude:
|
||||
- api-level: 15
|
||||
target: google_apis_playstore
|
||||
- api-level: 21
|
||||
target: google_apis_playstore
|
||||
- api-level: 24
|
||||
target: google_apis
|
||||
- api-level: 31
|
||||
target: google_apis
|
||||
|
||||
steps:
|
||||
- name: Checkout project
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
|
||||
- name: Load build outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: build
|
||||
key: build-${{ github.sha }}
|
||||
|
||||
- name: AVD cache
|
||||
uses: actions/cache@v4
|
||||
id: avd-cache
|
||||
with:
|
||||
path: |
|
||||
~/.android/avd/*
|
||||
~/.android/adb*
|
||||
key: avd-${{ matrix.api-level }}-${{ matrix.target }}-sd
|
||||
|
||||
- name: Set up JRE 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
java-package: 'jre'
|
||||
#cache: gradle
|
||||
|
||||
- name: Create AVD and generate snapshot for caching
|
||||
if: steps.avd-cache.outputs.cache-hit != 'true'
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: ${{ matrix.api-level >= 30 && 'x86_64' || 'x86' }}
|
||||
force-avd-creation: false
|
||||
sdcard-path-or-size: '64M'
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: false
|
||||
script: echo "Generated AVD snapshot for caching."
|
||||
|
||||
- name: Run Instrumented Tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: ${{ matrix.api-level >= 30 && 'x86_64' || 'x86' }}
|
||||
profile: Nexus 6
|
||||
force-avd-creation: false
|
||||
sdcard-path-or-size: '64M'
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: true
|
||||
script: adb logcat -c && adb logcat -f /sdcard/logcat.txt & JAVA_HOME="${JAVA_HOME_11_X64}" ./gradlew connectedCheck --stacktrace || ( adb pull /sdcard/logcat.txt build/reports/; exit 1 )
|
||||
|
||||
- name: Upload reports
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Instrument Test Reports API ${{ matrix.api-level }} ${{ matrix.target }}
|
||||
path: build/reports
|
||||
if: failure()
|
||||
|
||||
- name: Save successful debug APK
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Debug APK
|
||||
path: build/outputs/apk/debug/aprsdroid-debug.apk
|
||||
if: matrix.api-level == 31 && matrix.target == 'google_apis'
|
||||
|
|
@ -8,6 +8,8 @@ messages.
|
|||
|
||||
APRSdroid is Open Source Software written in Scala and licensed under the GPLv2.
|
||||
|
||||
master: [](../../actions/workflows/android.yml)
|
||||
|
||||
Quick links:
|
||||
|
||||
- [Google Play](https://play.google.com/store/apps/details?id=org.aprsdroid.app)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
package org.aprsdroid.app;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.closeTo;
|
||||
|
||||
import org.aprsdroid.app.testing.CoordinateMatcher;
|
||||
import org.junit.Test;
|
||||
|
||||
import scala.Tuple2;
|
||||
|
||||
public class CoordinateTest {
|
||||
// Reference data generated from https://www.pgc.umn.edu/apps/convert/
|
||||
private static final String providedNLatitude = "77° 15' 30\" N";
|
||||
private static final float expectedNLatitude = 77.258333f;
|
||||
private static final String providedELongitude = "164° 45' 15\" E";
|
||||
private static final float expectedELongitude = 164.754167f;
|
||||
private static final String providedSLatitude = "45° 30' 45\" S";
|
||||
private static final float expectedSLatitude = -45.5125f;
|
||||
private static final String providedWLongitude = "97° 20' 40\" W";
|
||||
private static final float expectedWLongitude = -97.344444f;
|
||||
|
||||
@Test
|
||||
public void givenLocationInNEHemisphere_whenFormattedAsDMSString_thenParseBackIntoDecimalValue() {
|
||||
Tuple2<String, String> actual = AprsPacket$.MODULE$.formatCoordinates(expectedNLatitude, expectedELongitude);
|
||||
float floatLatitude = CoordinateMatcher.matchLatitude(actual._1);
|
||||
float floatLongitude = CoordinateMatcher.matchLongitude(actual._2);
|
||||
assertThat("Latitude", (double) floatLatitude, closeTo((double) expectedNLatitude, 1e-7));
|
||||
assertThat("Longitude", (double) floatLongitude, closeTo((double) expectedELongitude, 1e-7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLocationInNWHemisphere_whenFormattedAsDMSString_thenParseBackIntoDecimalValue() {
|
||||
Tuple2<String, String> actual = AprsPacket$.MODULE$.formatCoordinates(expectedNLatitude, expectedWLongitude);
|
||||
float floatLatitude = CoordinateMatcher.matchLatitude(actual._1);
|
||||
float floatLongitude = CoordinateMatcher.matchLongitude(actual._2);
|
||||
assertThat("Latitude", (double) floatLatitude, closeTo((double) expectedNLatitude, 1e-7));
|
||||
assertThat("Longitude", (double) floatLongitude, closeTo((double) expectedWLongitude, 1e-7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLocationInSEHemisphere_whenFormattedAsDMSString_thenParseBackIntoDecimalValue() {
|
||||
Tuple2<String, String> actual = AprsPacket$.MODULE$.formatCoordinates(expectedSLatitude, expectedELongitude);
|
||||
float floatLatitude = CoordinateMatcher.matchLatitude(actual._1);
|
||||
float floatLongitude = CoordinateMatcher.matchLongitude(actual._2);
|
||||
assertThat("Latitude", (double) floatLatitude, closeTo((double) expectedSLatitude, 1e-7));
|
||||
assertThat("Longitude", (double) floatLongitude, closeTo((double) expectedELongitude, 1e-7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLocationInSWHemisphere_whenFormattedAsDMSString_thenParseBackIntoDecimalValue() {
|
||||
Tuple2<String, String> actual = AprsPacket$.MODULE$.formatCoordinates(expectedSLatitude, expectedWLongitude);
|
||||
float floatLatitude = CoordinateMatcher.matchLatitude(actual._1);
|
||||
float floatLongitude = CoordinateMatcher.matchLongitude(actual._2);
|
||||
assertThat("Latitude", (double) floatLatitude, closeTo((double) expectedSLatitude, 1e-7));
|
||||
assertThat("Longitude", (double) floatLongitude, closeTo((double) expectedWLongitude, 1e-7));
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@ package org.aprsdroid.app;
|
|||
|
||||
import android.content.Context;
|
||||
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
package org.aprsdroid.app;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
||||
import static androidx.test.espresso.action.ViewActions.typeText;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.RootMatchers.isDialog;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.core.AllOf.allOf;
|
||||
import static org.hamcrest.core.StringContains.containsString;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.aprsdroid.app.testing.SharedPreferencesRule;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FirstRunDialog {
|
||||
private final String pref_callsign = "callsign";
|
||||
private final String pref_passcode = "passcode";
|
||||
public ActivityScenarioRule activityRule = new ActivityScenarioRule<>(LogActivity.class);
|
||||
public SharedPreferencesRule prefsRule = new SharedPreferencesRule() {
|
||||
@Override
|
||||
protected void modifyPreferences(SharedPreferences preferences) {
|
||||
preferences.edit().clear().commit();
|
||||
}
|
||||
};
|
||||
@Rule
|
||||
public RuleChain rules = RuleChain.outerRule(prefsRule).around(activityRule);
|
||||
|
||||
@Test
|
||||
public void givenAFirstTimeRun_whenProvidedABadPasscode_ThenDialogStaysOpen() {
|
||||
onView(isRoot())
|
||||
.inRoot(isDialog())
|
||||
.check(matches(allOf(
|
||||
hasDescendant(withText(containsString("Welcome to APRSdroid"))),
|
||||
hasDescendant(withId(R.id.callsign)),
|
||||
hasDescendant(withId(R.id.passcode)))));
|
||||
onView(withId(R.id.callsign))
|
||||
.check(matches(withHint(containsString("Callsign"))))
|
||||
.perform(typeText("XA1AAA"), closeSoftKeyboard());
|
||||
onView(withId(R.id.passcode))
|
||||
.check(matches(withHint(containsString("Passcode"))))
|
||||
.perform(typeText("12345"), closeSoftKeyboard());
|
||||
onView(withId(android.R.id.button1)).perform(click()); // OK Button
|
||||
onView(isRoot())
|
||||
.inRoot(isDialog())
|
||||
.check(matches(allOf(
|
||||
hasDescendant(withText(containsString("Welcome to APRSdroid"))),
|
||||
hasDescendant(withId(R.id.callsign)),
|
||||
hasDescendant(withId(R.id.passcode)))));
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFirstTimeRun_whenProvidedAGoodPasscode_ThenDialogCloses() {
|
||||
onView(isRoot())
|
||||
.inRoot(isDialog())
|
||||
.check(matches(allOf(
|
||||
hasDescendant(withText(containsString("Welcome to APRSdroid"))),
|
||||
hasDescendant(withId(R.id.callsign)),
|
||||
hasDescendant(withId(R.id.passcode)))));
|
||||
onView(withId(R.id.callsign))
|
||||
.check(matches(withHint(containsString("Callsign"))))
|
||||
.perform(typeText("XA1AAA"), closeSoftKeyboard());
|
||||
onView(withId(R.id.passcode))
|
||||
.check(matches(withHint(containsString("Passcode"))))
|
||||
.perform(typeText("23459"), closeSoftKeyboard());
|
||||
onView(withId(android.R.id.button1)).perform(click()); // OK Button
|
||||
onView(allOf(
|
||||
isRoot(),
|
||||
hasDescendant(withText(containsString("Welcome to APRSdroid"))),
|
||||
hasDescendant(withId(R.id.callsign)),
|
||||
hasDescendant(withId(R.id.passcode))))
|
||||
.check(doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAFirstTimeRun_whenProvidedAGoodPasscode_ThenPrefsSaved() {
|
||||
String expected_callsign = "XA1AAA";
|
||||
String expected_passcode = "23459";
|
||||
SharedPreferences prefs = prefsRule.getPreferences();
|
||||
Assert.assertNull("Callsign", prefs.getString(pref_callsign, null));
|
||||
Assert.assertNull("Passcode", prefs.getString(pref_passcode, null));
|
||||
onView(withId(R.id.callsign))
|
||||
.check(matches(withHint(containsString("Callsign"))))
|
||||
.perform(typeText(expected_callsign), closeSoftKeyboard());
|
||||
onView(withId(R.id.passcode))
|
||||
.check(matches(withHint(containsString("Passcode"))))
|
||||
.perform(typeText(expected_passcode), closeSoftKeyboard());
|
||||
onView(withId(android.R.id.button1)).perform(click()); // OK Button
|
||||
Assert.assertEquals("Callsign", expected_callsign, prefs.getString(pref_callsign, null));
|
||||
Assert.assertEquals("Passcode", expected_passcode, prefs.getString(pref_passcode, null));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,464 @@
|
|||
package org.aprsdroid.app;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.closeTo;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.espresso.action.GeneralLocation;
|
||||
import androidx.test.espresso.action.GeneralSwipeAction;
|
||||
import androidx.test.espresso.action.Press;
|
||||
import androidx.test.espresso.action.Swipe;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.FlakyTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GoogleApiAvailability;
|
||||
|
||||
import org.aprsdroid.app.testing.DMSLocationAssertion;
|
||||
import org.aprsdroid.app.testing.SharedPreferencesRule;
|
||||
import org.aprsdroid.app.testing.SpecificDMSLocationAssertion;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.runners.Enclosed;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(Enclosed.class)
|
||||
public class MapModeTest {
|
||||
private static final String TAG = "APRSdroid-MapModeTest";
|
||||
private static final Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
private static final ActivityScenarioRule<GoogleMapAct> activityRule =
|
||||
new ActivityScenarioRule<>(new Intent(appContext, GoogleMapAct.class)
|
||||
.putExtra("info", R.string.p_source_from_map_save));
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public static class GivenDefaultHomeLocation {
|
||||
private final SharedPreferencesRule prefsRule = new SharedPreferencesRule() {
|
||||
@Override
|
||||
protected void modifyPreferences(SharedPreferences preferences) {
|
||||
preferences.edit().clear().commit();
|
||||
}
|
||||
};
|
||||
|
||||
@Rule
|
||||
public final RuleChain rules = RuleChain.outerRule(prefsRule).around(activityRule);
|
||||
|
||||
@Test
|
||||
public void whenFirstLoaded_thenSaveDisabled() {
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(withText("")));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(not(isEnabled())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMapIsDragged_thenPositionAndButtonShown() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(not(withText(""))));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(isEnabled()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMapIsDragged_thenPositionIsValidCoordinates() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(new DMSLocationAssertion());
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public static class GivenSavedLocationInNEHemisphere {
|
||||
private static final float expectedLatitude = 37.50123f;
|
||||
private static final float expectedLongitude = 88.25034f;
|
||||
private static final float expectedZoom = 4.0f;
|
||||
|
||||
private final SharedPreferencesRule prefsRule = new SharedPreferencesRule() {
|
||||
@Override
|
||||
protected void modifyPreferences(SharedPreferences preferences) {
|
||||
preferences.edit()
|
||||
.putFloat("map_lat", expectedLatitude)
|
||||
.putFloat("map_lon", expectedLongitude)
|
||||
.putFloat("map_zoom", expectedZoom)
|
||||
.commit();
|
||||
}
|
||||
};
|
||||
|
||||
@Rule
|
||||
public final RuleChain rules = RuleChain.outerRule(prefsRule).around(activityRule);
|
||||
|
||||
@Test
|
||||
public void whenFirstLoaded_thenSaveDisabled() {
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(withText("")));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(not(isEnabled())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMapIsDragged_thenPositionAndButtonShown() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(not(withText(""))));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(isEnabled()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
public void whenMapIsDraggedBackAndForth_thenPositionIsOriginalCoordinates() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER_LEFT,
|
||||
GeneralLocation.CENTER, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(new SpecificDMSLocationAssertion(expectedLatitude, expectedLongitude));
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
public void whenMapIsDraggedBackAndForthAndSaved_thenPositionIsSavedCorrectly() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException ex) {
|
||||
Log.w(TAG, "Sleep was interrupted: " + ex);
|
||||
}
|
||||
prefsRule.getPreferences()
|
||||
.edit()
|
||||
.remove("map_lat")
|
||||
.remove("map_lon")
|
||||
.remove("map_zoom")
|
||||
.commit();
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER_LEFT,
|
||||
GeneralLocation.CENTER, Press.THUMB));
|
||||
onView(withId(R.id.accept))
|
||||
.perform(click());
|
||||
float actualLatitude = prefsRule.getPreferences().getFloat("map_lat", 0.0f);
|
||||
float actualLongitude = prefsRule.getPreferences().getFloat("map_lon", 0.0f);
|
||||
float actualZoom = prefsRule.getPreferences().getFloat("map_zoom", 0.0f);
|
||||
assertThat("Latitude", (double) actualLatitude, closeTo(expectedLatitude, 5e-2));
|
||||
assertThat("Longitude", (double) actualLongitude, closeTo(expectedLongitude, 5e-2));
|
||||
assertThat("Zoom", (double) actualZoom, closeTo(expectedZoom, 1e-7));
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public static class GivenSavedLocationInSEHemisphere {
|
||||
private static final float expectedLatitude = -37.50123f;
|
||||
private static final float expectedLongitude = 88.25034f;
|
||||
private static final float expectedZoom = 4.5f;
|
||||
|
||||
private final SharedPreferencesRule prefsRule = new SharedPreferencesRule() {
|
||||
@Override
|
||||
protected void modifyPreferences(SharedPreferences preferences) {
|
||||
preferences.edit()
|
||||
.putFloat("map_lat", expectedLatitude)
|
||||
.putFloat("map_lon", expectedLongitude)
|
||||
.putFloat("map_zoom", expectedZoom)
|
||||
.commit();
|
||||
}
|
||||
};
|
||||
|
||||
@Rule
|
||||
public final RuleChain rules = RuleChain.outerRule(prefsRule).around(activityRule);
|
||||
|
||||
@Test
|
||||
public void whenFirstLoaded_thenSaveDisabled() {
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(withText("")));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(not(isEnabled())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMapIsDragged_thenPositionAndButtonShown() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(not(withText(""))));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(isEnabled()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
public void whenMapIsDraggedBackAndForth_thenPositionIsOriginalCoordinates() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER_LEFT,
|
||||
GeneralLocation.CENTER, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(new SpecificDMSLocationAssertion(expectedLatitude, expectedLongitude));
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
public void whenMapIsDraggedBackAndForthAndSaved_thenPositionIsSavedCorrectly() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException ex) {
|
||||
Log.w(TAG, "Sleep was interrupted: " + ex);
|
||||
}
|
||||
prefsRule.getPreferences()
|
||||
.edit()
|
||||
.remove("map_lat")
|
||||
.remove("map_lon")
|
||||
.remove("map_zoom")
|
||||
.commit();
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER_LEFT,
|
||||
GeneralLocation.CENTER, Press.THUMB));
|
||||
onView(withId(R.id.accept))
|
||||
.perform(click());
|
||||
float actualLatitude = prefsRule.getPreferences().getFloat("map_lat", 0.0f);
|
||||
float actualLongitude = prefsRule.getPreferences().getFloat("map_lon", 0.0f);
|
||||
float actualZoom = prefsRule.getPreferences().getFloat("map_zoom", 0.0f);
|
||||
assertThat("Latitude", (double) actualLatitude, closeTo(expectedLatitude, 5e-2));
|
||||
assertThat("Longitude", (double) actualLongitude, closeTo(expectedLongitude, 5e-2));
|
||||
assertThat("Zoom", (double) actualZoom, closeTo(expectedZoom, 1e-7));
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public static class GivenSavedLocationInNWHemisphere {
|
||||
private static final float expectedLatitude = 37.50123f;
|
||||
private static final float expectedLongitude = -88.25034f;
|
||||
private static final float expectedZoom = 5.0f;
|
||||
|
||||
private final SharedPreferencesRule prefsRule = new SharedPreferencesRule() {
|
||||
@Override
|
||||
protected void modifyPreferences(SharedPreferences preferences) {
|
||||
preferences.edit()
|
||||
.putFloat("map_lat", expectedLatitude)
|
||||
.putFloat("map_lon", expectedLongitude)
|
||||
.putFloat("map_zoom", expectedZoom)
|
||||
.commit();
|
||||
}
|
||||
};
|
||||
|
||||
@Rule
|
||||
public final RuleChain rules = RuleChain.outerRule(prefsRule).around(activityRule);
|
||||
|
||||
@Test
|
||||
public void whenFirstLoaded_thenSaveDisabled() {
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(withText("")));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(not(isEnabled())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMapIsDragged_thenPositionAndButtonShown() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(not(withText(""))));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(isEnabled()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
public void whenMapIsDraggedBackAndForth_thenPositionIsOriginalCoordinates() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER_LEFT,
|
||||
GeneralLocation.CENTER, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(new SpecificDMSLocationAssertion(expectedLatitude, expectedLongitude));
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
public void whenMapIsDraggedBackAndForthAndSaved_thenPositionIsSavedCorrectly() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException ex) {
|
||||
Log.w(TAG, "Sleep was interrupted: " + ex);
|
||||
}
|
||||
prefsRule.getPreferences()
|
||||
.edit()
|
||||
.remove("map_lat")
|
||||
.remove("map_lon")
|
||||
.remove("map_zoom")
|
||||
.commit();
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER_LEFT,
|
||||
GeneralLocation.CENTER, Press.THUMB));
|
||||
onView(withId(R.id.accept))
|
||||
.perform(click());
|
||||
float actualLatitude = prefsRule.getPreferences().getFloat("map_lat", 0.0f);
|
||||
float actualLongitude = prefsRule.getPreferences().getFloat("map_lon", 0.0f);
|
||||
float actualZoom = prefsRule.getPreferences().getFloat("map_zoom", 0.0f);
|
||||
assertThat("Latitude", (double) actualLatitude, closeTo(expectedLatitude, 5e-2));
|
||||
assertThat("Longitude", (double) actualLongitude, closeTo(expectedLongitude, 5e-2));
|
||||
assertThat("Zoom", (double) actualZoom, closeTo(expectedZoom, 1e-7));
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public static class GivenSavedLocationInSWHemisphere {
|
||||
private static final float expectedLatitude = -37.50123f;
|
||||
private static final float expectedLongitude = -88.25034f;
|
||||
private static final float expectedZoom = 5.5f;
|
||||
|
||||
private final SharedPreferencesRule prefsRule = new SharedPreferencesRule() {
|
||||
@Override
|
||||
protected void modifyPreferences(SharedPreferences preferences) {
|
||||
preferences.edit()
|
||||
.putFloat("map_lat", expectedLatitude)
|
||||
.putFloat("map_lon", expectedLongitude)
|
||||
.putFloat("map_zoom", expectedZoom)
|
||||
.commit();
|
||||
}
|
||||
};
|
||||
|
||||
@Rule
|
||||
public final RuleChain rules = RuleChain.outerRule(prefsRule).around(activityRule);
|
||||
|
||||
@Test
|
||||
public void whenFirstLoaded_thenSaveDisabled() {
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(withText("")));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(not(isEnabled())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMapIsDragged_thenPositionAndButtonShown() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(matches(not(withText(""))));
|
||||
onView(withId(R.id.accept))
|
||||
.check(matches(isEnabled()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
public void whenMapIsDraggedBackAndForth_thenPositionIsOriginalCoordinates() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER_LEFT,
|
||||
GeneralLocation.CENTER, Press.THUMB));
|
||||
onView(withId(R.id.info))
|
||||
.check(new SpecificDMSLocationAssertion(expectedLatitude, expectedLongitude));
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
public void whenMapIsDraggedBackAndForthAndSaved_thenPositionIsSavedCorrectly() {
|
||||
Assume.assumeThat("Google Play Services requires",
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(appContext),
|
||||
equalTo(ConnectionResult.SUCCESS));
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException ex) {
|
||||
Log.w(TAG, "Sleep was interrupted: " + ex);
|
||||
}
|
||||
prefsRule.getPreferences()
|
||||
.edit()
|
||||
.remove("map_lat")
|
||||
.remove("map_lon")
|
||||
.remove("map_zoom")
|
||||
.commit();
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER,
|
||||
GeneralLocation.CENTER_LEFT, Press.THUMB));
|
||||
onView(withId(R.id.mapview))
|
||||
.perform(new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.CENTER_LEFT,
|
||||
GeneralLocation.CENTER, Press.THUMB));
|
||||
onView(withId(R.id.accept))
|
||||
.perform(click());
|
||||
float actualLatitude = prefsRule.getPreferences().getFloat("map_lat", 0.0f);
|
||||
float actualLongitude = prefsRule.getPreferences().getFloat("map_lon", 0.0f);
|
||||
float actualZoom = prefsRule.getPreferences().getFloat("map_zoom", 0.0f);
|
||||
assertThat("Latitude", (double) actualLatitude, closeTo(expectedLatitude, 5e-2));
|
||||
assertThat("Longitude", (double) actualLongitude, closeTo(expectedLongitude, 5e-2));
|
||||
assertThat("Zoom", (double) actualZoom, closeTo(expectedZoom, 1e-7));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.aprsdroid.app.testing;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.espresso.NoMatchingViewException;
|
||||
import androidx.test.espresso.ViewAssertion;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
public class DMSLocationAssertion implements ViewAssertion {
|
||||
protected void checkCoordinates(float latitude, float longitude) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(View view, NoMatchingViewException noViewFoundException) {
|
||||
if (view == null)
|
||||
throw noViewFoundException;
|
||||
Assert.assertThat(view, instanceOf(TextView.class));
|
||||
TextView text = (TextView) view;
|
||||
float latitude = CoordinateMatcher.matchLatitude(text.getText());
|
||||
float longitude = CoordinateMatcher.matchLongitude(text.getText());
|
||||
checkCoordinates(latitude, longitude);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package org.aprsdroid.app.testing;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
public abstract class SharedPreferencesRule implements TestRule {
|
||||
private SharedPreferences preferences;
|
||||
|
||||
protected abstract void modifyPreferences(SharedPreferences preferences);
|
||||
|
||||
public SharedPreferences getPreferences() {
|
||||
return preferences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(appContext);
|
||||
modifyPreferences(preferences);
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package org.aprsdroid.app.testing;
|
||||
|
||||
import static org.hamcrest.Matchers.closeTo;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
public class SpecificDMSLocationAssertion extends DMSLocationAssertion {
|
||||
private final float expectedLatitude;
|
||||
private final float expectedLongitude;
|
||||
|
||||
public SpecificDMSLocationAssertion(float myExpectedLatitude, float myExpectedLongitude) {
|
||||
expectedLatitude = myExpectedLatitude;
|
||||
expectedLongitude = myExpectedLongitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkCoordinates(float latitude, float longitude) {
|
||||
super.checkCoordinates(latitude, longitude);
|
||||
Assert.assertThat("Latitude", (double) latitude, closeTo((double) expectedLatitude, 0.05));
|
||||
Assert.assertThat("Longitude", (double) longitude, closeTo((double) expectedLongitude, 0.05));
|
||||
}
|
||||
}
|
||||
19
build.gradle
19
build.gradle
|
|
@ -6,7 +6,7 @@ buildscript {
|
|||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
classpath 'com.android.tools.build:gradle:3.5.4'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ android {
|
|||
|
||||
resValue "string", "google_maps_key", mapsApiKey()
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
}
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
compileOptions {
|
||||
|
|
@ -131,10 +131,10 @@ android {
|
|||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
androidTest {
|
||||
java.srcDirs = ['androidTest/java']
|
||||
java.srcDirs = ['androidTest/java', 'sharedTest/java']
|
||||
}
|
||||
test {
|
||||
java.srcDirs = ['test/java']
|
||||
java.srcDirs = ['test/java', 'sharedTest/java']
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
|
|
@ -158,7 +158,12 @@ dependencies {
|
|||
|
||||
|
||||
implementation 'com.squareup.okio:okio:2.1.0'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
|
||||
testImplementation 'junit:junit:4.13.1'
|
||||
testImplementation 'org.hamcrest:hamcrest-core:1.3'
|
||||
testImplementation 'org.hamcrest:hamcrest-library:1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
package org.aprsdroid.app.testing;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CoordinateMatcher {
|
||||
private static final String NUMBER = "(-?\\d+(?:\\.\\d*)?)";
|
||||
private static final String dms_latitude_pattern = NUMBER + "°\\s*" + NUMBER + "'\\s*" + NUMBER + "\"\\s*([NS])";
|
||||
private static final String dms_longitude_pattern = NUMBER + "°\\s*" + NUMBER + "'\\s*" + NUMBER + "\"\\s*([EW])";
|
||||
private static final Pattern dms_latitude_regex = Pattern.compile(dms_latitude_pattern, Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern dms_longitude_regex = Pattern.compile(dms_longitude_pattern, Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static float convertField(CharSequence string, Pattern regex, String name) {
|
||||
Matcher matcher = regex.matcher(string);
|
||||
Assert.assertTrue(name + " not found", matcher.find());
|
||||
float value = 0;
|
||||
try {
|
||||
int degrees = Integer.parseInt(Objects.requireNonNull(matcher.group(1)));
|
||||
Assert.assertThat(name + " degrees", degrees, greaterThanOrEqualTo(0));
|
||||
value = (float) degrees;
|
||||
} catch (NumberFormatException ex) {
|
||||
Assert.fail(name + " degree field not an integer");
|
||||
}
|
||||
try {
|
||||
int minutes = Integer.parseInt(Objects.requireNonNull(matcher.group(2)));
|
||||
Assert.assertThat(name + " minutes", minutes, greaterThanOrEqualTo(0));
|
||||
value += (float) minutes / 60.0f;
|
||||
} catch (NumberFormatException ex) {
|
||||
Assert.fail(name + " minute field not an integer");
|
||||
}
|
||||
try {
|
||||
float seconds = Float.parseFloat(Objects.requireNonNull(matcher.group(3)));
|
||||
Assert.assertThat(name + " seconds", seconds, greaterThanOrEqualTo(0.0f));
|
||||
value += seconds / 3600.0f;
|
||||
} catch (NumberFormatException ex) {
|
||||
Assert.fail(name + " seconds field not an number");
|
||||
}
|
||||
String direction = Objects.requireNonNull(matcher.group(4));
|
||||
if (direction.equalsIgnoreCase("S") || direction.equalsIgnoreCase("W")) {
|
||||
value *= -1.0f;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static float matchLatitude(CharSequence string) {
|
||||
return convertField(string, dms_latitude_regex, "Latitude");
|
||||
}
|
||||
|
||||
public static float matchLongitude(CharSequence string) {
|
||||
return convertField(string, dms_longitude_regex, "Longitude");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package org.aprsdroid.app;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.closeTo;
|
||||
|
||||
import org.aprsdroid.app.testing.CoordinateMatcher;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CoordinateTest {
|
||||
// Reference data generated from https://www.pgc.umn.edu/apps/convert/
|
||||
private static final String providedNLatitude = "77° 15' 30\" N";
|
||||
private static final float expectedNLatitude = 77.258333f;
|
||||
private static final String providedELongitude = "164° 45' 15\" E";
|
||||
private static final float expectedELongitude = 164.754167f;
|
||||
private static final String providedSLatitude = "45° 30' 45\" S";
|
||||
private static final float expectedSLatitude = -45.5125f;
|
||||
private static final String providedWLongitude = "97° 20' 40\" W";
|
||||
private static final float expectedWLongitude = -97.344444f;
|
||||
|
||||
@Test
|
||||
public void givenDMSLatitudeInN_whenParsingString_ThenShouldMatchDecimal() {
|
||||
float value = CoordinateMatcher.matchLatitude(providedNLatitude);
|
||||
assertThat("Latitude", (double) value, closeTo((double) expectedNLatitude, 1e-7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenDMSLongitudeInE_whenParsingString_ThenShouldMatchDecimal() {
|
||||
float value = CoordinateMatcher.matchLongitude(providedELongitude);
|
||||
assertThat("Longitude", (double) value, closeTo((double) expectedELongitude, 1e-7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenDMSLatitudeInS_whenParsingString_ThenShouldMatchDecimal() {
|
||||
float value = CoordinateMatcher.matchLatitude(providedSLatitude);
|
||||
assertThat("Latitude", (double) value, closeTo((double) expectedSLatitude, 1e-7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenDMSLongitudeInW_whenParsingString_ThenShouldMatchDecimal() {
|
||||
float value = CoordinateMatcher.matchLongitude(providedWLongitude);
|
||||
assertThat("Longitude", (double) value, closeTo((double) expectedWLongitude, 1e-7));
|
||||
}
|
||||
}
|
||||
Ładowanie…
Reference in New Issue