From 751683dd97a33893a510250c07382163997ee09c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Guti=C3=A9rrez=20Alfaro?=
Date: Mon, 19 Jan 2026 19:40:08 -0600
Subject: [PATCH 1/4] Refactor Note List, add unit test
---
app/src/main/AndroidManifest.xml | 1 +
.../net/osmtracker/activity/NoteList.java | 245 +++++++-----------
.../net/osmtracker/adapter/NoteAdapter.java | 122 +++++++++
.../java/net/osmtracker/db/DataHelper.java | 4 +-
.../net/osmtracker/db/NoteListAdapter.java | 100 -------
.../osmtracker/db/TrackContentProvider.java | 28 ++
app/src/main/res/layout/notelist.xml | 8 +
app/src/main/res/layout/notelist_item.xml | 104 ++++----
.../net/osmtracker/db/DataHelperNoteTest.java | 68 +++++
.../db/model/OSMVisibilityTest.java | 2 +-
.../{test => }/db/model/TrackTest.java | 2 +-
11 files changed, 374 insertions(+), 310 deletions(-)
create mode 100644 app/src/main/java/net/osmtracker/adapter/NoteAdapter.java
delete mode 100644 app/src/main/java/net/osmtracker/db/NoteListAdapter.java
create mode 100644 app/src/main/res/layout/notelist.xml
create mode 100644 app/src/test/java/net/osmtracker/db/DataHelperNoteTest.java
rename app/src/test/java/net/osmtracker/{test => }/db/model/OSMVisibilityTest.java (98%)
rename app/src/test/java/net/osmtracker/{test => }/db/model/TrackTest.java (98%)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 97f6435b6..397e7e289 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -60,6 +60,7 @@
android:label="@string/wplist" />
{
+ String newName = editNoteName.getText().toString();
+ dataHelper.updateNote(trackId, uuid, newName);
+ refreshData();
+ alert.dismiss();
});
- // Delete waypoint
- buttonDelete.setOnClickListener(new EditNoteDialogOnClickListener(alert, cursor) {
- @Override
- public void onClick(View view) {
- new AlertDialog.Builder(NoteList.this)
- .setTitle(getString(R.string.delete_note_confirm_dialog_title))
- .setMessage(getString(R.string.delete_note_confirm_dialog_msg))
- .setPositiveButton(getString(R.string.delete_note_confirm_bt_ok), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dataHelper.deleteNote(uuid);
- cursor.requery();
- alert.dismiss();
- dialog.dismiss();
- }
- })
- .setNegativeButton(getString(R.string.delete_note_confirm_bt_cancel), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- })
- .show();
- }
- });
+ // Delete note
+ buttonDelete.setOnClickListener(v -> new AlertDialog.Builder(this)
+ .setTitle(R.string.delete_note_confirm_dialog_title)
+ .setMessage(R.string.delete_note_confirm_dialog_msg)
+ .setPositiveButton(R.string.delete_note_confirm_bt_ok, (dialog, which) -> {
+ dataHelper.deleteNote(uuid);
+ refreshData();
+ alert.dismiss();
+ })
+ .setNegativeButton(R.string.delete_note_confirm_bt_cancel,
+ (dialog, which) -> dialog.dismiss())
+ .show());
// Upload note text to OpenStreetMap
- buttonOSMUpload.setOnClickListener(new EditNoteDialogOnClickListener(alert, null) {
- @Override
- public void onClick(View view) {
- uploadNoteToOSM(cursor);
- }
+ buttonOSMUpload.setOnClickListener(v -> {
+ uploadNoteToOSM(noteId);
+ alert.dismiss();
});
// Cancel button
- buttonCancel.setOnClickListener(new EditWaypointDialogOnClickListener(alert, null) {
- @Override
- public void onClick(View view) {
- alert.dismiss();
- }
- });
+ buttonCancel.setOnClickListener(v -> alert.dismiss());
alert.setView(editNoteDialog);
alert.show();
-
- super.onListItemClick(l, v, position, id);
}
- // Where the menu items get defined
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- getMenuInflater().inflate(R.menu.note_contextmenu, menu);
- }
-
- // What happens when a menu item is selected
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
- final Cursor cursor = ((CursorAdapter) getListAdapter()).getCursor();
- if (!cursor.moveToPosition(info.position)) return super.onContextItemSelected(item);
-
- // Menu options when you long press on a note
- switch (item.getItemId()) {
- case R.id.notelist_contextmenu_osm_note_upload:
- uploadNoteToOSM(cursor);
- return true;
- }
- return super.onContextItemSelected(item);
- }
-
/**
- * Extracts note data from the cursor and launches the OSM Note Upload activity.
- * @param cursor The cursor positioned at the selected note.
+ * Extracts note data from DB and launches the OSM Note Upload activity.
*/
- private void uploadNoteToOSM(Cursor cursor) {
- long noteId = cursor.getLong(cursor.getColumnIndex(TrackContentProvider.Schema.COL_ID));
- String noteText = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_NAME));
- double lat = cursor.getDouble(cursor.getColumnIndex(TrackContentProvider.Schema.COL_LATITUDE));
- double lon = cursor.getDouble(cursor.getColumnIndex(TrackContentProvider.Schema.COL_LONGITUDE));
-
- Intent intent = new Intent(this, OpenStreetMapNotesUpload.class);
- intent.putExtra("noteId", noteId);
- intent.putExtra("noteContent", noteText);
- intent.putExtra("appName", getString(R.string.app_name));
- intent.putExtra("latitude", lat);
- intent.putExtra("longitude", lon);
-
- // Retrieve app version number
- try {
- PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
- intent.putExtra("version", pi.versionName);
- } catch (PackageManager.NameNotFoundException e) {
- // Log error or ignore
- }
+ private void uploadNoteToOSM(long noteId) {
+ // Query the specific note to get latest Lat/Lon
+ Cursor cursor = getContentResolver().query(
+ TrackContentProvider.noteUri(noteId),
+ null,null,null,null);
+
+ if (cursor != null && cursor.moveToFirst()) {
+ String noteText = cursor.getString(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_NAME));
+ double lat = cursor.getDouble(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_LATITUDE));
+ double lon = cursor.getDouble(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_LONGITUDE));
+
+ Intent intent = new Intent(this, OpenStreetMapNotesUpload.class);
+ intent.putExtra("noteId", noteId);
+ intent.putExtra("noteContent", noteText);
+ intent.putExtra("appName", getString(R.string.app_name));
+ intent.putExtra("latitude", lat);
+ intent.putExtra("longitude", lon);
+
+ // Retrieve app version number
+ try {
+ PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
+ intent.putExtra("version", pi.versionName);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore
+ Log.d(TAG, "Package name not found", e);
+ }
- startActivity(intent);
+ cursor.close();
+ startActivity(intent);
+ }
}
}
diff --git a/app/src/main/java/net/osmtracker/adapter/NoteAdapter.java b/app/src/main/java/net/osmtracker/adapter/NoteAdapter.java
new file mode 100644
index 000000000..896c8dadc
--- /dev/null
+++ b/app/src/main/java/net/osmtracker/adapter/NoteAdapter.java
@@ -0,0 +1,122 @@
+package net.osmtracker.adapter;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.osmtracker.R;
+import net.osmtracker.db.TrackContentProvider;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class NoteAdapter extends RecyclerView.Adapter {
+
+ public static final SimpleDateFormat DATE_FORMATTER =
+ new SimpleDateFormat("HH:mm:ss 'UTC'", Locale.ROOT);
+ static {
+ DATE_FORMATTER.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ private Cursor cursor;
+ private final OnNoteClickListener listener;
+
+ public interface OnNoteClickListener {
+ void onNoteClick(long id, long noteId, String uuid, String name);
+ }
+
+ public NoteAdapter(OnNoteClickListener listener) {
+ this.listener = listener;
+ }
+
+ public void swapCursor(Cursor newCursor) {
+ if (cursor == newCursor) return;
+ if (cursor != null) cursor.close();
+ cursor = newCursor;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public NoteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(
+ parent.getContext()).inflate(R.layout.notelist_item, parent, false);
+ return new NoteViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull NoteViewHolder holder, int position) {
+ if (cursor.moveToPosition(position)) {
+ Context context = holder.itemView.getContext();
+
+ // Bind name
+ String name = cursor.getString(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_NAME));
+ holder.tvName.setText(name);
+
+ // Upload status
+ if (cursor.isNull(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_OSM_UPLOAD_DATE))) {
+ holder.ivUploadStatus.setVisibility(View.GONE);
+ } else {
+ holder.ivUploadStatus.setImageResource(android.R.drawable.stat_sys_upload_done);
+ holder.ivUploadStatus.setVisibility(View.VISIBLE);
+ }
+
+ //Bind Location (Latitude/Longitude)
+ String lat = cursor.getString(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_LATITUDE));
+ String lon = cursor.getString(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_LONGITUDE));
+
+ String locationStr = context.getString(R.string.wplist_latitude) + lat + ", " +
+ context.getString(R.string.wplist_longitude) + lon;
+ holder.tvLocation.setText(locationStr);
+
+ // Bind Timestamp
+ Date ts = new Date(cursor.getLong(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_TIMESTAMP)));
+ holder.tvTimestamp.setText(DATE_FORMATTER.format(ts));
+
+ // Setup Click Listener
+ String uuid = cursor.getString(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_UUID));
+ long trackId = cursor.getLong(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_TRACK_ID));
+ long noteId = cursor.getLong(
+ cursor.getColumnIndexOrThrow(TrackContentProvider.Schema.COL_ID));
+ holder.itemView.setOnClickListener(
+ v -> listener.onNoteClick(trackId, noteId, uuid, name));
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return (cursor == null) ? 0 : cursor.getCount();
+ }
+
+ public static class NoteViewHolder extends RecyclerView.ViewHolder {
+ TextView tvName;
+ ImageView ivUploadStatus;
+ TextView tvLocation;
+ TextView tvTimestamp;
+
+
+ public NoteViewHolder(View v) {
+ super(v);
+ tvName = v.findViewById(R.id.notelist_item_name);
+ ivUploadStatus = v.findViewById(R.id.notelist_item_upload_status_icon);
+ tvLocation = v.findViewById(R.id.notelist_item_location);
+ tvTimestamp = v.findViewById(R.id.notelist_item_timestamp);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/osmtracker/db/DataHelper.java b/app/src/main/java/net/osmtracker/db/DataHelper.java
index 68f8f85fc..37935bec1 100644
--- a/app/src/main/java/net/osmtracker/db/DataHelper.java
+++ b/app/src/main/java/net/osmtracker/db/DataHelper.java
@@ -349,7 +349,7 @@ public void trackNote(long trackId, Location location, String name, String uuid)
* Updates a note
*
* @param trackId Id of the track
- * @param uuid Unique ID of the target waypoint
+ * @param uuid Unique ID of the target note
* @param name New text value for the note
*/
public void updateNote(long trackId, String uuid, String name) {
@@ -374,7 +374,7 @@ public void updateNote(long trackId, String uuid, String name) {
public void deleteNote(String uuid) {
Log.v(TAG, "Deleting note with uuid '" + uuid);
if (uuid != null) {
- contentResolver.delete(Uri.withAppendedPath(TrackContentProvider.CONTENT_URI_WAYPOINT_UUID, uuid), null, null);
+ contentResolver.delete(Uri.withAppendedPath(TrackContentProvider.CONTENT_URI_NOTE_UUID, uuid), null, null);
}
}
diff --git a/app/src/main/java/net/osmtracker/db/NoteListAdapter.java b/app/src/main/java/net/osmtracker/db/NoteListAdapter.java
deleted file mode 100644
index 17e83713a..000000000
--- a/app/src/main/java/net/osmtracker/db/NoteListAdapter.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package net.osmtracker.db;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CursorAdapter;
-import android.widget.ImageView;
-import android.widget.TableLayout;
-import android.widget.TextView;
-
-import net.osmtracker.R;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.TimeZone;
-
-/**
- * Adapter for the note list. Gets notes from database.
- *
- */
-public class NoteListAdapter extends CursorAdapter {
-
- /**
- * Date formatter
- */
- public static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("HH:mm:ss 'UTC'");
- static {
- DATE_FORMATTER.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- /**
- * Constructor.
- *
- * @param context
- * Application context
- * @param c
- * {@link Cursor} to data
- */
- public NoteListAdapter(Context context, Cursor c) {
- super(context, c);
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- TableLayout tl = (TableLayout) view;
- bind(cursor, tl, context);
- }
-
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup vg) {
- TableLayout tl = (TableLayout) LayoutInflater.from(vg.getContext()).inflate(R.layout.notelist_item,
- vg, false);
- return bind(cursor, tl, context);
- }
-
- /**
- * Do the binding between data and item view.
- *
- * @param cursor Cursor to pull data
- * @param tl RelativeView representing one item
- * @param context Context, to get resources
- * @return The relative view with data bound.
- */
- private View bind(Cursor cursor, TableLayout tl, Context context) {
- TextView vName = tl.findViewById(R.id.notelist_item_name);
- ImageView vUploadStatus = tl.findViewById(R.id.notelist_item_upload_status_icon);
- TextView vLocation = tl.findViewById(R.id.notelist_item_location);
- TextView vTimestamp = tl.findViewById(R.id.notelist_item_timestamp);
-
- // Bind name
- String name = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_NAME));
- vName.setText(name);
-
- // Upload status
- if (cursor.isNull(cursor.getColumnIndex(TrackContentProvider.Schema.COL_OSM_UPLOAD_DATE))) {
- vUploadStatus.setVisibility(View.GONE);
- }
- else{
- vUploadStatus.setImageResource(android.R.drawable.stat_sys_upload_done);
- vUploadStatus.setVisibility(View.VISIBLE);
- }
-
- // Bind location
- StringBuffer locationAsString = new StringBuffer();
- locationAsString.append(context.getResources().getString(R.string.wplist_latitude)
- + cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_LATITUDE)));
- locationAsString.append(", " + context.getResources().getString(R.string.wplist_longitude)
- + cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_LONGITUDE)));
-
- vLocation.setText(locationAsString.toString());
-
- // Bind timestamp
- Date ts = new Date(cursor.getLong(cursor.getColumnIndex(TrackContentProvider.Schema.COL_TIMESTAMP)));
- vTimestamp.setText(DATE_FORMATTER.format(ts));
- return tl;
- }
-
-}
diff --git a/app/src/main/java/net/osmtracker/db/TrackContentProvider.java b/app/src/main/java/net/osmtracker/db/TrackContentProvider.java
index 8b9467370..a5129c8a4 100644
--- a/app/src/main/java/net/osmtracker/db/TrackContentProvider.java
+++ b/app/src/main/java/net/osmtracker/db/TrackContentProvider.java
@@ -119,6 +119,7 @@ public class TrackContentProvider extends ContentProvider {
uriMatcher.addURI(AUTHORITY, Schema.TBL_WAYPOINT + "/uuid/*", Schema.URI_CODE_WAYPOINT_UUID);
uriMatcher.addURI(AUTHORITY, Schema.TBL_TRACKPOINT + "/#", Schema.URI_CODE_TRACKPOINT_ID);
uriMatcher.addURI(AUTHORITY, Schema.TBL_NOTE + "/#", Schema.URI_CODE_NOTE_ID);
+ uriMatcher.addURI(AUTHORITY, Schema.TBL_NOTE + "/uuid/*", Schema.URI_CODE_NOTE_UUID);
}
/**
@@ -139,6 +140,14 @@ public static final Uri waypointUri(long waypointId) {
return ContentUris.withAppendedId(CONTENT_URI_WAYPOINT, waypointId);
}
+ /**
+ * @param noteId target note id
+ * @return Uri for the note
+ */
+ public static final Uri noteUri(long noteId) {
+ return ContentUris.withAppendedId(CONTENT_URI_NOTE, noteId);
+ }
+
/**
* @param trackId target track id
* @return Uri for the notes of the track
@@ -223,6 +232,14 @@ public int delete(Uri uri, String selection, String[] selectionArgs) {
count = 0;
}
break;
+ case Schema.URI_CODE_NOTE_UUID:
+ String noteUUID = uri.getLastPathSegment();
+ if(noteUUID != null){
+ count = dbHelper.getWritableDatabase().delete(Schema.TBL_NOTE, Schema.COL_UUID + " = ?", new String[]{noteUUID});
+ }else{
+ count = 0;
+ }
+ break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
@@ -386,6 +403,16 @@ public Cursor query(Uri uri, String[] projection, String selectionIn, String[] s
selection = Schema.COL_TRACK_ID + " = ?";
selectionArgs = new String[] {trackId};
break;
+ case Schema.URI_CODE_NOTE_ID:
+ if (selectionIn != null || selectionArgsIn != null) {
+ // Any selection/selectionArgs will be ignored
+ throw new UnsupportedOperationException();
+ }
+ String noteId = uri.getPathSegments().get(1);
+ qb.setTables(Schema.TBL_NOTE);
+ selection = Schema.COL_ID + " = ?";
+ selectionArgs = new String[] {noteId};
+ break;
case Schema.URI_CODE_TRACK_START:
if (selectionIn != null || selectionArgsIn != null) {
// Any selection/selectionArgs will be ignored
@@ -585,6 +612,7 @@ public static final class Schema {
public static final int URI_CODE_TRACKPOINT_ID = 12;
public static final int URI_CODE_TRACK_NOTES = 13;
public static final int URI_CODE_NOTE_ID = 14;
+ public static final int URI_CODE_NOTE_UUID = 15;
public static final int VAL_TRACK_ACTIVE = 1;
diff --git a/app/src/main/res/layout/notelist.xml b/app/src/main/res/layout/notelist.xml
new file mode 100644
index 000000000..15b60f4ce
--- /dev/null
+++ b/app/src/main/res/layout/notelist.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/notelist_item.xml b/app/src/main/res/layout/notelist_item.xml
index de51a0e5d..1fef1e588 100644
--- a/app/src/main/res/layout/notelist_item.xml
+++ b/app/src/main/res/layout/notelist_item.xml
@@ -1,58 +1,58 @@
-
+
-
+
-
+
-
-
+
-
+
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/test/java/net/osmtracker/db/DataHelperNoteTest.java b/app/src/test/java/net/osmtracker/db/DataHelperNoteTest.java
new file mode 100644
index 000000000..b243fc9f4
--- /dev/null
+++ b/app/src/test/java/net/osmtracker/db/DataHelperNoteTest.java
@@ -0,0 +1,68 @@
+package net.osmtracker.db;
+
+import android.database.Cursor;
+import android.location.Location;
+import android.os.Bundle;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.UUID;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 25)
+public class DataHelperNoteTest {
+
+ private DataHelper dataHelper;
+
+ @Before
+ public void setup() {
+ // Initialize DataHelper with the Robolectric application context
+ dataHelper = new DataHelper(ApplicationProvider.getApplicationContext());
+ }
+
+ @Test
+ public void testDeleteNote_RemovesFromDatabase() {
+ String noteUUID = UUID.randomUUID().toString();
+ long trackId = 1L;
+
+ // 1. Insert a note
+ Location loc = new Location("gps");
+ loc.setLatitude(1.23);
+ loc.setLongitude(4.56);
+ loc.setTime(System.currentTimeMillis());
+
+ // Initialize extras to avoid NullPointerException if logic accesses location extras
+ loc.setExtras(new Bundle());
+
+ dataHelper.trackNote(trackId, loc, "Note to delete", noteUUID);
+
+ // 2. Verify it exists before deletion
+ Assert.assertTrue("Note should exist after insertion", noteExists(trackId));
+
+ // 3. Delete note
+ dataHelper.deleteNote(noteUUID);
+
+ // 4. Verify it is gone
+ Assert.assertFalse("Note should have been deleted from DB", noteExists(trackId));
+ }
+
+ private boolean noteExists(long TrackId) {
+ // Query using the content resolver provided by the Robolectric environment
+ Cursor c = ApplicationProvider.getApplicationContext().getContentResolver().query(
+ TrackContentProvider.notesUri(TrackId),
+ null,null,null,null);
+
+ boolean exists = (c != null && c.getCount() > 0);
+ if (c != null) {
+ c.close();
+ }
+ return exists;
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/net/osmtracker/test/db/model/OSMVisibilityTest.java b/app/src/test/java/net/osmtracker/db/model/OSMVisibilityTest.java
similarity index 98%
rename from app/src/test/java/net/osmtracker/test/db/model/OSMVisibilityTest.java
rename to app/src/test/java/net/osmtracker/db/model/OSMVisibilityTest.java
index 36e2fdde4..39ea4af0e 100644
--- a/app/src/test/java/net/osmtracker/test/db/model/OSMVisibilityTest.java
+++ b/app/src/test/java/net/osmtracker/db/model/OSMVisibilityTest.java
@@ -1,4 +1,4 @@
-package net.osmtracker.test.db.model;
+package net.osmtracker.db.model;
import android.content.Context;
diff --git a/app/src/test/java/net/osmtracker/test/db/model/TrackTest.java b/app/src/test/java/net/osmtracker/db/model/TrackTest.java
similarity index 98%
rename from app/src/test/java/net/osmtracker/test/db/model/TrackTest.java
rename to app/src/test/java/net/osmtracker/db/model/TrackTest.java
index 04c524fbc..fa02164cb 100644
--- a/app/src/test/java/net/osmtracker/test/db/model/TrackTest.java
+++ b/app/src/test/java/net/osmtracker/db/model/TrackTest.java
@@ -1,4 +1,4 @@
-package net.osmtracker.test.db.model;
+package net.osmtracker.db.model;
import android.content.ContentResolver;
import android.database.Cursor;
From 9beed2dd28111e11f9f22bc14f2ee87537162b54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Guti=C3=A9rrez=20Alfaro?=
Date: Tue, 20 Jan 2026 18:41:07 -0600
Subject: [PATCH 2/4] Open Street Map Notes Upload clases and add unit test
---
app/src/main/AndroidManifest.xml | 3 +-
.../activity/OpenStreetMapNotesUpload.java | 227 +++++++-------
.../osm/UploadToOpenStreetMapNotesTask.java | 283 +++++++++---------
app/src/main/res/layout/osm_note_upload.xml | 36 ++-
.../OpenStreetMapNotesUploadTest.java | 107 +++++++
5 files changed, 382 insertions(+), 274 deletions(-)
create mode 100644 app/src/test/java/net/osmtracker/activity/OpenStreetMapNotesUploadTest.java
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 397e7e289..071c23b80 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -70,6 +70,8 @@
android:label="@string/osm_upload"
android:exported="true">
+
@@ -114,7 +116,6 @@
-
Uploads a note on OSM using the API and
- * OAuth authentication.
+ * Uploads a note on OSM using the API and OAuth authentication.
*
* This activity may be called twice during a single
* upload cycle: First to start the upload, then a second
@@ -37,7 +38,7 @@
*
* @author Most of the code was made by Nicolas Guillaumin, adapted by Jose Andrés Vargas Serrano
*/
-public class OpenStreetMapNotesUpload extends Activity {
+public class OpenStreetMapNotesUpload extends AppCompatActivity {
private static final String TAG = OpenStreetMapNotesUpload.class.getSimpleName();
@@ -51,19 +52,39 @@ public class OpenStreetMapNotesUpload extends Activity {
/** URL that the browser will call once the user is authenticated */
public final static String OAUTH2_CALLBACK_URL = "osmtracker://osm-upload/oath2-completed/";
- public final static int RC_AUTH = 7;
-
private AuthorizationService authService;
+ private ActivityResultLauncher authLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- View uploadNoteView = getLayoutInflater().inflate(R.layout.osm_note_upload, null);
- setContentView(uploadNoteView);
- setTitle(R.string.osm_note_upload);
-
- noteContentView = uploadNoteView.findViewById(R.id.wplist_item_name);
- noteFooterView = uploadNoteView.findViewById(R.id.osm_note_footer);
+ super.onCreate(savedInstanceState);
+
+ // Register the launcher
+ authLauncher = registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ // This replaces the logic previously in onActivityResult
+ Intent data = result.getData();
+ // RC_AUTH logic
+ if (data != null) {
+ AuthorizationResponse resp = AuthorizationResponse.fromIntent(data);
+ AuthorizationException ex = AuthorizationException.fromIntent(data);
+
+ if (resp != null) {
+ exchangeAuthorizationCode(resp);
+ } else {
+ Log.e(TAG, "Authorization failed: " + (ex != null ? ex.getMessage() : "Unknown error"));
+ Toast.makeText(this, R.string.osm_upload_oauth_failed, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ );
+
+
+ setContentView(R.layout.osm_note_upload);
+ setTitle(R.string.osm_note_upload);
+ noteContentView = findViewById(R.id.wplist_item_name);
+ noteFooterView = findViewById(R.id.osm_note_footer);
// Read and cache extras
Bundle extras = getIntent().getExtras();
@@ -85,20 +106,10 @@ protected void onCreate(Bundle savedInstanceState) {
noteContentView.setText(initialNoteText);
noteFooterView.setText(getString(R.string.osm_note_footer, appName, version));
- final Button btnOk = (Button) findViewById(R.id.osm_note_upload_button_ok);
- btnOk.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- startUpload(noteId);
- }
- });
- final Button btnCancel = (Button) findViewById(R.id.osm_note_upload_button_cancel);
- btnCancel.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- finish();
- }
- });
+ final Button btnOk = findViewById(R.id.osm_note_upload_button_ok);
+ btnOk.setOnClickListener(v -> startUpload(noteId));
+ final Button btnCancel = findViewById(R.id.osm_note_upload_button_cancel);
+ btnCancel.setOnClickListener(v -> finish());
}
@@ -109,100 +120,96 @@ public void onClick(View v) {
*/
private void startUpload(long noteId) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- if ( prefs.contains(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN) ) {
- // Re-use saved token
- uploadToOsm(prefs.getString(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN, ""), noteId);
- } else {
- // Open browser and request token
+ String accessToken = prefs.getString(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN, null);
+
+ if (accessToken != null && !accessToken.isEmpty()) {
+ // STATE: AUTHORIZED. Re-use saved token
+ Log.d(TAG, "Token found, proceeding to upload note to OSM.");
+ uploadToOsm(accessToken, noteId);
+ } else {
+ // STATE: UNAUTHORIZED. Open browser and request token
+ Log.d(TAG, "No token found, requesting authorization.");
requestOsmAuth();
}
}
/*
- * Init Authorization request workflow.
+ * Init Authorization request workflow. Launches browser to request authorization.
*/
public void requestOsmAuth() {
// Authorization service configuration
- AuthorizationServiceConfiguration serviceConfig =
- new AuthorizationServiceConfiguration(
+ AuthorizationServiceConfiguration serviceConfig = new AuthorizationServiceConfiguration(
Uri.parse(OpenStreetMapConstants.OAuth2.Urls.AUTHORIZATION_ENDPOINT),
Uri.parse(OpenStreetMapConstants.OAuth2.Urls.TOKEN_ENDPOINT));
- // Obtaining an authorization code
- Uri redirectURI = Uri.parse(OAUTH2_CALLBACK_URL);
- AuthorizationRequest.Builder authRequestBuilder =
- new AuthorizationRequest.Builder(
- serviceConfig, OpenStreetMapConstants.OAuth2.CLIENT_ID,
- ResponseTypeValues.CODE, redirectURI);
- AuthorizationRequest authRequest = authRequestBuilder
- .setScope(OpenStreetMapConstants.OAuth2.SCOPE)
- .build();
-
- // Start activity.
+ // Obtaining an authorization code
+ AuthorizationRequest authRequest = new AuthorizationRequest.Builder(
+ serviceConfig,
+ OpenStreetMapConstants.OAuth2.CLIENT_ID,
+ ResponseTypeValues.CODE,
+ Uri.parse(OAUTH2_CALLBACK_URL))
+ .setScope(OpenStreetMapConstants.OAuth2.SCOPE)
+ .build();
+
+ // Start activity.
authService = new AuthorizationService(this);
Intent authIntent = authService.getAuthorizationRequestIntent(authRequest);
- startActivityForResult(authIntent, RC_AUTH); //when done onActivityResult will be called.
+ //when done onActivityResult will be called.
+ // Use the launcher instead of startActivityForResult
+ authLauncher.launch(authIntent);
}
-
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- // User is returning from authentication
- if (requestCode == RC_AUTH) {
- // Handling the authorization response
- AuthorizationResponse resp = AuthorizationResponse.fromIntent(data);
- AuthorizationException ex = AuthorizationException.fromIntent(data);
- // ... process the response or exception ...
- if (ex != null) {
- Log.e(TAG, "Authorization Error. Exception received from server.");
- Log.e(TAG, ex.getMessage());
- } else if (resp == null) {
- Log.e(TAG, "Authorization Error. Null response from server.");
- } else {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
-
- //Exchanging the authorization code
- authService.performTokenRequest(
- resp.createTokenExchangeRequest(),
- new AuthorizationService.TokenResponseCallback() {
- @Override public void onTokenRequestCompleted(
- TokenResponse resp, AuthorizationException ex) {
- if (resp != null) {
- // exchange succeeded
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN, resp.accessToken);
- editor.apply();
- //continue with the note Upload.
- uploadToOsm(resp.accessToken, noteId);
- } else {
- // authorization failed, check ex for more details
- Log.e(TAG, "OAuth failed.");
- }
- }
- });
- }
- } else {
- Log.e(TAG, "Unexpected requestCode:" + requestCode + ".");
- }
- }
+ private void exchangeAuthorizationCode(AuthorizationResponse resp) {
+ authService.performTokenRequest(resp.createTokenExchangeRequest(), (tokenResp, tokenEx) -> {
+ if (tokenResp != null && tokenResp.accessToken != null) {
+ // STATE: TRANSITION TO AUTHORIZED
+ persistToken(tokenResp.accessToken);
+ uploadToOsm(tokenResp.accessToken, noteId);
+ } else {
+ Log.e(TAG, "Token exchange failed");
+ }
+ });
+ }
+
+ private void persistToken(String token) {
+ PreferenceManager.getDefaultSharedPreferences(this).edit()
+ .putString(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN, token)
+ .apply();
+ }
/**
* Uploads notes to OSM.
*/
public void uploadToOsm(String accessToken, long noteId) {
- String noteText = noteContentView.getText().toString();
- String footer = noteFooterView.getText().toString();
- if (!footer.isEmpty()) {
- noteText = noteText + "\n\n" + footer;
- }
- new UploadToOpenStreetMapNotesTask(
- OpenStreetMapNotesUpload.this,
- accessToken,
- noteId,
- noteText,
- latitude,
- longitude
- ).execute();
- }
-
+ String noteText = noteContentView.getText().toString();
+ String footer = noteFooterView.getText().toString();
+ if (!footer.isEmpty()) {
+ noteText = noteText + "\n\n" + footer;
+ }
+
+ // Final variables for the background thread
+ final String finalNoteText = noteText;
+
+ // This replaces the deprecated AsyncTask.execute()
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ executor.execute(() -> {
+ try {
+ new UploadToOpenStreetMapNotesTask(
+ OpenStreetMapNotesUpload.this,
+ accessToken,
+ noteId,
+ finalNoteText,
+ latitude,
+ longitude
+ ).run();
+ } catch (Exception e) {
+ Log.e(TAG, "Error during OSM Note upload", e);
+ runOnUiThread(() ->
+ Toast.makeText(this, R.string.osm_upload_error, Toast.LENGTH_SHORT).show()
+ );
+ } finally {
+ executor.shutdown();
+ }
+ });
+ }
}
diff --git a/app/src/main/java/net/osmtracker/osm/UploadToOpenStreetMapNotesTask.java b/app/src/main/java/net/osmtracker/osm/UploadToOpenStreetMapNotesTask.java
index 55d75c749..28c6c560b 100644
--- a/app/src/main/java/net/osmtracker/osm/UploadToOpenStreetMapNotesTask.java
+++ b/app/src/main/java/net/osmtracker/osm/UploadToOpenStreetMapNotesTask.java
@@ -1,184 +1,173 @@
package net.osmtracker.osm;
import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.SharedPreferences.Editor;
-import android.os.AsyncTask;
-import android.preference.PreferenceManager;
import android.util.Log;
+import android.view.Gravity;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.PreferenceManager;
import net.osmtracker.OSMTracker;
import net.osmtracker.R;
import net.osmtracker.db.DataHelper;
import net.osmtracker.util.DialogUtils;
+import java.lang.ref.WeakReference;
+
import de.westnordost.osmapi.OsmConnection;
import de.westnordost.osmapi.common.errors.OsmAuthorizationException;
import de.westnordost.osmapi.common.errors.OsmBadUserInputException;
import de.westnordost.osmapi.map.data.OsmLatLon;
-import de.westnordost.osmapi.notes.Note; // Note object
-import de.westnordost.osmapi.notes.NotesApi; // Api for uploading notes to OSM
-import de.westnordost.osmapi.map.data.LatLon; // Data type for location points, maybe I'll put it in the dialog file
+import de.westnordost.osmapi.notes.NotesApi;
/**
* Uploads a note to OpenStreetMap
*
* @author Most of the code was made by Nicolas Guillaumin, adapted by Jose Andrés Vargas Serrano
*/
-public class UploadToOpenStreetMapNotesTask extends AsyncTask {
+public class UploadToOpenStreetMapNotesTask {
private static final String TAG = UploadToOpenStreetMapNotesTask.class.getSimpleName();
- /** Upload progress dialog */
- private ProgressDialog dialog;
+ // Result constants
+ private static final int RESULT_OK = 1;
+ private static final int RESULT_ERROR_INTERNAL = -1;
+ private static final int RESULT_ERROR_AUTH = -2;
+ private static final int RESULT_ERROR_OSM_USER = -3;
- private final Activity activity;
+ private final WeakReference activityRef;
private final String accessToken;
+ //OSM Note data
private final long noteId;
-
- /** Note text */
private final String noteText;
-
- /** Note longitude */
private final double longitude;
-
- /** Note latitude */
private final double latitude;
- /**
- * Error message, or text of the response returned by OSM
- * if the request completed
- */
+ private AlertDialog progressDialog;
+ // Error message returned by OSM if the request completed
private String errorMsg;
+ private int resultCode = RESULT_ERROR_INTERNAL;
- /**
- * Either the HTTP result code, or -1 for an internal error
- */
- private int resultCode = -1;
- private final int authorizationErrorResultCode = -2;
- private final int anotherErrorResultCode = -3;
- private final int okResultCode = 1;
-
- // Not using an activity yet
- public UploadToOpenStreetMapNotesTask(Activity activity, String accessToken, long noteId,
+ public UploadToOpenStreetMapNotesTask(Activity activity, String accessToken, long noteId,
String noteText, double latitude, double longitude) {
- this.activity = activity;
- this.accessToken = accessToken;
+ this.activityRef = new WeakReference<>(activity);
+ this.accessToken = accessToken;
this.noteId = noteId;
- this.noteText = noteText;
- this.longitude = longitude;
- this.latitude = latitude;
- }
-
- @Override
- protected void onPreExecute() {
- try {
- // Display progress dialog
- dialog = new ProgressDialog(activity);
- dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- dialog.setIndeterminate(true);
- dialog.setTitle(R.string.osm_note_upload);
-
- dialog.setCancelable(false);
- dialog.show();
-
- } catch (Exception e) {
- Log.e(TAG, "onPreExecute() failed", e);
- errorMsg = e.getLocalizedMessage();
- cancel(true);
- }
- }
-
- @Override
- protected void onPostExecute(Void result) {
- switch (resultCode) {
- case -1:
- dialog.dismiss();
- // Internal error, the request didn't start at all
- DialogUtils.showErrorDialog(activity,
- activity.getResources().getString(R.string.osm_note_upload_error)
- + ": " + errorMsg);
- break;
- case okResultCode:
- dialog.dismiss();
- // Success ! Update database and close activity
- DataHelper.setNoteUploadDate(noteId, System.currentTimeMillis(), activity.getContentResolver());
-
- new AlertDialog.Builder(activity)
- .setTitle(android.R.string.dialog_alert_title)
- .setIcon(android.R.drawable.ic_dialog_info)
- .setMessage(R.string.osm_upload_sucess)
- .setCancelable(true)
- .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- activity.finish();
- }
- }).create().show();
-
- break;
- case authorizationErrorResultCode:
- dialog.dismiss();
- Log.e(TAG, "onPostExecute() authorization failed: " + errorMsg + " (" + resultCode + ")");
- // Authorization issue. Provide a way to clear credentials
- new AlertDialog.Builder(activity)
- .setTitle(android.R.string.dialog_alert_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.osm_note_upload_unauthorized)
- .setCancelable(true)
- .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- })
- .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Editor editor = PreferenceManager.getDefaultSharedPreferences(activity).edit();
- editor.remove(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN);
- editor.commit();
-
- dialog.dismiss();
- }
- }).create().show();
- break;
-
- default:
- // Another error. Display OSM response
- dialog.dismiss();
- // Internal error, the request didn't start at all
- Log.e(TAG, "onPostExecute() default failed: " + errorMsg + " (" + resultCode + ")");
- DialogUtils.showErrorDialog(activity,
- activity.getResources().getString(R.string.osm_note_upload_error)
- + ": " + errorMsg);
- }
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- OsmConnection osm = new OsmConnection(OpenStreetMapConstants.Api.OSM_API_URL_PATH,
- OpenStreetMapConstants.OAuth2.USER_AGENT, accessToken);
-
- try {
- LatLon point = new OsmLatLon(latitude, longitude);
- Note note = new NotesApi(osm).create(point, noteText);
- resultCode = okResultCode;
- } catch (/*IOException |*/ IllegalArgumentException | OsmBadUserInputException e) {
- Log.d(TAG, e.getMessage());
- resultCode = -1; //internal error.
- } catch (OsmAuthorizationException oae) {
- Log.d(TAG, "OsmAuthorizationException");
- resultCode = authorizationErrorResultCode;
- } catch (Exception e) {
- Log.e(TAG, e.getMessage());
- resultCode = anotherErrorResultCode;
- }
- return null;
- }
+ this.noteText = noteText;
+ this.longitude = longitude;
+ this.latitude = latitude;
+ }
+
+ /**
+ * Synchronous execution for use with ExecutorService.
+ * Replaces the lifecycle of AsyncTask. */
+ public void run() {
+ final Activity activity = activityRef.get();
+ if (activity == null || activity.isFinishing()) return;
+
+ // 1. Prepare UI (Equivalent to onPreExecute)
+ activity.runOnUiThread(() -> progressDialog = createProgressDialog(activity));
+
+ // 2. Execute Network Logic (Equivalent to doInBackground)
+ OsmConnection osm = new OsmConnection(
+ OpenStreetMapConstants.Api.OSM_API_URL_PATH,
+ OpenStreetMapConstants.OAuth2.USER_AGENT,
+ accessToken);
+
+ try {
+ new NotesApi(osm).create(new OsmLatLon(latitude, longitude), noteText);
+ resultCode = RESULT_OK;
+ } catch (OsmBadUserInputException e) {
+ Log.e(TAG, "Bad OSM user input or illegal argument", e);
+ errorMsg = e.getLocalizedMessage();
+ resultCode = RESULT_ERROR_OSM_USER;
+ } catch (OsmAuthorizationException oae) {
+ Log.e(TAG, "OSM Authorization error", oae);
+ errorMsg = oae.getLocalizedMessage();
+ resultCode = RESULT_ERROR_AUTH;
+ } catch (Exception e) {
+ Log.e(TAG, "Upload error", e);
+ errorMsg = e.getLocalizedMessage();
+ resultCode = RESULT_ERROR_INTERNAL;
+ }
+
+ // 3. Handle Results (Equivalent to onPostExecute)
+ activity.runOnUiThread(() -> {
+ Activity act = activityRef.get();
+ if (act == null || act.isFinishing()) return;
+
+ if (progressDialog != null && progressDialog.isShowing()) {
+ progressDialog.dismiss();
+ }
+
+ handleResult(act);
+ });
+ }
+
+ private void handleResult(Activity activity) {
+ switch (resultCode) {
+ case RESULT_OK:
+ DataHelper.setNoteUploadDate(noteId,
+ System.currentTimeMillis(), activity.getContentResolver());
+ new AlertDialog.Builder(activity)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setMessage(R.string.osm_upload_sucess)
+ .setPositiveButton(android.R.string.ok,
+ (d, w) -> activity.finish())
+ .show();
+ break;
+
+ case RESULT_ERROR_AUTH:
+ showAuthErrorDialog(activity);
+ break;
+
+ default:
+ DialogUtils.showErrorDialog(activity,
+ activity.getString(R.string.osm_note_upload_error) + ": " + errorMsg);
+ break;
+ }
+ }
+
+ private AlertDialog createProgressDialog(Activity activity) {
+ LinearLayout layout = new LinearLayout(activity);
+ layout.setOrientation(LinearLayout.HORIZONTAL);
+ layout.setPadding(50, 50, 50, 50);
+ layout.setGravity(Gravity.CENTER_VERTICAL);
+
+ ProgressBar pb = new ProgressBar(activity);
+ pb.setIndeterminate(true);
+ layout.addView(pb);
+
+ TextView tv = new TextView(activity);
+ tv.setText(R.string.osm_note_upload);
+ tv.setPadding(40, 0, 0, 0);
+ layout.addView(tv);
+
+ AlertDialog progressDialog = new AlertDialog.Builder(activity)
+ .setView(layout).setCancelable(false).create();
+ progressDialog.show();
+ return progressDialog;
+ }
+
+ /**
+ * Helper to show the specific authorization error dialog
+ */
+ private void showAuthErrorDialog(Activity activity) {
+ new AlertDialog.Builder(activity)
+ .setTitle(android.R.string.dialog_alert_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(R.string.osm_note_upload_unauthorized)
+ .setCancelable(true)
+ .setNegativeButton(android.R.string.no, (d, w) -> d.dismiss())
+ .setPositiveButton(android.R.string.yes, (d, w) -> {
+ PreferenceManager.getDefaultSharedPreferences(activity).edit()
+ .remove(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN).apply();
+ d.dismiss();
+ }).show();
+ }
}
diff --git a/app/src/main/res/layout/osm_note_upload.xml b/app/src/main/res/layout/osm_note_upload.xml
index 268319633..17de9d4e7 100644
--- a/app/src/main/res/layout/osm_note_upload.xml
+++ b/app/src/main/res/layout/osm_note_upload.xml
@@ -5,45 +5,50 @@
android:padding="16dp">
+ android:gravity="center"
+ android:orientation="vertical">
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textStyle="bold" />
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@android:color/holo_green_light" />
+ android:id="@+id/osm_note_footer"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@android:color/holo_green_light" />
+ android:orientation="horizontal">
+
-
-
-
+
diff --git a/app/src/test/java/net/osmtracker/activity/OpenStreetMapNotesUploadTest.java b/app/src/test/java/net/osmtracker/activity/OpenStreetMapNotesUploadTest.java
new file mode 100644
index 000000000..9c2bb99cf
--- /dev/null
+++ b/app/src/test/java/net/osmtracker/activity/OpenStreetMapNotesUploadTest.java
@@ -0,0 +1,107 @@
+package net.osmtracker.activity;
+
+import static org.junit.Assert.assertEquals;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceManager;
+import androidx.test.core.app.ApplicationProvider;
+
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowActivity;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 25)
+public class OpenStreetMapNotesUploadTest {
+
+ private Intent intent;
+
+ @Before
+ public void setUp() {
+ // Prepare a valid intent with extras
+ intent = new Intent(ApplicationProvider.getApplicationContext(),
+ OpenStreetMapNotesUpload.class);
+ intent.putExtra("noteId", 123L);
+ intent.putExtra("noteContent", "Test Note Content");
+ intent.putExtra("latitude", 45.0);
+ intent.putExtra("longitude", 9.0);
+ intent.putExtra("version", "3.0.1");
+ intent.putExtra("appName", "OSMTrackerTest");
+
+ // Reset preferences
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
+ ApplicationProvider.getApplicationContext());
+ prefs.edit().clear().apply();
+ }
+
+
+ /**
+ * Verification of UI Binding and Intent Data Extraction
+ */
+ @Test
+ public void onCreate_populatesViewsCorrectly() {
+ // Launch Activity
+ OpenStreetMapNotesUpload activity = Robolectric.buildActivity(
+ OpenStreetMapNotesUpload.class, intent)
+ .create()
+ .start()
+ .resume()
+ .get();
+
+ TextView noteContentView = activity.findViewById(R.id.wplist_item_name);
+ TextView noteFooterView = activity.findViewById(R.id.osm_note_footer);
+
+ // Verify content extracted from intent
+ assertEquals("Test Note Content", noteContentView.getText().toString());
+
+ //check footer is correctly constructed according to strings.xml
+ // Verify footer constructed with intent extras
+ String expectedFooter = activity.getString(
+ R.string.osm_note_footer,"OSMTrackerTest", "3.0.1");
+ assertEquals(expectedFooter, noteFooterView.getText().toString());
+ }
+
+
+ /**
+ * Flow Control - Existing token should skip Auth and trigger task directly
+ */
+ @Test
+ public void startUpload_withExistingToken_skipsAuthFlow() {
+ // Inject a fake token into SharedPreferences
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
+ ApplicationProvider.getApplicationContext());
+ prefs.edit().putString(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN,
+ "fake_token").commit();
+
+ OpenStreetMapNotesUpload activity = Robolectric.buildActivity(
+ OpenStreetMapNotesUpload.class, intent)
+ .create()
+ .start()
+ .resume()
+ .get();
+
+ // Trigger OK button
+ activity.findViewById(R.id.osm_note_upload_button_ok).performClick();
+
+ // Verify that NO new activity was started
+ // because it bypassed auth and went to background task
+ ShadowActivity shadowActivity = shadowOf(activity);
+ Intent startedIntent = shadowActivity.getNextStartedActivity();
+
+ Assert.assertNull("Should not start Auth browser if token is already present",
+ startedIntent);
+ }
+
+}
From 40788792b31c9ec0fd8758ee014df638f3e3b4f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Guti=C3=A9rrez=20Alfaro?=
Date: Wed, 21 Jan 2026 10:33:21 -0600
Subject: [PATCH 3/4] Refactor: Delete EditNoteDialogOnClickListener Java class
---
.../EditNoteDialogOnClickListener.java | 24 -------------------
1 file changed, 24 deletions(-)
delete mode 100644 app/src/main/java/net/osmtracker/listener/EditNoteDialogOnClickListener.java
diff --git a/app/src/main/java/net/osmtracker/listener/EditNoteDialogOnClickListener.java b/app/src/main/java/net/osmtracker/listener/EditNoteDialogOnClickListener.java
deleted file mode 100644
index 3152cc9a7..000000000
--- a/app/src/main/java/net/osmtracker/listener/EditNoteDialogOnClickListener.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package net.osmtracker.listener;
-
-import android.app.AlertDialog;
-import android.database.Cursor;
-import android.view.View;
-
-/**
- * Class that implements an OnClickListener to display an edit note dialog.
- */
-public class EditNoteDialogOnClickListener implements View.OnClickListener {
-
- private Cursor cursor;
-
- protected AlertDialog alert;
-
- protected EditNoteDialogOnClickListener(AlertDialog alert, Cursor cu) {
- this.cursor = cu; // Assigns the received cursor to the class attribute
- this.alert = alert; // Assigns the received alert to the class attribute
- }
-
- @Override
- public void onClick(View view) {
- }
-}
From 988428831201022bf7d6894b2acfc11b247ad441 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Guti=C3=A9rrez=20Alfaro?=
Date: Thu, 22 Jan 2026 10:05:21 -0600
Subject: [PATCH 4/4] Refactor(test): Replace PowerMock with Mockito
---
app/build.gradle | 3 -
.../activity/ButtonsPresetsTest.java | 439 ++++++----------
.../net/osmtracker/data/WayPointMocks.java | 4 -
.../net/osmtracker/db/model/TrackTest.java | 19 +-
.../gpx/ExportToStorageTaskTest.java | 467 ++++++------------
.../layout/DownloadCustomLayoutTaskTest.java | 82 ++-
.../util/CustomLayoutsUtilsTest.java | 152 +++---
.../osmtracker/util/ThemeValidatorTest.java | 109 ++--
.../net/osmtracker/util/URLCreatorTest.java | 63 +--
.../net/osmtracker/util/UnitTestUtils.java | 48 --
10 files changed, 469 insertions(+), 917 deletions(-)
delete mode 100644 app/src/test/java/net/osmtracker/util/UnitTestUtils.java
diff --git a/app/build.gradle b/app/build.gradle
index 0d1a11d90..a7b6d9f61 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -114,9 +114,6 @@ dependencies {
// Mockito framework
testImplementation "org.mockito:mockito-core:3.12.4"
- testImplementation 'org.powermock:powermock-core:2.0.9'
- testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'
- testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
// Required for local unit tests. Prevent null in JSONObject, JSONArray, etc.
testImplementation 'org.json:json:20240303'
diff --git a/app/src/test/java/net/osmtracker/activity/ButtonsPresetsTest.java b/app/src/test/java/net/osmtracker/activity/ButtonsPresetsTest.java
index 0786ac9d5..e41c0e96e 100644
--- a/app/src/test/java/net/osmtracker/activity/ButtonsPresetsTest.java
+++ b/app/src/test/java/net/osmtracker/activity/ButtonsPresetsTest.java
@@ -1,313 +1,184 @@
package net.osmtracker.activity;
import android.content.SharedPreferences;
+import android.view.View;
import android.widget.CheckBox;
-import android.content.Context;
-import android.content.res.Resources;
import android.os.Environment;
-import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.osmtracker.OSMTracker;
import net.osmtracker.R;
+import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-import org.powermock.reflect.Whitebox;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowEnvironment;
import java.lang.reflect.Field;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Hashtable;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.verify;
-import static org.powermock.api.mockito.PowerMockito.mock;
-
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.verifyPrivate;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.junit.Assert.assertEquals;
import androidx.preference.PreferenceManager;
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore("jdk.internal.reflect.*")
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 25)
public class ButtonsPresetsTest {
-
-
- // Variables used in selectLayoutTest
ButtonsPresets activity;
- private CheckBox pressedCheckbox, priorSelectedCheckbox;
- private Field selectedField;
- private String label = "SOME LABEL";
- SharedPreferences.Editor mockEditor;
- SharedPreferences mockPrefs;
- Field layoutFileNamesField;
- Field prefsField;
- Hashtable mockHash;
-
- @Test
- public void getIsoTest(){
- ButtonsPresets activity = new ButtonsPresets();
-
- int VALUE = 0, EXPECTED = 1;
- String cases[][] = {
- {"test_es.xml", "es"},
- {"a_ge.xml", "ge"},
- {"en_fr.xml", "fr"},
- {"foo_en.xml", "en"},
- {"en.xml", "en"},
- }; // TODO add invalid cases when the real method is improved (now fails with outOfBounds)
-
- try{
- // Make the method callable
- Method getIso = activity.getClass().getDeclaredMethod("getIso", new Class[]{String.class});
- getIso.setAccessible(true);
- String result, expected;
- for(String[] option : cases){
- result = (String) getIso.invoke(activity, option[VALUE]);
- expected = option[EXPECTED];
- assertEquals(expected, result);
- }
-
-
- }catch(Exception e){
- e.printStackTrace();
- }
-
- }
-
- @Test
- public void selectLayoutTest() throws Exception {
- try {
- setupMocksForSelectLayoutTest();
- callSelectLayout();
- makeAssertionsForSelectLayout();
- }catch (Exception e){
- System.out.println("Error testing selectLayout method");
- e.printStackTrace();
- fail();
- }
-
-
- }
-
- @Test
- @PrepareForTest({ButtonsPresets.class, Environment.class})
- public void refreshActivityTest() {
- try {
- ButtonsPresets mockActivity = mock(ButtonsPresets.class);
-
- injectMockHashtable(mockActivity, 1); // to check it's reset actually
-
- LinearLayout downloadedLayouts = mock(LinearLayout.class);
- LinearLayout defaultSection = mock(LinearLayout.class);
-
-
- when(mockActivity,"findViewById", R.id.list_layouts).thenReturn(downloadedLayouts);
- when(mockActivity,"findViewById",R.id.buttons_presets).thenReturn(defaultSection);
-
-
- mockStatic(Environment.class);
- when(mockActivity.getExternalFilesDir(null)).thenReturn(new File(""));
-
- // Actual method call
- when(mockActivity,"refreshActivity").thenCallRealMethod();
- mockActivity.refreshActivity();
-
- Hashtable internalHash = Whitebox.getInternalState(mockActivity.getClass(), "layoutsFileNames");
- assertEquals(0, internalHash.size());
-
- // Check internal method calls happen
- verifyPrivate(mockActivity).invoke("listLayouts", downloadedLayouts);
- verifyPrivate(mockActivity).invoke("checkCurrentLayout", downloadedLayouts, defaultSection);
-
- }catch (Exception e){
- e.printStackTrace();
- System.out.println("Error testing refresh activity");
- fail();
- }
- }
-
- @Test
- @PrepareForTest(PreferenceManager.class)
- public void initializeAttributesTest() {
- ButtonsPresets mockActivity = mock(ButtonsPresets.class);
-
- // Setup the mock resources
- Resources mockResources = mock(Resources.class);
- when(mockResources.getString(R.string.prefs_ui_buttons_layout)).thenReturn("fooTitle");
- when(mockActivity.getResources()).thenReturn(mockResources);
-
- // Mock the shared preferences
- mockDefaultSharedPreferences(mockActivity);
-
- // Call actual method
- callInitializeAttributes(mockActivity);
-
- // Check internal methods calls
- verify(mockActivity).setTitle("fooTitle");
- verify(mockActivity).setContentView(R.layout.buttons_presets);
-
- // Check attributes are set
- checkAttributesAfterInitialization(mockActivity);
- }
-
- @Test
- @PrepareForTest({File.class, Preferences.class, Environment.class})
- public void listLayoutsTest(){
- for (int i = 0; i <= 1 ; i++) {
- try {
- doTestListLayouts(i);
- }
- catch (Exception e){
- e.printStackTrace();
- fail();
- }
- }
- }
-
-
-
-
-
- // TODO: make this test complete after refactoring the method,
- // now tests only a small part because couldn't be tested deeply
- // without refactoring or making it public
- private void doTestListLayouts(int numberOfDownloadedLayouts) throws Exception {
- ButtonsPresets mockActivity = mock(ButtonsPresets.class);
-
- mockStatic(Environment.class); // avoid getExternal call
-
- TextView mockEmpyText = mock(TextView.class);
- when(mockActivity.findViewById(R.id.btnpre_empty)).thenReturn(mockEmpyText);
-
- CheckBox mockDefaultcheckBox = mock(CheckBox.class);
- when(mockDefaultcheckBox.getText()).thenReturn("foo");
- when(mockActivity.findViewById(R.id.def_layout)).thenReturn(mockDefaultcheckBox);
-
- injectMockHashtable(mockActivity, numberOfDownloadedLayouts);
-
- callListLayouts(mockActivity, null);
-
- int expectedVisibility = (numberOfDownloadedLayouts > 1) ? View.INVISIBLE : View.VISIBLE;
- verify(mockEmpyText).setVisibility(expectedVisibility);
-
-
-
-
- }
-
- private void callListLayouts(ButtonsPresets mockActivity, LinearLayout mockRootLayout) {
- try {
- Method m = ButtonsPresets.class.getDeclaredMethod("listLayouts", LinearLayout.class);
- m.setAccessible(true);
- m.invoke(mockActivity, mockRootLayout);
- }catch (Exception e){
- e.printStackTrace();
- fail();
- }
- }
-
- private void mockDefaultSharedPreferences(Context context) {
- SharedPreferences mockPrefs = mock(SharedPreferences.class);
- mockStatic(PreferenceManager.class);
- when(PreferenceManager.getDefaultSharedPreferences(context)).thenReturn(mockPrefs);
- }
-
- private void checkAttributesAfterInitialization(ButtonsPresets mockActivity) {
- Hashtable hashtable = Whitebox.getInternalState(mockActivity.getClass(), "layoutsFileNames");
- Object listener = Whitebox.getInternalState(mockActivity, "listener");
- Object sharedPrefs = Whitebox.getInternalState(mockActivity, "prefs");
- String storageDir = Whitebox.getInternalState(mockActivity.getClass(),"storageDir");
-
- assertTrue(sharedPrefs instanceof SharedPreferences);
- assertEquals(0, hashtable.size());
- assertNotNull(listener);
- assertEquals(File.separator+OSMTracker.Preferences.VAL_STORAGE_DIR, storageDir);
- }
-
- private void callInitializeAttributes(ButtonsPresets mockActivity) {
- try {
- Method m = ButtonsPresets.class.getDeclaredMethod("initializeAttributes");
- m.setAccessible(true);
- m.invoke(mockActivity);
- }catch (Exception e){
- e.printStackTrace();
- fail();
- }
- }
-
- private void injectMockHashtable(ButtonsPresets mockActivity, int numberOfEntries) {
- Hashtable internalHash = new Hashtable();
- for (int i = 0; i < numberOfEntries; i++) {
- internalHash.put("foo", "bar");
- }
- Whitebox.setInternalState(mockActivity.getClass(), "layoutsFileNames", internalHash);
- }
-
- private void setupMocksForSelectLayoutTest() throws Exception{
-
- activity = new ButtonsPresets();
-
- // Mock a selected checkbox
- pressedCheckbox = mock(CheckBox.class);
- when(pressedCheckbox.getText()).thenReturn(label);
-
- // Mock and set a previously selected checkbox
- priorSelectedCheckbox = mock(CheckBox.class);
- selectedField = activity.getClass().getDeclaredField("selected");
- selectedField.setAccessible(true);
- selectedField.set(activity, priorSelectedCheckbox);
-
- // Mock and set the PreferencesEditor
- mockEditor = mock(SharedPreferences.Editor.class);
- when(mockEditor.commit()).thenReturn(true);
- when(mockEditor.putString("ui.buttons.layout", label)).thenReturn(mockEditor);
-
- // Mock prefs to use the mock editor
- mockPrefs = mock(SharedPreferences.class);
- when(mockPrefs.edit()).thenReturn(mockEditor);
-
- // Mock and set the preferences
- prefsField = activity.getClass().getDeclaredField("prefs");
- prefsField.setAccessible(true);
- prefsField.set(activity, mockPrefs);
-
- // Mock and set the layoutFilenames Hashtable
- mockHash = mock(Hashtable.class);
- when(mockHash.get(label)).thenReturn(label);
- layoutFileNamesField = activity.getClass().getDeclaredField("layoutsFileNames");
- layoutFileNamesField.setAccessible(true);
- layoutFileNamesField.set(activity, mockHash);
- }
-
- private void callSelectLayout() throws Exception{
- Method selectLayoutMethod = activity.getClass().getDeclaredMethod("selectLayout", CheckBox.class);
- selectLayoutMethod.setAccessible(true);
- selectLayoutMethod.invoke(activity, pressedCheckbox);
- }
-
- private void makeAssertionsForSelectLayout() throws Exception{
- // Make sure the previously selected is unchecked
- verify(priorSelectedCheckbox).setChecked(false);
-
- // Make sure the just selected is checked
- verify(pressedCheckbox).setChecked(true);
-
- // Make sure selected variable is updated to match the just selected
- assertEquals(selectedField.get(activity), pressedCheckbox);
-
- // Make sure the value in SharedPreferences is updated to match the just selected
- verify(mockEditor).putString(OSMTracker.Preferences.KEY_UI_BUTTONS_LAYOUT, label);
- verify(mockEditor).commit();
- }
+ @Before
+ public void setUp() {
+ // Build and start the activity lifecycle
+ activity = Robolectric.buildActivity(ButtonsPresets.class)
+ .create()
+ .start()
+ .resume()
+ .get();
+ }
+
+ @Test
+ public void getIsoTest() throws Exception {
+ int VALUE = 0, EXPECTED = 1;
+ String[][] cases = {
+ {"test_es.xml", "es"},
+ {"a_ge.xml", "ge"},
+ {"en_fr.xml", "fr"},
+ {"foo_en.xml", "en"},
+ {"en.xml", "en"},
+ };
+
+ Method getIso = ButtonsPresets.class.getDeclaredMethod("getIso", String.class);
+ getIso.setAccessible(true);
+
+ for (String[] option : cases) {
+ String result = (String) getIso.invoke(activity, option[VALUE]);
+ assertEquals(option[EXPECTED], result);
+ }
+ }
+
+ @Test
+ public void testSelectLayout_UpdatesUIAndPreferences() throws Exception {
+ // 1. Setup: Create two CheckBoxes to simulate "old" and "new" selection
+ CheckBox oldCheckBox = new CheckBox(activity);
+ oldCheckBox.setText("Default");
+ oldCheckBox.setChecked(true);
+
+ CheckBox newCheckBox = new CheckBox(activity);
+ newCheckBox.setText("Cycling");
+ newCheckBox.setChecked(false);
+
+ // 2. Setup: Populate the internal Hashtable and 'selected' field via reflection
+ Hashtable mockLayouts = new Hashtable<>();
+ mockLayouts.put("Default", "default.xml");
+ mockLayouts.put("Cycling", "cycling_en.xml");
+
+ setInternalState(ButtonsPresets.class, "layoutsFileNames", mockLayouts);
+ setInternalState(activity, "selected", oldCheckBox);
+
+ // 3. Execution: Invoke the private selectLayout method
+ Method selectLayoutMethod =
+ ButtonsPresets.class.getDeclaredMethod("selectLayout", CheckBox.class);
+ selectLayoutMethod.setAccessible(true);
+ selectLayoutMethod.invoke(activity, newCheckBox);
+
+ // 4. Assertions: Verify UI State
+ Assert.assertFalse("Previous checkbox should be unchecked", oldCheckBox.isChecked());
+ Assert.assertTrue("New checkbox should be checked", newCheckBox.isChecked());
+
+ // 5. Assertions: Verify Internal State
+ CheckBox currentSelected = (CheckBox) getInternalState(activity, "selected");
+ Assert.assertEquals(
+ "Internal 'selected' field should be updated",newCheckBox, currentSelected);
+
+ // 6. Assertions: Verify Persistence in SharedPreferences
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
+ String savedLayout = prefs.getString(OSMTracker.Preferences.KEY_UI_BUTTONS_LAYOUT, null);
+ Assert.assertEquals("SharedPreferences should store the filename from the map",
+ "cycling_en.xml", savedLayout);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked") // Suppress cast warning for the internal Hashtable
+ public void testRefreshActivity_PopulatesUIFromFilesystem() throws Exception {
+ // 1. Setup: Mock the SD Card being mounted
+ ShadowEnvironment.setExternalStorageState(Environment.MEDIA_MOUNTED);
+
+ // 2. Setup: Create dummy layout files in the expected directory
+ // The path logic in ButtonsPresets uses: getExternalFilesDir(null) + /osmtracker/layouts
+ File externalDir = activity.getExternalFilesDir(null);
+ File layoutsDir = new File(externalDir, "osmtracker" + File.separator + "layouts");
+ if (!layoutsDir.exists()) {
+ Assert.assertTrue(layoutsDir.mkdirs());
+ }
+
+ // Create two dummy layout files
+ File layout1 = new File(layoutsDir, "hiking_en.xml");
+ File layout2 = new File(layoutsDir, "cycling_es.xml");
+ Assert.assertTrue(layout1.createNewFile());
+ Assert.assertTrue(layout2.createNewFile());
+
+ // 3. Execution: Trigger the refresh
+ activity.refreshActivity();
+
+ // 4. Assertions: Check internal state (the Hashtable)
+ // We use reflection to get the private static field 'layoutsFileNames'
+ Hashtable layoutsMap = (Hashtable) getInternalState(
+ ButtonsPresets.class, "layoutsFileNames");
+
+ Assert.assertNotNull("Hashtable should be initialized", layoutsMap);
+ // Map should contain 'hiking', 'cycling', and 'Default' (from defaultCheckBox)
+ Assert.assertTrue("Should contain 'hiking' layout", layoutsMap.containsKey("hiking"));
+ Assert.assertTrue("Should contain 'cycling' layout", layoutsMap.containsKey("cycling"));
+
+ // 5. Assertions: Check UI state (the LinearLayout)
+ LinearLayout listLayouts = activity.findViewById(R.id.list_layouts);
+
+ // Count how many CheckBoxes were added.
+ // listLayouts should contain CheckBoxes for every file found.
+ int checkBoxCount = 0;
+ for (int i = 0; i < listLayouts.getChildCount(); i++) {
+ if (listLayouts.getChildAt(i) instanceof CheckBox) {
+ checkBoxCount++;
+ }
+ }
+
+ assertEquals("Two checkboxes should have been added to the UI", 2, checkBoxCount);
+
+ // 6. Verification: Check 'Empty Message' visibility
+ TextView emptyText = activity.findViewById(R.id.btnpre_empty);
+ Assert.assertEquals("Empty message should be INVISIBLE because layouts exist",
+ View.INVISIBLE, emptyText.getVisibility());
+ }
+
+ private void setInternalState(Object target, String fieldName, Object value) throws Exception {
+ Field field;
+ if (target instanceof Class) {
+ field = ((Class>) target).getDeclaredField(fieldName);
+ } else {
+ field = target.getClass().getDeclaredField(fieldName);
+ }
+ field.setAccessible(true);
+ field.set(target instanceof Class ? null : target, value);
+ }
+
+ private Object getInternalState(Object target, String fieldName) throws Exception {
+ Field field;
+ if (target instanceof Class) {
+ field = ((Class>) target).getDeclaredField(fieldName);
+ } else {
+ field = target.getClass().getDeclaredField(fieldName);
+ }
+ field.setAccessible(true);
+ return field.get(target instanceof Class ? null : target);
+ }
}
diff --git a/app/src/test/java/net/osmtracker/data/WayPointMocks.java b/app/src/test/java/net/osmtracker/data/WayPointMocks.java
index adc23b6f6..9f82405ac 100644
--- a/app/src/test/java/net/osmtracker/data/WayPointMocks.java
+++ b/app/src/test/java/net/osmtracker/data/WayPointMocks.java
@@ -3,10 +3,6 @@
import net.osmtracker.db.model.WayPoint;
-import java.util.Date;
-
-import static net.osmtracker.util.UnitTestUtils.createDateFrom;
-
public class WayPointMocks {
// WayPoints of gpx-test.gpx
diff --git a/app/src/test/java/net/osmtracker/db/model/TrackTest.java b/app/src/test/java/net/osmtracker/db/model/TrackTest.java
index fa02164cb..d0ce59190 100644
--- a/app/src/test/java/net/osmtracker/db/model/TrackTest.java
+++ b/app/src/test/java/net/osmtracker/db/model/TrackTest.java
@@ -1,29 +1,32 @@
package net.osmtracker.db.model;
-import android.content.ContentResolver;
import android.database.Cursor;
import net.osmtracker.db.TrackContentProvider;
-import net.osmtracker.db.model.Track;
import static net.osmtracker.db.TrackContentProvider.Schema.*;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
-import static org.powermock.api.mockito.PowerMockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
-//@RunWith(PowerMockRunner.class)
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 25)
public class TrackTest {
final long START_DATE = 123;
final String NAME = "some name";
final String DESCRIPTION = "foo desc";
final String TAGS = "tag1,tag2,tag3";
- final List TAGS_LIST = Arrays.asList("tag1","tag2","tag3");
+ final List TAGS_LIST = Arrays.asList("tag1","tag2","tag3");
final String VISIBILITY = Track.OSMVisibility.Public.name();
final int TRACKPOINT_COUNT = 10;
final int WAYPOINT_COUNT = 20;
@@ -60,12 +63,10 @@ public Cursor initMockCursor(){
@Test
public void testBuild(){
- int trackId = 1;
- ContentResolver resolver = null; // Not used in the method
Cursor mockCursor = initMockCursor();
boolean withExtraInfo = false;
- Track t = Track.build(1, mockCursor, resolver, withExtraInfo);
+ Track t = Track.build(1, mockCursor, null, withExtraInfo);
try {
@@ -86,7 +87,7 @@ public void testBuild(){
}catch (Exception e){
- e.printStackTrace();
+ throw new RuntimeException("Reflection failed during Track fields verification", e);
}
}
}
diff --git a/app/src/test/java/net/osmtracker/gpx/ExportToStorageTaskTest.java b/app/src/test/java/net/osmtracker/gpx/ExportToStorageTaskTest.java
index 59b5b8013..51a024b24 100644
--- a/app/src/test/java/net/osmtracker/gpx/ExportToStorageTaskTest.java
+++ b/app/src/test/java/net/osmtracker/gpx/ExportToStorageTaskTest.java
@@ -1,355 +1,172 @@
package net.osmtracker.gpx;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
-import static net.osmtracker.OSMTracker.Preferences;
-import static net.osmtracker.OSMTracker.Preferences.KEY_OUTPUT_FILENAME;
-import static net.osmtracker.OSMTracker.Preferences.VAL_OUTPUT_FILENAME;
import static net.osmtracker.db.TrackContentProvider.Schema;
-import static net.osmtracker.util.UnitTestUtils.createDateFrom;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.res.Resources;
import android.database.Cursor;
import android.os.Environment;
import androidx.preference.PreferenceManager;
+import androidx.test.core.app.ApplicationProvider;
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
+import net.osmtracker.OSMTracker.Preferences;
import net.osmtracker.db.DataHelper;
import net.osmtracker.db.model.Track;
import net.osmtracker.exception.ExportTrackException;
-import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowEnvironment;
+
import java.io.File;
+import java.util.Calendar;
import java.util.Date;
+import java.util.TimeZone;
-@RunWith(PowerMockRunner.class)
-@PrepareForTest({Environment.class, PreferenceManager.class})
-@PowerMockIgnore("jdk.internal.reflect.*")
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 25)
public class ExportToStorageTaskTest {
- @Rule
- private final TemporaryFolder temporaryFolder = new TemporaryFolder();
- private final Context mockContext = mock(Context.class);
- private final DataHelper mockDataHelper = mock(DataHelper.class);
- private final SharedPreferences mockPrefs = mock(SharedPreferences.class);
- private final Resources mockResources = mock(Resources.class);
-
- private ExportToStorageTask task;
-
- private static final String ERROR_CREATE_TRACK_DIR = "Error creating track directory";
- private static final String UNABLE_TO_WRITE_STORAGE = "Unable to write to external storage";
-
- @Before
- public void setUp() {
- mockStatic(Environment.class);
- mockStatic(PreferenceManager.class);
-
- when(PreferenceManager.getDefaultSharedPreferences(mockContext)).thenReturn(mockPrefs);
- when(mockContext.getResources()).thenReturn(mockResources);
- when(mockResources.getString(R.string.error_create_track_dir)).thenReturn(ERROR_CREATE_TRACK_DIR);
- when(mockResources.getString(R.string.error_externalstorage_not_writable)).thenReturn(UNABLE_TO_WRITE_STORAGE);
-
- task = new ExportToStorageTask(mockContext, mockDataHelper, 1L);
- }
-
- @After
- public void tearDown() {
- temporaryFolder.delete();
- }
-
- @Test
- public void testBuildGPXFilenameUsingOnlyTrackName() {
- String trackNameInDatabase = "MyTrack";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME;
-
- String expectedFilename = "MyTrack";
- if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals("")))expectedFilename += "_";
- expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameUsingTrackNameAndStartDate() {
- String trackNameInDatabase = "MyTrack";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME_DATE;
-
- String expectedFilename = "MyTrack_2000-01-02_03-04-05";
- if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
- expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameUsingStartDateAndTrackName() {
- String trackNameInDatabase = "MyTrack";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_DATE_NAME;
-
- String expectedFilename = "2000-01-02_03-04-05_MyTrack";
- if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
- expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameUsingOnlyStartDate() {
- String trackNameInDatabase = "MyTrack";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_DATE;
-
- String expectedFilename = "2000-01-02_03-04-05";
- if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
- expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameWhenSanitizesTrackName() {
- String trackNameInDatabase = ":M/y*T@r~a\\c?k:";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME;
-
- String expectedFilename = ";M_y_T_r_a_c_k;";
- if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
- expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameWhenUsesTrackNameButThereIsNoName() {
- String trackNameInDatabase = "";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME;
-
- String expectedFilename = "2000-01-02_03-04-05";
- if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
- expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameWhenUsesTrackNameAndStartDateButThereIsNoName() {
- String trackNameInDatabase = "";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME_DATE;
-
- String expectedFilename = "2000-01-02_03-04-05";
- if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
- expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- private void doTestBuildGPXFilename(String trackName, String desiredFormat, long trackStartDate, String expectedFilename) {
- when(mockPrefs.getString(KEY_OUTPUT_FILENAME, VAL_OUTPUT_FILENAME)).thenReturn(desiredFormat);
-
- String result = task.buildGPXFilename(createMockCursor(trackName, trackStartDate), temporaryFolder.getRoot());
-
- assertEquals(expectedFilename, result);
- }
-
- private Cursor createMockCursor(String trackName, long trackStartDate) {
- Cursor mockCursor = mock(Cursor.class);
- when(mockCursor.getColumnIndex(Schema.COL_NAME)).thenReturn(1);
- when(mockCursor.getString(1)).thenReturn(trackName);
-
- when(mockCursor.getColumnIndex(Schema.COL_START_DATE)).thenReturn(2);
- when(mockCursor.getLong(2)).thenReturn(trackStartDate);
-
- return mockCursor;
- }
-
- @Test
- public void testGetExportDirectoryWhenStorageIsWritableAndDirExists() throws Exception {
- // Mocking external storage state
- when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
-
- // Mocking preferences and context
- when(mockPrefs.getString(any(), any())).thenReturn(OSMTracker.Preferences.VAL_STORAGE_DIR);
- when(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)).thenReturn(temporaryFolder.getRoot());
-
- var osmTrackerFolder = temporaryFolder.newFolder("osmtracker");
-
- // Creating task and invoking method
- File exportDirectory = task.getExportDirectory(new Date());
-
- // Verifying the directory path
- assertEquals(osmTrackerFolder.getAbsolutePath(), exportDirectory.getAbsolutePath());
- }
-
- @Test
- public void testGetExportDirectoryWhenStorageIsNotWritable() {
- // Mocking external storage state
- when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED_READ_ONLY);
-
- // Verifying the exception
- assertThrows(ExportTrackException.class, () -> task.getExportDirectory(new Date()));
- }
-
- @Test
- public void testGetExportDirectoryWhenStorageIsWritableAndDirNotExists() throws Exception {
- // Mocking external storage state
- when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
-
- // Mocking preferences and context
- when(mockPrefs.getString(any(), any())).thenReturn(OSMTracker.Preferences.VAL_STORAGE_DIR);
- when(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)).thenReturn(temporaryFolder.getRoot());
-
- // Creating task and invoking method
- File exportDirectory = task.getExportDirectory(new Date());
-
- var osmTrackerFolder = new File(temporaryFolder.getRoot(), OSMTracker.Preferences.VAL_STORAGE_DIR);
-
- // Verifying the directory path
- assertEquals(osmTrackerFolder.getAbsolutePath(), exportDirectory.getAbsolutePath());
- }
-
- @Test
- public void testGetExportDirectoryWhenDirDoesNotExistAndCreatesIt() throws Exception {
- // Mocking external storage state
- when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
-
- // Mocking preferences and context
- when(mockPrefs.getString(any(), any())).thenReturn("NonExistentDir");
-
- // Creating task and invoking method
- File exportDirectory = task.getExportDirectory(new Date());
-
- // Verifying the directory creation
- assertEquals(new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "NonExistentDir").getAbsolutePath(), exportDirectory.getAbsolutePath());
- }
-
- @Test
- public void testGetSanitizedTrackNameByStartDateWithValidTrackName() {
- // Mock track data
- Track mockTrack = new Track();
- mockTrack.setName("My/Track");
- when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
-
- // Execute the method
- String result = task.getSanitizedTrackNameByStartDate(new Date());
-
- // Verify the sanitized track name
- assertEquals("My_Track", result);
- }
-
- @Test
- public void testGetSanitizedTrackNameByStartDateWithEmptyTrackName() {
- // Mock track data
- Track mockTrack = new Track();
- mockTrack.setName("");
- when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
+ private Context context;
+ private DataHelper mockDataHelper;
+ private ExportToStorageTask task;
+ private SharedPreferences prefs;
+
+ // Standard test date: Jan 2nd, 2000, 03:04:05 UTC
+ private static final String DATE_STRING = "2000-01-02_03-04-05";
+ private static final String TRACK_NAME = "MyTrack";
+
+ @Before
+ public void setUp() {
+ context = ApplicationProvider.getApplicationContext();
+ mockDataHelper = mock(DataHelper.class);
+ prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ // Reset preferences to prevent test-to-test leakage
+ prefs.edit().clear().apply();
+ task = new ExportToStorageTask(context, mockDataHelper, 1L);
+ }
+
+ // --- Filename Generation Tests ---
+
+ @Test
+ public void testBuildGPXFilename_OnlyTrackName() {
+ setupFilenamePreference(Preferences.VAL_OUTPUT_FILENAME_NAME);
+ Assert.assertEquals("MyTrack.gpx",
+ executeBuildFilename(TRACK_NAME, createDate()));
+ }
+
+ @Test
+ public void testBuildGPXFilename_TrackNameAndDate() {
+ setupFilenamePreference(Preferences.VAL_OUTPUT_FILENAME_NAME_DATE);
+ assertEquals("MyTrack_" + DATE_STRING + ".gpx",
+ executeBuildFilename(TRACK_NAME, createDate()));
+ }
+
+ @Test
+ public void testBuildGPXFilename_DateAndTrackName() {
+ setupFilenamePreference(Preferences.VAL_OUTPUT_FILENAME_DATE_NAME);
+ assertEquals(DATE_STRING + "_MyTrack" + ".gpx",
+ executeBuildFilename(TRACK_NAME, createDate()));
+ }
+
+ @Test
+ public void testBuildGPXFilename_OnlyDate() {
+ setupFilenamePreference(Preferences.VAL_OUTPUT_FILENAME_DATE);
+ assertEquals(DATE_STRING + ".gpx",
+ executeBuildFilename(TRACK_NAME, createDate()));
+ }
+
+ @Test
+ public void testBuildGPXFilename_Sanitization() {
+ String dirtyName = ":M/y*T@r~a\\c?k:";
+ setupFilenamePreference(Preferences.VAL_OUTPUT_FILENAME_NAME);
+ assertEquals(";M_y_T_r_a_c_k;.gpx",
+ executeBuildFilename(dirtyName, createDate()));
+ }
+
+ @Test
+ public void testBuildGPXFilename_FallbackToDateWhenNameEmpty() {
+ String emptyName = "";
+ setupFilenamePreference(Preferences.VAL_OUTPUT_FILENAME_NAME);
+ // Should fallback to the timestamp if name is missing
+ assertEquals(DATE_STRING + ".gpx",
+ executeBuildFilename(emptyName, createDate()));
+ }
+
+ // --- Export Directory Tests ---
+
+ @Test
+ public void testGetExportDirectory_CreatesMissingFolders() throws Exception {
+ ShadowEnvironment.setExternalStorageState(Environment.MEDIA_MOUNTED);
+ prefs.edit().putString(Preferences.KEY_STORAGE_DIR, "NewAppFolder").apply();
+
+ File result = task.getExportDirectory(new Date());
+
+ assertTrue("Folder should be created", result.exists());
+ assertTrue("Path should contain custom dir name", result.getAbsolutePath().contains("NewAppFolder"));
+ }
+
+ @Test
+ public void testGetExportDirectory_ThrowsWhenNotWritable() {
+ ShadowEnvironment.setExternalStorageState(Environment.MEDIA_MOUNTED_READ_ONLY);
+ assertThrows(ExportTrackException.class, () -> task.getBaseExportDirectory());
+ }
+
+ @Test
+ public void testGetSanitizedTrackName_ReplacesSlashes() {
+ Track mockTrack = new Track();
+ mockTrack.setName("Category/Sub/Track");
+ when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
+
+ String result = task.getSanitizedTrackNameByStartDate(new Date());
+ assertEquals("Category_Sub_Track", result);
+ }
+
+ // --- Internal Helpers ---
+
+ private void setupFilenamePreference(String format) {
+ prefs.edit()
+ .putString(Preferences.KEY_OUTPUT_FILENAME, format)
+ // Reset label for predictability
+ .putString(Preferences.KEY_OUTPUT_FILENAME_LABEL, "")
+ .apply();
+ }
+
+ private String executeBuildFilename(String name, Date date) {
+ return task.buildGPXFilename(createMockCursor(name, date.getTime()), context.getCacheDir());
+ }
+
+ private Cursor createMockCursor(String trackName, long trackStartDate) {
+ Cursor mockCursor = mock(Cursor.class);
+ when(mockCursor.getColumnIndex(Schema.COL_NAME)).thenReturn(1);
+ when(mockCursor.getString(1)).thenReturn(trackName);
+ when(mockCursor.getColumnIndex(Schema.COL_START_DATE)).thenReturn(2);
+ when(mockCursor.getLong(2)).thenReturn(trackStartDate);
+ return mockCursor;
+ }
+
+ /**
+ * Creates a UTC Date representing 2000-01-02 03:04:05.
+ */
+ private static Date createDate() {
+ Calendar cal = Calendar.getInstance(TimeZone.getDefault());
+ // Calendar months are 0-based (January is 0), so we subtract 1 from the input
+ cal.set(2000, Calendar.JANUARY, 2, 3, 4, 5);
+ cal.set(Calendar.MILLISECOND, 0);
+ return cal.getTime();
+
+ }
- // Execute the method
- String result = task.getSanitizedTrackNameByStartDate(new Date());
-
- // Verify the sanitized track name
- assertEquals("", result);
- }
-
- @Test
- public void testGetSanitizedTrackNameByStartDateWithNullTrackName() {
- // Mock track data
- Track mockTrack = new Track();
- mockTrack.setName(null);
- when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
-
- // Execute the method
- String result = task.getSanitizedTrackNameByStartDate(new Date());
-
- // Verify the sanitized track name
- assertNull(result);
- }
-
- @Test
- public void testGetSanitizedTrackNameByStartDateWithSpecialCharacters() {
- // Mock track data
- Track mockTrack = new Track();
- mockTrack.setName("/M/y/T/r/@/c/k/");
- when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
-
- // Execute the method
- String result = task.getSanitizedTrackNameByStartDate(new Date());
-
- // Verify the sanitized track name
- assertEquals("_M_y_T_r_@_c_k_", result);
- }
-
- @Test
- public void testGetSanitizedTrackNameByStartDateWithNoTrackFound() {
- // Mock no track data
- when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(null);
-
- // Execute the method
- String result = task.getSanitizedTrackNameByStartDate(new Date());
-
- // Verify the sanitized track name
- assertEquals("", result);
- }
-
- @Test
- public void testConstructorCallsSuperclassConstructor() {
- long trackId = 1L;
-
- // Use a spy to verify the constructor call
- var taskSpy = new ExportToStorageTask(mockContext, trackId);
-
- assertTrue(taskSpy.exportMediaFiles());
- assertTrue(taskSpy.updateExportDate());
- }
-
- @Test
- public void testCreateDirectoryPerTrack() throws Exception{
- when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
- when(mockPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true);
- when(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)).thenReturn(temporaryFolder.getRoot());
- when(mockPrefs.getString(any(), any())).thenReturn(OSMTracker.Preferences.VAL_STORAGE_DIR);
- Track mockTrack = new Track();
- mockTrack.setName("MyTrack");
- when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
-
- task.getExportDirectory(new Date());
- }
-
- @Test
- public void testCreateDirectoryPerTrackEmptyTrackname() throws Exception{
- when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
- when(mockPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true);
- when(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)).thenReturn(temporaryFolder.getRoot());
- when(mockPrefs.getString(any(), any())).thenReturn(OSMTracker.Preferences.VAL_STORAGE_DIR);
- Track mockTrack = new Track();
- mockTrack.setName("");
- when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
-
- task.getExportDirectory(new Date());
- }
}
diff --git a/app/src/test/java/net/osmtracker/layout/DownloadCustomLayoutTaskTest.java b/app/src/test/java/net/osmtracker/layout/DownloadCustomLayoutTaskTest.java
index 9410e7c21..a81041611 100644
--- a/app/src/test/java/net/osmtracker/layout/DownloadCustomLayoutTaskTest.java
+++ b/app/src/test/java/net/osmtracker/layout/DownloadCustomLayoutTaskTest.java
@@ -3,81 +3,69 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
-import android.util.Log;
import net.osmtracker.OSMTracker;
import net.osmtracker.db.DataHelper;
-import net.osmtracker.util.UnitTestUtils;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowEnvironment;
import java.io.File;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
import androidx.preference.PreferenceManager;
+import androidx.test.core.app.ApplicationProvider;
-@RunWith(PowerMockRunner.class)
-@PrepareForTest({PreferenceManager.class, Environment.class, Log.class})
-@PowerMockIgnore("jdk.internal.reflect.*")
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 25)
public class DownloadCustomLayoutTaskTest {
DownloadCustomLayoutTask downloadCustomLayoutTask;
+ private Context context;
- Context mockContext;
- SharedPreferences mockPrefs;
-
- //FIXME: layout name and iso are coded.
String layoutName = "abc";
String iso = "en";
String expectedLayoutFilename = "abc_en.xml";
- public void setupMocks() {
- // Create SharedPreferences mock
- mockPrefs = mock(SharedPreferences.class);
- UnitTestUtils.setLayoutsTestingRepository(mockPrefs);
+ @Before
+ public void setUp() {
+ context = ApplicationProvider.getApplicationContext();
- // Create PreferenceManager mock
- mockContext = mock(Context.class);
- mockStatic(PreferenceManager.class);
- when(PreferenceManager.getDefaultSharedPreferences(mockContext)).thenReturn(mockPrefs);
- // external storage is writeable
- mockStatic(Environment.class);
- when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
- // log
- mockStatic(Log.class);
+ // Setup real SharedPreferences logic
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit()
+ .putString(OSMTracker.Preferences.KEY_GITHUB_USERNAME, "labexp")
+ .putString(OSMTracker.Preferences.KEY_REPOSITORY_NAME, "osmtracker-android-layouts")
+ .putString(OSMTracker.Preferences.KEY_BRANCH_NAME, "for_tests")
+ .apply();
- downloadCustomLayoutTask = new DownloadCustomLayoutTask(mockContext);
- }
+ // Setup Environment Shadow
+ ShadowEnvironment.setExternalStorageState(Environment.MEDIA_MOUNTED);
- @Test
- public void downloadLayoutWithoutIconsTest() {
- setupMocks();
+ downloadCustomLayoutTask = new DownloadCustomLayoutTask(context);
+ }
- boolean result = downloadCustomLayoutTask.downloadLayout(layoutName,iso);
- assertEquals(true, result);
+ @Test
+ public void downloadLayoutWithoutIconsTest() {
+ boolean result = downloadCustomLayoutTask.downloadLayout(layoutName, iso);
+ assertTrue("Download should return true", result);
- // Check if layout was downloaded at .../osmtracker/layouts/abc_en.xml
- String expectedLayoutFilePath = mockContext.getExternalFilesDir(null)
- + OSMTracker.Preferences.VAL_STORAGE_DIR + File.separator
- + DataHelper.LAYOUTS_SUBDIR + File.separator
- + expectedLayoutFilename;
+ // Check if layout was downloaded at .../osmtracker/layouts/abc_en.xml
+ File layoutsDir = new File(context.getExternalFilesDir(null),
+ OSMTracker.Preferences.VAL_STORAGE_DIR + File.separator + DataHelper.LAYOUTS_SUBDIR);
- System.out.println(expectedLayoutFilePath);
- File layoutFile = new File(expectedLayoutFilePath);
- assertTrue(layoutFile.exists());
+ File layoutFile = new File(layoutsDir, expectedLayoutFilename);
- // Add N icons to abc layout and check if the N icons are downloaded
- // at ... /osmtracker/layouts/abc_icons.
+ System.out.println("Expected path: " + layoutFile.getAbsolutePath());
+ assertTrue("Layout file should exist at path", layoutFile.exists());
- }
+ // Add N icons to abc layout and check if the N icons are downloaded
+ // at ... /osmtracker/layouts/abc_icons.
+ }
}
diff --git a/app/src/test/java/net/osmtracker/util/CustomLayoutsUtilsTest.java b/app/src/test/java/net/osmtracker/util/CustomLayoutsUtilsTest.java
index 05faea75e..7d1b0052e 100644
--- a/app/src/test/java/net/osmtracker/util/CustomLayoutsUtilsTest.java
+++ b/app/src/test/java/net/osmtracker/util/CustomLayoutsUtilsTest.java
@@ -2,117 +2,77 @@
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.res.AssetManager;
import net.osmtracker.OSMTracker;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
-import java.io.BufferedReader;
-import java.io.FileInputStream;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
-import java.util.Scanner;
import static org.junit.Assert.assertEquals;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
import androidx.preference.PreferenceManager;
+import androidx.test.core.app.ApplicationProvider;
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(PreferenceManager.class)
-@PowerMockIgnore("jdk.internal.reflect.*")
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 25)
public class CustomLayoutsUtilsTest {
- Context mockContext;
- SharedPreferences mockPrefs;
- AssetManager mockAssetManager;
- InputStream resultStream;
- InputStream expectedStream;
-
- public void setupMocks() {
- // Create SharedPreferences mock
- mockPrefs = mock(SharedPreferences.class);
- when(mockPrefs.getString(OSMTracker.Preferences.KEY_UI_BUTTONS_LAYOUT,
- OSMTracker.Preferences.VAL_UI_BUTTONS_LAYOUT))
- .thenReturn("transporte publico");
-
- // Create PreferenceManager mock
- mockContext = mock(Context.class);
-
- mockStatic(PreferenceManager.class);
-
- when(PreferenceManager.getDefaultSharedPreferences(mockContext)).thenReturn(mockPrefs);
-
- mockAssetManager = mock(AssetManager.class);
-
- try {
- resultStream = new FileInputStream("./src/test/assets/gpx/gpx-test.gpx");
- expectedStream = new FileInputStream("./src/test/assets/gpx/gpx-test.gpx");
- when(mockContext.getAssets()).thenReturn(mockAssetManager);
- when(mockAssetManager.open("result.gpx")).thenReturn(resultStream);
- when(mockAssetManager.open("expected.gpx")).thenReturn(expectedStream);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- @Test
- public void convertFileName() {
- String result = CustomLayoutsUtils.convertFileName("public_transport.xml");
- String expected = "public transport";
- assertEquals(result, expected);
- }
-
- @Test
- public void unconvertFileName() {
- String result = CustomLayoutsUtils.unconvertFileName("public transport");
- String expected = "public_transport.xml";
- assertEquals(result, expected);
- }
-
- @Test
- public void createFileName() {
- String result = CustomLayoutsUtils.createFileName("public transport", "es");
- String expected = "public_transport_es.xml";
- assertEquals(result, expected);
- }
-
- @Test
- public void getStringFromStream() throws IOException {
- setupMocks();
-
- InputStream resultIs = mockAssetManager.open("result.gpx");
- String result = CustomLayoutsUtils.getStringFromStream(resultIs);
-
- String expected;
- try (InputStream expectedIs = mockAssetManager.open("expected.gpx");
- InputStreamReader expectedIsr = new InputStreamReader(expectedIs, StandardCharsets.UTF_8);
- BufferedReader expectedReader = new BufferedReader(expectedIsr)) {
- StringBuilder expectedBuilder = new StringBuilder();
- String line;
- while ((line = expectedReader.readLine()) != null) {
- expectedBuilder.append(line).append(System.lineSeparator());
- }
- expected = expectedBuilder.toString();
- }
- assertEquals("String should have same content", expected, result);
- }
-
- @Test
- public void getCurrentLayoutName() {
- setupMocks();
- String result = CustomLayoutsUtils.getCurrentLayoutName(mockContext);
- String expected = "transporte publico";
- assertEquals(result, expected);
- }
+ private Context context;
+ private SharedPreferences prefs;
+
+ @Before
+ public void setUp() {
+ context = ApplicationProvider.getApplicationContext();
+ prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ // Ensure a clean state for every test
+ prefs.edit().clear().apply();
+ }
+
+ @Test
+ public void convertFileName() {
+ assertEquals("public transport", CustomLayoutsUtils.convertFileName("public_transport.xml"));
+ assertEquals("simple", CustomLayoutsUtils.convertFileName("simple.xml"));
+ }
+
+ @Test
+ public void unconvertFileName() {
+ assertEquals("public_transport.xml", CustomLayoutsUtils.unconvertFileName("public transport"));
+ }
+
+ @Test
+ public void createFileName() {
+ assertEquals("public_transport_es.xml", CustomLayoutsUtils.createFileName("public transport", "es"));
+ }
+
+ @Test
+ public void getStringFromStream() throws IOException {
+ String content = "GPX Test Content" + System.lineSeparator() + "Second Line";
+ InputStream inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
+ String result = CustomLayoutsUtils.getStringFromStream(inputStream);
+ assertEquals(content, result);
+ }
+
+ @Test
+ public void getCurrentLayoutName() {
+ // Set value in real Robolectric preferences
+ prefs.edit().putString(OSMTracker.Preferences.KEY_UI_BUTTONS_LAYOUT, "transporte publico").apply();
+ String result = CustomLayoutsUtils.getCurrentLayoutName(context);
+ assertEquals("transporte publico", result);
+ }
+
+ @Test
+ public void getCurrentLayoutName_ReturnsDefaultWhenEmpty() {
+ // Test fallback logic
+ String result = CustomLayoutsUtils.getCurrentLayoutName(context);
+ assertEquals(OSMTracker.Preferences.VAL_UI_BUTTONS_LAYOUT, result);
+ }
}
diff --git a/app/src/test/java/net/osmtracker/util/ThemeValidatorTest.java b/app/src/test/java/net/osmtracker/util/ThemeValidatorTest.java
index 8eeedd0a8..78be7b670 100644
--- a/app/src/test/java/net/osmtracker/util/ThemeValidatorTest.java
+++ b/app/src/test/java/net/osmtracker/util/ThemeValidatorTest.java
@@ -2,90 +2,81 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
-import android.content.SharedPreferences.Editor;
import net.osmtracker.OSMTracker;
import net.osmtracker.R;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import static org.junit.Assert.*;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.verify;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import androidx.preference.PreferenceManager;
+import androidx.test.core.app.ApplicationProvider;
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(PreferenceManager.class)
-@PowerMockIgnore("jdk.internal.reflect.*")
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 25)
public class ThemeValidatorTest {
- Context mockContext;
- SharedPreferences mockPrefs;
- Resources mockRes;
- Editor mockEditor;
- /** Setup all the mocks(classes) that are used when calling the
- * ThemeValidator class with a selected theme
- * @param theme
- */
- public void setupMocks(String theme) {
+ private SharedPreferences realPrefs;
+ private Resources mockRes;
- mockPrefs = mock(SharedPreferences.class);
- when(mockPrefs.getString(OSMTracker.Preferences.KEY_UI_THEME,
- OSMTracker.Preferences.VAL_UI_THEME))
- .thenReturn(theme);
+ @Before
+ public void setUp() {
+ Context context = ApplicationProvider.getApplicationContext();
+ realPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+ // Ensure a clean state for every test
+ realPrefs.edit().clear().commit();
+ mockRes = mock(Resources.class);
+ String[] themes = {
+ "net.osmtracker:style/DefaultTheme",
+ "net.osmtracker:style/DarkTheme",
+ "net.osmtracker:style/LightTheme",
+ "net.osmtracker:style/HighContrast"
+ };
+ when(mockRes.getStringArray(R.array.prefs_theme_values)).thenReturn(themes);
+ }
- String[] themes = { "net.osmtracker:style/DefaultTheme",
- "net.osmtracker:style/DarkTheme",
- "net.osmtracker:style/LightTheme",
- "net.osmtracker:style/HighContrast"};
-
- mockRes = mock(Resources.class);
- when(mockRes.getStringArray(R.array.prefs_theme_values))
- .thenReturn(themes);
-
-
- mockContext = mock(Context.class);
-
- mockStatic(PreferenceManager.class);
-
- when(PreferenceManager.getDefaultSharedPreferences(mockContext)).thenReturn(mockPrefs);
-
- mockEditor=mock(SharedPreferences.Editor.class);
-
- when(mockPrefs.edit())
- .thenReturn(mockEditor);
-
- }
@Test
public void validateDefaultTheme(){
- setupMocks("net.osmtracker:style/DefaultTheme");
- String result =ThemeValidator.getValidTheme(mockPrefs, mockRes);
+ // Set a valid theme in preferences
+ realPrefs.edit().putString(
+ OSMTracker.Preferences.KEY_UI_THEME,
+ "net.osmtracker:style/DefaultTheme")
+ .commit();
+
+ String result =ThemeValidator.getValidTheme(realPrefs, mockRes);
String expected = "net.osmtracker:style/DefaultTheme";
- assertEquals(result, expected);
+ assertEquals(expected, result);
}
/*Use a theme that is not included on the theme values array and also
* verify methods of the mocked editor so that the preferences are saved.*/
@Test
public void validateWrongTheme(){
-
- setupMocks("net.osmtracker:style/YellowTheme");
- String result =ThemeValidator.getValidTheme(mockPrefs, mockRes);
- String expected = "net.osmtracker:style/DefaultTheme";
- assertEquals(result, expected);
- verify(mockPrefs,atLeastOnce()).edit();
- verify(mockEditor,atLeastOnce()).putString(OSMTracker.Preferences.KEY_UI_THEME, OSMTracker.Preferences.VAL_UI_THEME);
- verify(mockEditor).commit();
- }
+ // Set an invalid theme in preferences
+ realPrefs.edit().putString(
+ OSMTracker.Preferences.KEY_UI_THEME,
+ "net.osmtracker:style/YellowTheme")
+ .commit();
+
+ // The validator should detect "YellowTheme" is missing from the Resources array
+ // and reset it to the default.
+ String result = ThemeValidator.getValidTheme(realPrefs, mockRes);
+ String expected = "net.osmtracker:style/DefaultTheme";
+
+ assertEquals("Should fallback to DefaultTheme", expected, result);
+
+ // Verify that the preference was actually updated/repaired in the storage
+ assertEquals("Preference should be repaired in storage",
+ expected, realPrefs.getString(OSMTracker.Preferences.KEY_UI_THEME, null));
+ }
}
diff --git a/app/src/test/java/net/osmtracker/util/URLCreatorTest.java b/app/src/test/java/net/osmtracker/util/URLCreatorTest.java
index 4b4888f9a..8aac745dc 100644
--- a/app/src/test/java/net/osmtracker/util/URLCreatorTest.java
+++ b/app/src/test/java/net/osmtracker/util/URLCreatorTest.java
@@ -1,83 +1,62 @@
package net.osmtracker.util;
import android.content.Context;
-import android.content.SharedPreferences;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import static org.junit.Assert.*;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
-import androidx.preference.PreferenceManager;
+import androidx.test.core.app.ApplicationProvider;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(PreferenceManager.class)
-@PowerMockIgnore("jdk.internal.reflect.*")
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 25)
public class URLCreatorTest {
- Context mockContext;
- SharedPreferences mockPrefs;
-
-
- public void setupMocks(){
- // Create SharedPreferences mock
- mockPrefs = mock(SharedPreferences.class);
-
- UnitTestUtils.setLayoutsDefaultRepository(mockPrefs);
+ private Context context;
- // Create PreferenceManager mock
- mockContext = mock(Context.class);
- mockStatic(PreferenceManager.class);
- when(PreferenceManager.getDefaultSharedPreferences(mockContext)).thenReturn(mockPrefs);
-
- }
+ @Before
+ public void setUp() {
+ context = ApplicationProvider.getApplicationContext();
+ }
@Test
public void createMetadataDirUrl() {
- setupMocks();
- String result = URLCreator.createMetadataDirUrl(mockContext);
- String expected = "https://api.github.com/repos/labexp/osmtracker-android-layouts/contents/layouts/metadata?ref=master";
- assertEquals(result, expected);
+ String result = URLCreator.createMetadataDirUrl(context);
+ String expected = "https://api.github.com/repos/labexp/osmtracker-android-layouts/contents/layouts/metadata?ref=master";
+ assertEquals(expected, result);
}
@Test
public void createMetadataFileURL() {
- setupMocks();
- String result = URLCreator.createMetadataFileURL(mockContext, "transporte_publico");
+ String result = URLCreator.createMetadataFileURL(context, "transporte_publico");
String expected = "https://raw.githubusercontent.com/labexp/osmtracker-android-layouts/master/layouts/metadata/transporte_publico.xml";
- assertEquals(result, expected);
+ assertEquals(expected, result);
}
@Test
public void createLayoutFileURL() {
- setupMocks();
- String result = URLCreator.createLayoutFileURL(mockContext, "hidrantes","es");
+ String result = URLCreator.createLayoutFileURL(context, "hidrantes","es");
String expected = "https://raw.githubusercontent.com/labexp/osmtracker-android-layouts/master/layouts/hidrantes/es.xml";
- assertEquals(result, expected);
+ assertEquals(expected, result);
}
@Test
public void createIconsDirUrl() {
- setupMocks();
- String result = URLCreator.createIconsDirUrl(mockContext, "hidrantes");
+ String result = URLCreator.createIconsDirUrl(context, "hidrantes");
String expected = "https://api.github.com/repos/labexp/osmtracker-android-layouts/contents/layouts/hidrantes/hidrantes_icons?ref=master";
- assertEquals(result, expected);
+ assertEquals(expected, result);
}
@Test
public void createTestURL() {
- setupMocks();
String result = URLCreator.createTestURL("labexp", "osmtracker-android-layouts", "master");
String expected = "https://api.github.com/repos/labexp/osmtracker-android-layouts/contents/layouts/metadata?ref=master";
- assertEquals(result, expected);
+ assertEquals(expected, result);
}
}
diff --git a/app/src/test/java/net/osmtracker/util/UnitTestUtils.java b/app/src/test/java/net/osmtracker/util/UnitTestUtils.java
deleted file mode 100644
index e45472c8f..000000000
--- a/app/src/test/java/net/osmtracker/util/UnitTestUtils.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package net.osmtracker.util;
-
-import android.content.SharedPreferences;
-
-import net.osmtracker.OSMTracker;
-
-import java.util.Date;
-
-import static org.powermock.api.mockito.PowerMockito.when;
-
-public class UnitTestUtils {
-
- public static String TESTING_GITHUB_USER = "labexp";
- public static String TESTING_GITHUB_REPOSITORY = "osmtracker-android-layouts";
- public static String TESTING_GITHUB_BRANCH = "for_tests";
-
- public static void setGithubRepositorySettings(SharedPreferences mockPrefs, String user,
- String repo, String branch) {
- when(mockPrefs.getString(OSMTracker.Preferences.KEY_GITHUB_USERNAME,
- OSMTracker.Preferences.VAL_GITHUB_USERNAME))
- .thenReturn(user);
-
- when(mockPrefs.getString(OSMTracker.Preferences.KEY_REPOSITORY_NAME,
- OSMTracker.Preferences.VAL_REPOSITORY_NAME))
- .thenReturn(repo);
-
- when(mockPrefs.getString(OSMTracker.Preferences.KEY_BRANCH_NAME,
- OSMTracker.Preferences.VAL_BRANCH_NAME))
- .thenReturn(branch);
- }
-
- public static void setLayoutsTestingRepository(SharedPreferences mockPrefs){
- setGithubRepositorySettings(mockPrefs, TESTING_GITHUB_USER, TESTING_GITHUB_REPOSITORY,
- TESTING_GITHUB_BRANCH);
- }
-
- public static void setLayoutsDefaultRepository(SharedPreferences mockPrefs){
- setGithubRepositorySettings(mockPrefs, OSMTracker.Preferences.VAL_GITHUB_USERNAME,
- OSMTracker.Preferences.VAL_REPOSITORY_NAME,
- OSMTracker.Preferences.VAL_BRANCH_NAME);
- }
-
- // This method is used to hide the weird modifications (offsets) that need to be made when creating a Date object
- // See why here https://docs.oracle.com/javase/8/docs/api/java/util/Date.html#setYear-int-
- public static Date createDateFrom(int year, int month, int day, int hour, int minute, int second) {
- return new Date(year-1900, month-1, day, hour, minute, second);
- }
-}