use of com.ichi2.anki.CardBrowser.Column.CHANGED in project AnkiChinaAndroid by ankichinateam.
the class CardTemplateEditorTest method testEditTemplateContents.
@Test
@SuppressWarnings("PMD.NPathComplexity")
public void testEditTemplateContents() throws Exception {
String modelName = "Basic";
// Start the CardTemplateEditor with a specific model, and make sure the model starts unchanged
JSONObject collectionBasicModelOriginal = getCurrentDatabaseModelCopy(modelName);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra("modelId", collectionBasicModelOriginal.getLong("id"));
ActivityController<CardTemplateEditor> templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class, intent).create().start().resume().visible();
saveControllerForCleanup(templateEditorController);
CardTemplateEditor testEditor = (CardTemplateEditor) templateEditorController.get();
Assert.assertFalse("Model should not have changed yet", testEditor.modelHasChanged());
// Change the model and make sure it registers as changed, but the database is unchanged
EditText templateFront = testEditor.findViewById(R.id.front_edit);
String TEST_MODEL_QFMT_EDIT = "!@#$%^&*TEST*&^%$#@!";
templateFront.getText().append(TEST_MODEL_QFMT_EDIT);
advanceRobolectricLooper();
Assert.assertTrue("Model did not change after edit?", testEditor.modelHasChanged());
Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
// Kill and restart the Activity, make sure model edit is preserved
Bundle outBundle = new Bundle();
templateEditorController.saveInstanceState(outBundle);
templateEditorController.pause().stop().destroy();
templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class).create(outBundle).start().resume().visible();
saveControllerForCleanup(templateEditorController);
testEditor = (CardTemplateEditor) templateEditorController.get();
ShadowActivity shadowTestEditor = shadowOf(testEditor);
Assert.assertTrue("model change not preserved across activity lifecycle?", testEditor.modelHasChanged());
Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
// Make sure we get a confirmation dialog if we hit the back button
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(android.R.id.home));
advanceRobolectricLooper();
Assert.assertEquals("Wrong dialog shown?", getDialogText(true), getResourceString(R.string.discard_unsaved_changes));
clickDialogButton(DialogAction.NEGATIVE, true);
advanceRobolectricLooper();
Assert.assertTrue("model change not preserved despite canceling back button?", testEditor.modelHasChanged());
// Make sure we things are cleared out after a cancel
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(android.R.id.home));
Assert.assertEquals("Wrong dialog shown?", getDialogText(true), getResourceString(R.string.discard_unsaved_changes));
clickDialogButton(DialogAction.POSITIVE, true);
advanceRobolectricLooper();
Assert.assertFalse("model change not cleared despite discarding changes?", testEditor.modelHasChanged());
// Get going for content edit assertions again...
templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class, intent).create().start().resume().visible();
saveControllerForCleanup(templateEditorController);
testEditor = (CardTemplateEditor) templateEditorController.get();
shadowTestEditor = shadowOf(testEditor);
templateFront = testEditor.findViewById(R.id.front_edit);
templateFront.getText().append(TEST_MODEL_QFMT_EDIT);
advanceRobolectricLooper();
Assert.assertTrue("Model did not change after edit?", testEditor.modelHasChanged());
// Make sure we pass the edit to the Previewer
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_preview));
advanceRobolectricLooper();
Intent startedIntent = shadowTestEditor.getNextStartedActivity();
ShadowIntent shadowIntent = shadowOf(startedIntent);
advanceRobolectricLooper();
Assert.assertEquals("Previewer not started?", CardTemplatePreviewer.class.getName(), shadowIntent.getIntentClass().getName());
Assert.assertNotNull("intent did not have model JSON filename?", startedIntent.getStringExtra(TemporaryModel.INTENT_MODEL_FILENAME));
Assert.assertNotEquals("Model sent to Previewer is unchanged?", testEditor.getTempModel().getModel(), TemporaryModel.getTempModel(startedIntent.getStringExtra(TemporaryModel.INTENT_MODEL_FILENAME)));
Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
shadowTestEditor.receiveResult(startedIntent, Activity.RESULT_OK, new Intent());
// Save the template then fetch it from the collection to see if it was saved correctly
JSONObject testEditorModelEdited = testEditor.getTempModel().getModel();
advanceRobolectricLooper();
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_confirm));
advanceRobolectricLooper();
JSONObject collectionBasicModelCopyEdited = getCurrentDatabaseModelCopy(modelName);
Assert.assertNotEquals("model is unchanged?", collectionBasicModelOriginal, collectionBasicModelCopyEdited);
Assert.assertEquals("model did not save?", testEditorModelEdited.toString().trim(), collectionBasicModelCopyEdited.toString().trim());
Assert.assertTrue("model does not have our change?", collectionBasicModelCopyEdited.toString().contains(TEST_MODEL_QFMT_EDIT));
}
use of com.ichi2.anki.CardBrowser.Column.CHANGED in project AnkiChinaAndroid by ankichinateam.
the class CardTemplateEditorTest method testDeletePendingAddExistingCardCount.
/**
* Deleting a template you just added - but in the same ordinal as a previous pending delete - should get it's card count correct
*/
@SuppressWarnings("PMD.ExcessiveMethodLength")
@Test
public void testDeletePendingAddExistingCardCount() {
String modelName = "Basic (optional reversed card)";
Model collectionBasicModelOriginal = getCurrentDatabaseModelCopy(modelName);
// Start the CardTemplateEditor with a specific model, and make sure the model starts unchanged
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra("modelId", collectionBasicModelOriginal.getLong("id"));
ActivityController<CardTemplateEditor> templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class, intent).create().start().resume().visible();
saveControllerForCleanup(templateEditorController);
CardTemplateEditor testEditor = (CardTemplateEditor) templateEditorController.get();
Assert.assertFalse("Model should not have changed yet", testEditor.modelHasChanged());
Assert.assertEquals("Model should have 2 templates now", 2, testEditor.getTempModel().getTemplateCount());
Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 0));
Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 1));
// Create note with forward and back info
Note selectiveGeneratedNote = getCol().newNote(collectionBasicModelOriginal);
selectiveGeneratedNote.setField(0, "TestFront");
selectiveGeneratedNote.setField(1, "TestBack");
selectiveGeneratedNote.setField(2, "y");
getCol().addNote(selectiveGeneratedNote);
Assert.assertEquals("card generation should result in two cards", 2, getModelCardCount(collectionBasicModelOriginal));
// Delete ord 1 / 'Card 2' and check the message
ShadowActivity shadowTestEditor = shadowOf(testEditor);
testEditor.mViewPager.setCurrentItem(1);
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_delete));
advanceRobolectricLooper();
Assert.assertEquals("Did not show dialog about deleting template and it's card?", getQuantityString(R.plurals.card_template_editor_confirm_delete, 1, 1, "Card 2"), getDialogText(true));
clickDialogButton(DialogAction.POSITIVE, true);
advanceRobolectricLooper();
Assert.assertTrue("Model should have changed", testEditor.modelHasChanged());
Assert.assertNotNull("Cannot delete template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0 }));
Assert.assertNotNull("Cannot delete template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 1 }));
Assert.assertNull("Can delete both templates?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0, 1 }));
Assert.assertEquals("Change in database despite no save?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
Assert.assertEquals("Model should have 1 template", 1, testEditor.getTempModel().getTemplateCount());
// Add a template - click add, click confirm for card add, click confirm again for full sync
shadowTestEditor.clickMenuItem(R.id.action_add);
advanceRobolectricLooper();
Assert.assertTrue("Model should have changed", testEditor.modelHasChanged());
Assert.assertEquals("Change added but not adjusted correctly?", 1, TemporaryModel.getAdjustedAddOrdinalAtChangeIndex(testEditor.getTempModel(), 1));
Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 0));
Assert.assertTrue("Ordinal not pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 1));
Assert.assertEquals("Model should have 2 templates", 2, testEditor.getTempModel().getTemplateCount());
// Delete ord 1 / 'Card 2' again and check the message - it's in the same spot as the pre-existing template but there are no cards actually associated
testEditor.mViewPager.setCurrentItem(1);
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_delete));
advanceRobolectricLooper();
Assert.assertEquals("Did not show dialog about deleting template and it's card?", getQuantityString(R.plurals.card_template_editor_confirm_delete, 0, 0, "Card 2"), getDialogText(true));
clickDialogButton(DialogAction.POSITIVE, true);
advanceRobolectricLooper();
Assert.assertTrue("Model should have changed", testEditor.modelHasChanged());
Assert.assertNotNull("Cannot delete template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0 }));
Assert.assertNotNull("Cannot delete template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 1 }));
Assert.assertNull("Can delete both templates?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0, 1 }));
Assert.assertEquals("Change in database despite no save?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
Assert.assertEquals("Model should have 1 template", 1, testEditor.getTempModel().getTemplateCount());
// Save it out and make some assertions
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_confirm));
advanceRobolectricLooper();
Assert.assertFalse("Model should now be unchanged", testEditor.modelHasChanged());
Assert.assertEquals("card generation should result in 1 card", 1, getModelCardCount(collectionBasicModelOriginal));
}
use of com.ichi2.anki.CardBrowser.Column.CHANGED in project AnkiChinaAndroid by ankichinateam.
the class CardTemplateEditorTest method testTemplateAdd.
@Test
public void testTemplateAdd() throws Exception {
// Make sure we test previewing a new card template - not working for real yet
String modelName = "Basic";
JSONObject collectionBasicModelOriginal = getCurrentDatabaseModelCopy(modelName);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra("modelId", collectionBasicModelOriginal.getLong("id"));
ActivityController<CardTemplateEditor> templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class, intent).create().start().resume().visible();
saveControllerForCleanup(templateEditorController);
CardTemplateEditor testEditor = (CardTemplateEditor) templateEditorController.get();
Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 0));
// Try to add a template - click add, click confirm for card add, click confirm again for full sync
ShadowActivity shadowTestEditor = shadowOf(testEditor);
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_add));
advanceRobolectricLooper();
// if AnkiDroid moves to match AnkiDesktop it will pop a dialog to confirm card create
// Assert.assertEquals("Wrong dialog shown?", "This will create NN cards. Proceed?", getDialogText());
// clickDialogButton(DialogAction.POSITIVE);
Assert.assertTrue("Model should have changed", testEditor.modelHasChanged());
Assert.assertEquals("Change not pending add?", 1, TemporaryModel.getAdjustedAddOrdinalAtChangeIndex(testEditor.getTempModel(), 0));
Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 0));
Assert.assertTrue("Ordinal not pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 1));
Assert.assertEquals("Model should have 2 templates now", 2, testEditor.getTempModel().getTemplateCount());
// Make sure we pass the new template to the Previewer
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_preview));
Intent startedIntent = shadowTestEditor.getNextStartedActivity();
ShadowIntent shadowIntent = shadowOf(startedIntent);
Assert.assertEquals("Previewer not started?", CardTemplatePreviewer.class.getName(), shadowIntent.getIntentClass().getName());
Assert.assertNotNull("intent did not have model JSON filename?", startedIntent.getStringExtra(TemporaryModel.INTENT_MODEL_FILENAME));
Assert.assertEquals("intent did not have ordinal?", 1, startedIntent.getIntExtra("ordinal", -1));
Assert.assertNotEquals("Model sent to Previewer is unchanged?", testEditor.getTempModel().getModel(), TemporaryModel.getTempModel(startedIntent.getStringExtra(TemporaryModel.INTENT_MODEL_FILENAME)));
Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
// Save the change to the database and make sure there are two templates after
JSONObject testEditorModelEdited = testEditor.getTempModel().getModel();
Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_confirm));
advanceRobolectricLooper();
JSONObject collectionBasicModelCopyEdited = getCurrentDatabaseModelCopy(modelName);
Assert.assertNotEquals("model is unchanged?", collectionBasicModelOriginal, collectionBasicModelCopyEdited);
Assert.assertEquals("model did not save?", testEditorModelEdited.toString().trim(), collectionBasicModelCopyEdited.toString().trim());
}
use of com.ichi2.anki.CardBrowser.Column.CHANGED in project AnkiChinaAndroid by ankichinateam.
the class ImportTest method testAnki2DiffmodelTemplates.
@Test
public void testAnki2DiffmodelTemplates() throws IOException, JSONException, ImportExportException {
// different from the above as this one tests only the template text being
// changed, not the number of cards/fields
// import the first version of the model
String tmp = Shared.getTestFilePath(InstrumentationRegistry.getInstrumentation().getTargetContext(), "diffmodeltemplates-1.apkg");
AnkiPackageImporter imp = new AnkiPackageImporter(testCol, tmp);
imp.setDupeOnSchemaChange(true);
imp.run();
// then the version with updated template
tmp = Shared.getTestFilePath(InstrumentationRegistry.getInstrumentation().getTargetContext(), "diffmodeltemplates-2.apkg");
imp = new AnkiPackageImporter(testCol, tmp);
imp.setDupeOnSchemaChange(true);
imp.run();
// collection should contain the note we imported
assertEquals(1, testCol.noteCount());
// the front template should contain the text added in the 2nd package
Long tcid = testCol.findCards("").get(0);
Note tnote = testCol.getCard(tcid).note();
assertTrue(testCol.findTemplates(tnote).get(0).getString("qfmt").contains("Changed Front Template"));
}
use of com.ichi2.anki.CardBrowser.Column.CHANGED in project AnkiChinaAndroid by ankichinateam.
the class Anki2Importer method _importNotes.
/**
* Notes
* ***********************************************************
*/
private void _importNotes() {
// build guid -> (id,mod,mid) hash & map of existing note ids
mNotes = new HashMap<>();
Set<Long> existing = new HashSet<>();
Cursor cur = null;
try {
cur = mDst.getDb().getDatabase().query("select id, guid, mod, mid from notes", null);
while (cur.moveToNext()) {
long id = cur.getLong(0);
String guid = cur.getString(1);
long mod = cur.getLong(2);
long mid = cur.getLong(3);
mNotes.put(guid, new Object[] { id, mod, mid });
existing.add(id);
}
} finally {
if (cur != null) {
cur.close();
}
}
// we may need to rewrite the guid if the model schemas don't match,
// so we need to keep track of the changes for the card import stage
mChangedGuids = new HashMap<>();
// we ignore updates to changed schemas. we need to note the ignored
// guids, so we avoid importing invalid cards
mIgnoredGuids = new HashMap<>();
// iterate over source collection
ArrayList<Object[]> add = new ArrayList<>();
int totalAddCount = 0;
final int thresExecAdd = 1000;
ArrayList<Object[]> update = new ArrayList<>();
int totalUpdateCount = 0;
final int thresExecUpdate = 1000;
ArrayList<Long> dirty = new ArrayList<>();
int totalDirtyCount = 0;
final int thresExecDirty = 1000;
int usn = mDst.usn();
int dupes = 0;
ArrayList<String> dupesIgnored = new ArrayList<>();
try {
mDst.getDb().getDatabase().beginTransaction();
cur = mSrc.getDb().getDatabase().query("select * from notes", null);
// Counters for progress updates
int total = cur.getCount();
boolean largeCollection = total > 200;
int onePercent = total / 100;
int i = 0;
while (cur.moveToNext()) {
// turn the db result into a mutable list
Object[] note = new Object[] { cur.getLong(0), cur.getString(1), cur.getLong(2), cur.getLong(3), cur.getInt(4), cur.getString(5), cur.getString(6), cur.getString(7), cur.getLong(8), cur.getInt(9), cur.getString(10) };
boolean shouldAdd = _uniquifyNote(note);
if (shouldAdd) {
// ensure id is unique
while (existing.contains(note[0])) {
note[0] = ((Long) note[0]) + 999;
}
existing.add((Long) note[0]);
// bump usn
note[4] = usn;
// update media references in case of dupes
note[6] = _mungeMedia((Long) note[MID], (String) note[6]);
add.add(note);
dirty.add((Long) note[0]);
// note we have the added guid
mNotes.put((String) note[GUID], new Object[] { note[0], note[3], note[MID] });
} else {
// a duplicate or changed schema - safe to update?
dupes += 1;
if (mAllowUpdate) {
Object[] n = mNotes.get(note[GUID]);
long oldNid = (Long) n[0];
long oldMod = (Long) n[1];
long oldMid = (Long) n[2];
// will update if incoming note more recent
if (oldMod < (Long) note[MOD]) {
// safe if note types identical
if (oldMid == (Long) note[MID]) {
// incoming note should use existing id
note[0] = oldNid;
note[4] = usn;
note[6] = _mungeMedia((Long) note[MID], (String) note[6]);
update.add(note);
dirty.add((Long) note[0]);
} else {
dupesIgnored.add(String.format("%s: %s", mCol.getModels().get(oldMid).getString("name"), ((String) note[6]).replace("\u001f", ",")));
mIgnoredGuids.put((String) note[GUID], true);
}
}
}
}
i++;
// add to col partially, so as to avoid OOM
if (add.size() >= thresExecAdd) {
totalAddCount += add.size();
addNotes(add);
add.clear();
Timber.d("add notes: %d", totalAddCount);
}
// add to col partially, so as to avoid OOM
if (update.size() >= thresExecUpdate) {
totalUpdateCount += update.size();
updateNotes(update);
update.clear();
Timber.d("update notes: %d", totalUpdateCount);
}
// add to col partially, so as to avoid OOM
if (dirty.size() >= thresExecDirty) {
totalDirtyCount += dirty.size();
long[] das = Utils.collection2Array(dirty);
mDst.updateFieldCache(das);
mDst.getTags().registerNotes(das);
dirty.clear();
Timber.d("dirty notes: %d", totalDirtyCount);
}
if (total != 0 && (!largeCollection || i % onePercent == 0)) {
// Calls to publishProgress are reasonably expensive due to res.getString()
publishProgress(i * 100 / total, 0, 0);
}
}
publishProgress(100, 0, 0);
// summarize partial add/update/dirty results for total values
totalAddCount += add.size();
totalUpdateCount += update.size();
totalDirtyCount += dirty.size();
if (dupes > 0) {
mLog.add(getRes().getString(R.string.import_update_details, totalUpdateCount, dupes));
if (dupesIgnored.size() > 0) {
mLog.add(getRes().getString(R.string.import_update_ignored));
}
}
// export info for calling code
mDupes = dupes;
mAdded = totalAddCount;
mUpdated = totalUpdateCount;
Timber.d("add notes total: %d", totalAddCount);
Timber.d("update notes total: %d", totalUpdateCount);
Timber.d("dirty notes total: %d", totalDirtyCount);
// add to col (for last chunk)
addNotes(add);
add.clear();
updateNotes(update);
update.clear();
mDst.getDb().getDatabase().setTransactionSuccessful();
} finally {
if (cur != null) {
cur.close();
}
if (mDst.getDb().getDatabase().inTransaction()) {
try {
mDst.getDb().getDatabase().endTransaction();
} catch (Exception e) {
Timber.w(e);
}
}
}
long[] das = Utils.collection2Array(dirty);
mDst.updateFieldCache(das);
mDst.getTags().registerNotes(das);
}
Aggregations