diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java index 62f069b3ca..b7e1a60396 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -27,7 +27,7 @@ import fr.free.nrw.commons.di.CommonsApplicationComponent; import fr.free.nrw.commons.di.CommonsApplicationModule; import fr.free.nrw.commons.di.DaggerCommonsApplicationComponent; -import fr.free.nrw.commons.modifications.ModifierSequence; +import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.utils.FileUtils; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -168,7 +168,7 @@ private void updateAllDatabases() { dbOpenHelper.getReadableDatabase().close(); SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); - ModifierSequence.Table.onDelete(db); + ModifierSequenceDao.Table.onDelete(db); CategoryDao.Table.onDelete(db); ContributionDao.Table.onDelete(db); } diff --git a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java index c4b4a5258a..c8d33e3da7 100644 --- a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java @@ -5,7 +5,7 @@ import android.database.sqlite.SQLiteOpenHelper; import fr.free.nrw.commons.contributions.ContributionDao; -import fr.free.nrw.commons.modifications.ModifierSequence; +import fr.free.nrw.commons.modifications.ModifierSequenceDao; public class DBOpenHelper extends SQLiteOpenHelper { @@ -13,7 +13,8 @@ public class DBOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 6; /** - * Do not use, please call CommonsApplication.getDBOpenHelper() + * Do not use directly - @Inject an instance where it's needed and let + * dependency injection take care of managing this as a singleton. */ public DBOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -22,14 +23,14 @@ public DBOpenHelper(Context context) { @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { ContributionDao.Table.onCreate(sqLiteDatabase); - ModifierSequence.Table.onCreate(sqLiteDatabase); + ModifierSequenceDao.Table.onCreate(sqLiteDatabase); CategoryDao.Table.onCreate(sqLiteDatabase); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int from, int to) { ContributionDao.Table.onUpdate(sqLiteDatabase, from, to); - ModifierSequence.Table.onUpdate(sqLiteDatabase, from, to); + ModifierSequenceDao.Table.onUpdate(sqLiteDatabase, from, to); CategoryDao.Table.onUpdate(sqLiteDatabase, from, to); } } diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java index 6dc993c9e9..e1877e79c6 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java @@ -16,13 +16,15 @@ import fr.free.nrw.commons.data.DBOpenHelper; import timber.log.Timber; +import static fr.free.nrw.commons.modifications.ModifierSequenceDao.Table.TABLE_NAME; + public class ModificationsContentProvider extends ContentProvider { private static final int MODIFICATIONS = 1; private static final int MODIFICATIONS_ID = 2; public static final String AUTHORITY = "fr.free.nrw.commons.modifications.contentprovider"; - private static final String BASE_PATH = "modifications"; + public static final String BASE_PATH = "modifications"; public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH); @@ -47,7 +49,7 @@ public boolean onCreate() { @Override public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); - queryBuilder.setTables(ModifierSequence.Table.TABLE_NAME); + queryBuilder.setTables(TABLE_NAME); int uriType = uriMatcher.match(uri); @@ -78,7 +80,7 @@ public Uri insert(@NonNull Uri uri, ContentValues contentValues) { long id = 0; switch (uriType) { case MODIFICATIONS: - id = sqlDB.insert(ModifierSequence.Table.TABLE_NAME, null, contentValues); + id = sqlDB.insert(TABLE_NAME, null, contentValues); break; default: throw new IllegalArgumentException("Unknown URI: " + uri); @@ -94,7 +96,7 @@ public int delete(@NonNull Uri uri, String s, String[] strings) { switch (uriType) { case MODIFICATIONS_ID: String id = uri.getLastPathSegment(); - sqlDB.delete(ModifierSequence.Table.TABLE_NAME, + sqlDB.delete(TABLE_NAME, "_id = ?", new String[] { id } ); @@ -114,7 +116,7 @@ public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { case MODIFICATIONS: for (ContentValues value: values) { Timber.d("Inserting! %s", value); - sqlDB.insert(ModifierSequence.Table.TABLE_NAME, null, value); + sqlDB.insert(TABLE_NAME, null, value); } break; default: @@ -140,7 +142,7 @@ public int update(@NonNull Uri uri, ContentValues contentValues, String selectio int rowsUpdated = 0; switch (uriType) { case MODIFICATIONS: - rowsUpdated = sqlDB.update(ModifierSequence.Table.TABLE_NAME, + rowsUpdated = sqlDB.update(TABLE_NAME, contentValues, selection, selectionArgs); @@ -149,9 +151,9 @@ public int update(@NonNull Uri uri, ContentValues contentValues, String selectio int id = Integer.valueOf(uri.getLastPathSegment()); if (TextUtils.isEmpty(selection)) { - rowsUpdated = sqlDB.update(ModifierSequence.Table.TABLE_NAME, + rowsUpdated = sqlDB.update(TABLE_NAME, contentValues, - ModifierSequence.Table.COLUMN_ID + " = ?", + ModifierSequenceDao.Table.COLUMN_ID + " = ?", new String[] { String.valueOf(id) } ); } else { throw new IllegalArgumentException("Parameter `selection` should be empty when updating an ID"); diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java index ff7ed50499..d000a2ed51 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java @@ -83,8 +83,8 @@ public void onPerformSync(Account account, Bundle bundle, String s, ContentProvi contributionsClient = getContext().getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY); while (!allModifications.isAfterLast()) { - ModifierSequence sequence = ModifierSequence.fromCursor(allModifications); - sequence.setContentProviderClient(contentProviderClient); + ModifierSequence sequence = ModifierSequenceDao.fromCursor(allModifications); + ModifierSequenceDao dao = new ModifierSequenceDao(contributionsClient); Contribution contrib; Cursor contributionCursor; @@ -122,7 +122,7 @@ public void onPerformSync(Account account, Bundle bundle, String s, ContentProvi // FIXME: Log this somewhere else Timber.d("Non success result! %s", editResult); } else { - sequence.delete(); + dao.delete(sequence); } } allModifications.moveToNext(); diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java index 880b533137..93cb3bc3d5 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java @@ -1,14 +1,8 @@ package fr.free.nrw.commons.modifications; -import android.content.ContentProviderClient; -import android.content.ContentValues; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; import android.net.Uri; -import android.os.RemoteException; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; @@ -17,14 +11,13 @@ public class ModifierSequence { private Uri mediaUri; private ArrayList modifiers; private Uri contentUri; - private ContentProviderClient client; public ModifierSequence(Uri mediaUri) { this.mediaUri = mediaUri; modifiers = new ArrayList<>(); } - public ModifierSequence(Uri mediaUri, JSONObject data) { + ModifierSequence(Uri mediaUri, JSONObject data) { this(mediaUri); JSONArray modifiersJSON = data.optJSONArray("modifiers"); for (int i = 0; i < modifiersJSON.length(); i++) { @@ -32,7 +25,7 @@ public ModifierSequence(Uri mediaUri, JSONObject data) { } } - public Uri getMediaUri() { + Uri getMediaUri() { return mediaUri; } @@ -40,14 +33,14 @@ public void queueModifier(PageModifier modifier) { modifiers.add(modifier); } - public String executeModifications(String pageName, String pageContents) { + String executeModifications(String pageName, String pageContents) { for (PageModifier modifier: modifiers) { pageContents = modifier.doModification(pageName, pageContents); } return pageContents; } - public String getEditSummary() { + String getEditSummary() { StringBuilder editSummary = new StringBuilder(); for (PageModifier modifier: modifiers) { editSummary.append(modifier.getEditSumary()).append(" "); @@ -56,97 +49,16 @@ public String getEditSummary() { return editSummary.toString(); } - public JSONObject toJSON() { - JSONObject data = new JSONObject(); - try { - JSONArray modifiersJSON = new JSONArray(); - for (PageModifier modifier: modifiers) { - modifiersJSON.put(modifier.toJSON()); - } - data.put("modifiers", modifiersJSON); - return data; - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - - public ContentValues toContentValues() { - ContentValues cv = new ContentValues(); - cv.put(Table.COLUMN_MEDIA_URI, mediaUri.toString()); - cv.put(Table.COLUMN_DATA, toJSON().toString()); - return cv; + ArrayList getModifiers() { + return modifiers; } - public static ModifierSequence fromCursor(Cursor cursor) { - // Hardcoding column positions! - ModifierSequence ms = null; - try { - ms = new ModifierSequence(Uri.parse(cursor.getString(1)), - new JSONObject(cursor.getString(2))); - } catch (JSONException e) { - throw new RuntimeException(e); - } - ms.contentUri = ModificationsContentProvider.uriForId(cursor.getInt(0)); - - return ms; - } - - public void save() { - try { - if (contentUri == null) { - contentUri = client.insert(ModificationsContentProvider.BASE_URI, this.toContentValues()); - } else { - client.update(contentUri, toContentValues(), null, null); - } - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - public void delete() { - try { - client.delete(contentUri, null, null); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + Uri getContentUri() { + return contentUri; } - public void setContentProviderClient(ContentProviderClient client) { - this.client = client; + void setContentUri(Uri contentUri) { + this.contentUri = contentUri; } - public static class Table { - public static final String TABLE_NAME = "modifications"; - - public static final String COLUMN_ID = "_id"; - public static final String COLUMN_MEDIA_URI = "mediauri"; - public static final String COLUMN_DATA = "data"; - - // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES. - public static final String[] ALL_FIELDS = { - COLUMN_ID, - COLUMN_MEDIA_URI, - COLUMN_DATA - }; - - private static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " (" - + "_id INTEGER PRIMARY KEY," - + "mediauri STRING," - + "data STRING" - + ");"; - - public static void onCreate(SQLiteDatabase db) { - db.execSQL(CREATE_TABLE_STATEMENT); - } - - public static void onUpdate(SQLiteDatabase db, int from, int to) { - db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); - onCreate(db); - } - - public static void onDelete(SQLiteDatabase db) { - db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); - onCreate(db); - } - } } diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequenceDao.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequenceDao.java new file mode 100644 index 0000000000..c98081c726 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequenceDao.java @@ -0,0 +1,113 @@ +package fr.free.nrw.commons.modifications; + +import android.content.ContentProviderClient; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; +import android.os.RemoteException; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class ModifierSequenceDao { + + private final ContentProviderClient client; + + public ModifierSequenceDao(ContentProviderClient client) { + this.client = client; + } + + public static ModifierSequence fromCursor(Cursor cursor) { + // Hardcoding column positions! + ModifierSequence ms = null; + try { + ms = new ModifierSequence(Uri.parse(cursor.getString(1)), + new JSONObject(cursor.getString(2))); + } catch (JSONException e) { + throw new RuntimeException(e); + } + ms.setContentUri( ModificationsContentProvider.uriForId(cursor.getInt(0))); + + return ms; + } + + public void save(ModifierSequence sequence) { + try { + if (sequence.getContentUri() == null) { + sequence.setContentUri(client.insert(ModificationsContentProvider.BASE_URI, toContentValues(sequence))); + } else { + client.update(sequence.getContentUri(), toContentValues(sequence), null, null); + } + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + public void delete(ModifierSequence sequence) { + try { + client.delete(sequence.getContentUri(), null, null); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + private JSONObject toJSON(ModifierSequence sequence) { + JSONObject data = new JSONObject(); + try { + JSONArray modifiersJSON = new JSONArray(); + for (PageModifier modifier: sequence.getModifiers()) { + modifiersJSON.put(modifier.toJSON()); + } + data.put("modifiers", modifiersJSON); + return data; + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + private ContentValues toContentValues(ModifierSequence sequence) { + ContentValues cv = new ContentValues(); + cv.put(Table.COLUMN_MEDIA_URI, sequence.getMediaUri().toString()); + cv.put(Table.COLUMN_DATA, toJSON(sequence).toString()); + return cv; + } + + public static class Table { + static final String TABLE_NAME = "modifications"; + + static final String COLUMN_ID = "_id"; + static final String COLUMN_MEDIA_URI = "mediauri"; + static final String COLUMN_DATA = "data"; + + // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES. + public static final String[] ALL_FIELDS = { + COLUMN_ID, + COLUMN_MEDIA_URI, + COLUMN_DATA + }; + + static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME; + + static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " (" + + "_id INTEGER PRIMARY KEY," + + "mediauri STRING," + + "data STRING" + + ");"; + + public static void onCreate(SQLiteDatabase db) { + db.execSQL(CREATE_TABLE_STATEMENT); + } + + public static void onUpdate(SQLiteDatabase db, int from, int to) { + db.execSQL(DROP_TABLE_STATEMENT); + onCreate(db); + } + + public static void onDelete(SQLiteDatabase db) { + db.execSQL(DROP_TABLE_STATEMENT); + onCreate(db); + } + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java index 8858a11f5a..15acdcd4a2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java @@ -40,6 +40,7 @@ import fr.free.nrw.commons.modifications.CategoryModifier; import fr.free.nrw.commons.modifications.ModificationsContentProvider; import fr.free.nrw.commons.modifications.ModifierSequence; +import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.modifications.TemplateRemoveModifier; import fr.free.nrw.commons.mwapi.MediaWikiApi; import timber.log.Timber; @@ -165,15 +166,14 @@ private void multipleUploadBegins() { @Override public void onCategoriesSave(List categories) { if (categories.size() > 0) { - ContentProviderClient client = getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY); + ModifierSequenceDao dao = new ModifierSequenceDao(getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY)); for (Contribution contribution : photosList) { ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri()); categoriesSequence.queueModifier(new CategoryModifier(categories.toArray(new String[]{}))); categoriesSequence.queueModifier(new TemplateRemoveModifier("Uncategorized")); - categoriesSequence.setContentProviderClient(client); - categoriesSequence.save(); + dao.save(categoriesSequence); } } // FIXME: Make sure that the content provider is up diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 963ae3fd34..0992cbb312 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -39,7 +39,6 @@ import javax.inject.Named; import butterknife.ButterKnife; -import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.AuthenticatedActivity; import fr.free.nrw.commons.auth.SessionManager; @@ -50,8 +49,8 @@ import fr.free.nrw.commons.modifications.CategoryModifier; import fr.free.nrw.commons.modifications.ModificationsContentProvider; import fr.free.nrw.commons.modifications.ModifierSequence; +import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.modifications.TemplateRemoveModifier; -import fr.free.nrw.commons.mwapi.EventLog; import fr.free.nrw.commons.mwapi.MediaWikiApi; import timber.log.Timber; @@ -167,8 +166,8 @@ public void onCategoriesSave(List categories) { categoriesSequence.queueModifier(new CategoryModifier(categories.toArray(new String[]{}))); categoriesSequence.queueModifier(new TemplateRemoveModifier("Uncategorized")); - categoriesSequence.setContentProviderClient(getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY)); - categoriesSequence.save(); + ModifierSequenceDao dao = new ModifierSequenceDao(getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY)); + dao.save(categoriesSequence); } // FIXME: Make sure that the content provider is up diff --git a/app/src/test/java/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.java b/app/src/test/java/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.java new file mode 100644 index 0000000000..888c758ff6 --- /dev/null +++ b/app/src/test/java/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.java @@ -0,0 +1,181 @@ +package fr.free.nrw.commons.modifications; + +import android.content.ContentProviderClient; +import android.content.ContentValues; +import android.database.MatrixCursor; +import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; +import android.os.RemoteException; +import android.support.annotation.NonNull; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.Arrays; + +import fr.free.nrw.commons.BuildConfig; +import fr.free.nrw.commons.TestCommonsApplication; + +import static fr.free.nrw.commons.modifications.ModificationsContentProvider.BASE_URI; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(RobolectricTestRunner.class) +@Config(constants = BuildConfig.class, sdk = 21, application = TestCommonsApplication.class) +public class ModifierSequenceDaoTest { + + private static final String EXPECTED_MEDIA_URI = "http://example.com/"; + + @Mock + ContentProviderClient client; + @Mock + SQLiteDatabase database; + @Captor + ArgumentCaptor contentValuesCaptor; + + private ModifierSequenceDao testObject; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + testObject = new ModifierSequenceDao(client); + } + + @Test + public void createFromCursorWithEmptyModifiers() { + MatrixCursor cursor = createCursor(""); + + ModifierSequence seq = ModifierSequenceDao.fromCursor(cursor); + + assertEquals(EXPECTED_MEDIA_URI, seq.getMediaUri().toString()); + assertEquals(BASE_URI.buildUpon().appendPath("1").toString(), seq.getContentUri().toString()); + assertTrue(seq.getModifiers().isEmpty()); + } + + @Test + public void createFromCursorWtihCategoryModifier() { + MatrixCursor cursor = createCursor("{\"name\": \"CategoriesModifier\", \"data\": {}}"); + + ModifierSequence seq = ModifierSequenceDao.fromCursor(cursor); + + assertEquals(1, seq.getModifiers().size()); + assertTrue(seq.getModifiers().get(0) instanceof CategoryModifier); + } + + @Test + public void createFromCursorWithRemoveModifier() { + MatrixCursor cursor = createCursor("{\"name\": \"TemplateRemoverModifier\", \"data\": {}}"); + + ModifierSequence seq = ModifierSequenceDao.fromCursor(cursor); + + assertEquals(1, seq.getModifiers().size()); + assertTrue(seq.getModifiers().get(0) instanceof TemplateRemoveModifier); + } + + @Test + public void deleteSequence() throws Exception { + when(client.delete(isA(Uri.class), isNull(String.class), isNull(String[].class))).thenReturn(1); + ModifierSequence seq = ModifierSequenceDao.fromCursor(createCursor("")); + + testObject.delete(seq); + + verify(client).delete(eq(seq.getContentUri()), isNull(String.class), isNull(String[].class)); + } + + @Test(expected = RuntimeException.class) + public void deleteTranslatesRemoteExceptions() throws Exception { + when(client.delete(isA(Uri.class), isNull(String.class), isNull(String[].class))).thenThrow(new RemoteException("")); + ModifierSequence seq = ModifierSequenceDao.fromCursor(createCursor("")); + + testObject.delete(seq); + } + + @Test + public void saveExistingSequence() throws Exception { + String modifierJson = "{\"name\":\"CategoriesModifier\",\"data\":{}}"; + String expectedData = "{\"modifiers\":[" + modifierJson + "]}"; + MatrixCursor cursor = createCursor(modifierJson); + + testObject.save(ModifierSequenceDao.fromCursor(cursor)); + + verify(client).update(eq(ModifierSequenceDao.fromCursor(cursor).getContentUri()), contentValuesCaptor.capture(), isNull(String.class), isNull(String[].class)); + ContentValues cv = contentValuesCaptor.getValue(); + assertEquals(2, cv.size()); + assertEquals(EXPECTED_MEDIA_URI, cv.get(ModifierSequenceDao.Table.COLUMN_MEDIA_URI)); + assertEquals(expectedData, cv.get(ModifierSequenceDao.Table.COLUMN_DATA)); + } + + @Test + public void saveNewSequence() throws Exception { + Uri expectedContentUri = BASE_URI.buildUpon().appendPath("1").build(); + when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenReturn(expectedContentUri); + + ModifierSequence seq = new ModifierSequence(Uri.parse(EXPECTED_MEDIA_URI)); + testObject.save(seq); + + verify(client).insert(eq(ModificationsContentProvider.BASE_URI), contentValuesCaptor.capture()); + ContentValues cv = contentValuesCaptor.getValue(); + assertEquals(2, cv.size()); + assertEquals(EXPECTED_MEDIA_URI, cv.get(ModifierSequenceDao.Table.COLUMN_MEDIA_URI)); + assertEquals("{\"modifiers\":[]}", cv.get(ModifierSequenceDao.Table.COLUMN_DATA)); + assertEquals(expectedContentUri.toString(), seq.getContentUri().toString()); + } + + @Test(expected = RuntimeException.class) + public void saveTranslatesRemoteExceptions() throws Exception { + when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenThrow(new RemoteException("")); + + testObject.save(new ModifierSequence(Uri.parse(EXPECTED_MEDIA_URI))); + } + + @Test + public void createTable() { + ModifierSequenceDao.Table.onCreate(database); + + verify(database).execSQL(ModifierSequenceDao.Table.CREATE_TABLE_STATEMENT); + } + + @Test + public void updateTable() { + ModifierSequenceDao.Table.onUpdate(database, 1, 2); + + InOrder inOrder = inOrder(database); + inOrder.verify(database).execSQL(ModifierSequenceDao.Table.DROP_TABLE_STATEMENT); + inOrder.verify(database).execSQL(ModifierSequenceDao.Table.CREATE_TABLE_STATEMENT); + } + + @Test + public void deleteTable() { + ModifierSequenceDao.Table.onDelete(database); + + InOrder inOrder = inOrder(database); + inOrder.verify(database).execSQL(ModifierSequenceDao.Table.DROP_TABLE_STATEMENT); + inOrder.verify(database).execSQL(ModifierSequenceDao.Table.CREATE_TABLE_STATEMENT); + } + + @NonNull + private MatrixCursor createCursor(String modifierJson) { + MatrixCursor cursor = new MatrixCursor(new String[]{ + ModifierSequenceDao.Table.COLUMN_ID, + ModifierSequenceDao.Table.COLUMN_MEDIA_URI, + ModifierSequenceDao.Table.COLUMN_DATA + }, 1); + cursor.addRow(Arrays.asList("1", EXPECTED_MEDIA_URI, "{\"modifiers\": [" + modifierJson + "]}")); + cursor.moveToFirst(); + return cursor; + } +} \ No newline at end of file