Search in sources :

Example 41 with Model

use of com.ichi2.libanki.Model in project AnkiChinaAndroid by ankichinateam.

the class TemporaryModelTest method testAddDeleteTracking.

@Test
public void testAddDeleteTracking() {
    // Assume you start with a 2 template model (like "Basic (and reversed)")
    // Add a 3rd new template, remove the 2nd, remove the 1st, add a new now-2nd, remove 1st again
    // ...and it should reduce to just removing the original 1st/2nd and adding the final as first
    TemporaryModel tempModel = new TemporaryModel(new Model("{ foo: bar }"));
    tempModel.addTemplateChange(ADD, 3);
    Object[][] expected1 = { { 3, ADD } };
    // 3 templates and one change now
    assertTemplateChangesEqual(expected1, tempModel.getTemplateChanges());
    assertTemplateChangesEqual(expected1, tempModel.getAdjustedTemplateChanges());
    Assert.assertArrayEquals(new int[] { 3 }, tempModel.getDeleteDbOrds(3));
    tempModel.addTemplateChange(DELETE, 2);
    // 2 templates and two changes now
    Object[][] expected2 = { { 3, ADD }, { 2, DELETE } };
    Object[][] adjExpected2 = { { 2, ADD }, { 2, DELETE } };
    assertTemplateChangesEqual(expected2, tempModel.getTemplateChanges());
    assertTemplateChangesEqual(adjExpected2, tempModel.getAdjustedTemplateChanges());
    Assert.assertArrayEquals(new int[] { 2, 4 }, tempModel.getDeleteDbOrds(3));
    tempModel.addTemplateChange(DELETE, 1);
    // 1 template and three changes now
    Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
    Object[][] expected3 = { { 3, ADD }, { 2, DELETE }, { 1, DELETE } };
    Object[][] adjExpected3 = { { 1, ADD }, { 2, DELETE }, { 1, DELETE } };
    assertTemplateChangesEqual(expected3, tempModel.getTemplateChanges());
    assertTemplateChangesEqual(adjExpected3, tempModel.getAdjustedTemplateChanges());
    tempModel.addTemplateChange(ADD, 2);
    // 2 templates and 4 changes now
    Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
    Object[][] expected4 = { { 3, ADD }, { 2, DELETE }, { 1, DELETE }, { 2, ADD } };
    Object[][] adjExpected4 = { { 1, ADD }, { 2, DELETE }, { 1, DELETE }, { 2, ADD } };
    assertTemplateChangesEqual(expected4, tempModel.getTemplateChanges());
    assertTemplateChangesEqual(adjExpected4, tempModel.getAdjustedTemplateChanges());
    // Make sure we can resurrect these changes across lifecycle
    Bundle outBundle = tempModel.toBundle();
    assertTemplateChangesEqual(expected4, outBundle.getSerializable("mTemplateChanges"));
    // This is the hard part. We will delete a template we added so everything shifts.
    // The template currently at ordinal 1 was added as template 3 at the start before it slid down on the deletes
    // So the first template add should be negated by this delete, and the second template add should slide down to 1
    tempModel.addTemplateChange(DELETE, 1);
    // 1 template and 3 changes now (the delete just cancelled out one of the adds)
    Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
    Object[][] expected5 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD } };
    Object[][] adjExpected5 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD } };
    assertTemplateChangesEqual(expected5, tempModel.getTemplateChanges());
    assertTemplateChangesEqual(adjExpected5, tempModel.getAdjustedTemplateChanges());
    tempModel.addTemplateChange(ADD, 2);
    // 2 template and 4 changes now (the delete just cancelled out one of the adds)
    Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
    Object[][] expected6 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD } };
    Object[][] adjExpected6 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD } };
    assertTemplateChangesEqual(expected6, tempModel.getTemplateChanges());
    assertTemplateChangesEqual(adjExpected6, tempModel.getAdjustedTemplateChanges());
    tempModel.addTemplateChange(ADD, 3);
    // 2 template and 4 changes now (the delete just cancelled out one of the adds)
    Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
    Object[][] expected7 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD }, { 3, ADD } };
    Object[][] adjExpected7 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD }, { 3, ADD } };
    assertTemplateChangesEqual(expected7, tempModel.getTemplateChanges());
    assertTemplateChangesEqual(adjExpected7, tempModel.getAdjustedTemplateChanges());
    tempModel.addTemplateChange(DELETE, 3);
    // 1 template and 3 changes now (two deletes cancelled out adds)
    Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
    Object[][] expected8 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD } };
    Object[][] adjExpected8 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD } };
    assertTemplateChangesEqual(expected8, tempModel.getTemplateChanges());
    assertTemplateChangesEqual(adjExpected8, tempModel.getAdjustedTemplateChanges());
}
Also used : Bundle(android.os.Bundle) Model(com.ichi2.libanki.Model) Test(org.junit.Test)

Example 42 with Model

use of com.ichi2.libanki.Model in project AnkiChinaAndroid by ankichinateam.

the class TemporaryModelTest method testTempModelStorage.

@Test
public void testTempModelStorage() throws Exception {
    // Start off with clean state in the cache dir
    TemporaryModel.clearTempModelFiles();
    // Make sure save / retrieve works
    String tempModelPath = TemporaryModel.saveTempModel(getTargetContext(), new JSONObject("{foo: bar}"));
    Assert.assertNotNull("Saving temp model unsuccessful", tempModelPath);
    JSONObject tempModel = TemporaryModel.getTempModel(tempModelPath);
    Assert.assertNotNull("Temp model not read successfully", tempModel);
    Assert.assertEquals(new JSONObject("{foo: bar}").toString(), tempModel.toString());
    // Make sure clearing works
    Assert.assertEquals(1, TemporaryModel.clearTempModelFiles());
    Timber.i("The following logged NoSuchFileException is an expected part of verifying a file delete.");
    try {
        TemporaryModel.getTempModel(tempModelPath);
        Assert.fail("Should have caught an exception here because the file is missing");
    } catch (IOException e) {
    // this is expected
    }
}
Also used : JSONObject(com.ichi2.utils.JSONObject) IOException(java.io.IOException) Test(org.junit.Test)

Example 43 with Model

use of com.ichi2.libanki.Model in project AnkiChinaAndroid by ankichinateam.

the class CardTemplateEditorTest method testDeleteTemplate.

@Test
public void testDeleteTemplate() {
    String modelName = "Basic (and reversed card)";
    // 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());
    Assert.assertEquals("Model should have 2 templates now", 2, testEditor.getTempModel().getTemplateCount());
    // Try to delete the template - click delete, click confirm for card delete, click confirm again for full sync
    ShadowActivity shadowTestEditor = shadowOf(testEditor);
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_delete));
    advanceRobolectricLooper();
    Assert.assertEquals("Wrong dialog shown?", "Delete the “Card 1” card type, and its 0 cards?", getDialogText(true));
    clickDialogButton(DialogAction.POSITIVE, true);
    advanceRobolectricLooper();
    Assert.assertTrue("Model should have changed", testEditor.modelHasChanged());
    Assert.assertEquals("Model should have 1 template now", 1, testEditor.getTempModel().getTemplateCount());
    // Try to delete the template again, but there's only one
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_delete));
    advanceRobolectricLooper();
    Assert.assertEquals("Did not show dialog about deleting only card?", getResourceString(R.string.card_template_editor_cant_delete), getDialogText(true));
    Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
    // Save the change to the database and make sure there's only one template 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());
}
Also used : JSONObject(com.ichi2.utils.JSONObject) ShadowActivity(org.robolectric.shadows.ShadowActivity) ShadowIntent(org.robolectric.shadows.ShadowIntent) Intent(android.content.Intent) Test(org.junit.Test)

Example 44 with Model

use of com.ichi2.libanki.Model in project AnkiChinaAndroid by ankichinateam.

the class CardTemplateEditorTest method testDeleteTemplateWithSelectivelyGeneratedCards.

/**
 * In a model with two card templates using different fields, some notes may only use card 1,
 * and some may only use card 2. If you delete the 2nd template,
 * it will cause the notes that only use card 2 to disappear.
 *
 * So the unit test would then be to make a model like the "basic (optional reverse card)"
 * with two fields Enable1 and Enable2, and two templates "card 1" and "card 2".
 * Both cards use selective generation, so they're empty unless the corresponding field is set.
 *
 * So then in the unit test you make the model, add the two templates, then you add two notes,
 * with Enable1 and Enable2 respectively set to "y".
 * Then you try to delete one of the templates and it should fail
 *
 * (question: but I thought deleting one should work - still one card left to maintain the note,
 * and second template delete should fail since we finally get to a place where no cards are left?
 * I am having trouble creating selectively generated cards though - I can do one optional field but not 2 ugh)
 */
@Test
public void testDeleteTemplateWithSelectivelyGeneratedCards() {
    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));
    // Try to delete Card 1 template - click delete, check confirm for card delete popup indicating it was possible, then dismiss it
    ShadowActivity shadowTestEditor = shadowOf(testEditor);
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_delete));
    advanceRobolectricLooper();
    Assert.assertEquals("Wrong dialog shown?", "Delete the “Card 1” card type, and its 0 cards?", getDialogText(true));
    clickDialogButton(DialogAction.NEGATIVE, true);
    advanceRobolectricLooper();
    Assert.assertFalse("Model should not have changed", testEditor.modelHasChanged());
    // Create note with forward and back info, Add Reverse is empty, so should only be one card
    Note selectiveGeneratedNote = getCol().newNote(collectionBasicModelOriginal);
    selectiveGeneratedNote.setField(0, "TestFront");
    selectiveGeneratedNote.setField(1, "TestBack");
    String[] fields = selectiveGeneratedNote.getFields();
    for (String field : fields) {
        Timber.d("Got a field: %s", field);
    }
    getCol().addNote(selectiveGeneratedNote);
    Assert.assertEquals("selective generation should result in one card", 1, getModelCardCount(collectionBasicModelOriginal));
    // Try to delete the template again, but there's selective generation means it would orphan the note
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_delete));
    advanceRobolectricLooper();
    Assert.assertEquals("Did not show dialog about deleting only card?", getResourceString(R.string.card_template_editor_would_delete_note), getDialogText(true));
    clickDialogButton(DialogAction.POSITIVE, true);
    advanceRobolectricLooper();
    Assert.assertNull("Can delete used template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0 }));
    Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 0));
    Assert.assertEquals("Change incorrectly added to list?", 0, testEditor.getTempModel().getTemplateChanges().size());
    // Assert can delete 'Card 2'
    Assert.assertNotNull("Cannot delete unused template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 1 }));
    // Edit note to have Add Reverse set to 'y' so we get a second card
    selectiveGeneratedNote.setField(2, "y");
    selectiveGeneratedNote.flush();
    // - assert two cards
    Assert.assertEquals("should be two cards now", 2, getModelCardCount(collectionBasicModelOriginal));
    // - assert can delete either Card template but not both
    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 }));
    // A couple more notes to make sure things are okay
    Note secondNote = getCol().newNote(collectionBasicModelOriginal);
    secondNote.setField(0, "TestFront2");
    secondNote.setField(1, "TestBack2");
    secondNote.setField(2, "y");
    getCol().addNote(secondNote);
    // - assert can delete either Card template but not both
    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 }));
}
Also used : Note(com.ichi2.libanki.Note) Model(com.ichi2.libanki.Model) ShadowActivity(org.robolectric.shadows.ShadowActivity) ShadowIntent(org.robolectric.shadows.ShadowIntent) Intent(android.content.Intent) Test(org.junit.Test)

Example 45 with Model

use of com.ichi2.libanki.Model in project AnkiChinaAndroid by ankichinateam.

the class CardTemplatePreviewerTest method testPreviewUnsavedTemplate.

@Test
public void testPreviewUnsavedTemplate() throws Exception {
    String modelName = "Basic";
    Model collectionBasicModelOriginal = getCurrentDatabaseModelCopy(modelName);
    JSONObject template = collectionBasicModelOriginal.getJSONArray("tmpls").getJSONObject(0);
    template.put("qfmt", template.getString("qfmt").concat("PREVIEWER_TEST"));
    String tempModelPath = TemporaryModel.saveTempModel(getTargetContext(), collectionBasicModelOriginal);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.putExtra(TemporaryModel.INTENT_MODEL_FILENAME, tempModelPath);
    intent.putExtra("index", 0);
    ActivityController<TestCardTemplatePreviewer> previewerController = Robolectric.buildActivity(TestCardTemplatePreviewer.class, intent).create().start().resume().visible();
    saveControllerForCleanup((previewerController));
    TestCardTemplatePreviewer testCardTemplatePreviewer = (TestCardTemplatePreviewer) previewerController.get();
    Assert.assertTrue("model change did not show up?", testCardTemplatePreviewer.getDummyCard(collectionBasicModelOriginal, 0).q().contains("PREVIEWER_TEST") && testCardTemplatePreviewer.getDummyCard(collectionBasicModelOriginal, 0).a().contains("PREVIEWER_TEST"));
    // Take it through a destroy/re-create lifecycle in order to test instance state persistence
    Bundle outBundle = new Bundle();
    previewerController.saveInstanceState(outBundle);
    previewerController.pause().stop().destroy();
    previewerController = Robolectric.buildActivity(TestCardTemplatePreviewer.class).create(outBundle).start().resume().visible();
    saveControllerForCleanup(previewerController);
    testCardTemplatePreviewer = (TestCardTemplatePreviewer) previewerController.get();
    Assert.assertTrue("model change not preserved in lifecycle??", testCardTemplatePreviewer.getDummyCard(collectionBasicModelOriginal, 0).q().contains("PREVIEWER_TEST") && testCardTemplatePreviewer.getDummyCard(collectionBasicModelOriginal, 0).a().contains("PREVIEWER_TEST"));
    // Make sure we can click
    Assert.assertFalse("Showing the answer already?", testCardTemplatePreviewer.getShowingAnswer());
    testCardTemplatePreviewer.disableDoubleClickPrevention();
    View showAnswerButton = testCardTemplatePreviewer.findViewById(R.id.preview_buttons_layout);
    showAnswerButton.performClick();
    Assert.assertTrue("Not showing the answer?", testCardTemplatePreviewer.getShowingAnswer());
}
Also used : JSONObject(com.ichi2.utils.JSONObject) Bundle(android.os.Bundle) Model(com.ichi2.libanki.Model) Intent(android.content.Intent) View(android.view.View) Test(org.junit.Test)

Aggregations

JSONObject (com.ichi2.utils.JSONObject)124 Model (com.ichi2.libanki.Model)95 Test (org.junit.Test)82 JSONArray (com.ichi2.utils.JSONArray)79 Collection (com.ichi2.libanki.Collection)53 ArrayList (java.util.ArrayList)48 Note (com.ichi2.libanki.Note)40 RobolectricTest (com.ichi2.anki.RobolectricTest)38 JSONException (com.ichi2.utils.JSONException)32 Intent (android.content.Intent)30 Card (com.ichi2.libanki.Card)27 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)26 HashMap (java.util.HashMap)22 Bundle (android.os.Bundle)20 NonNull (androidx.annotation.NonNull)20 SuppressLint (android.annotation.SuppressLint)16 View (android.view.View)16 ConfirmationDialog (com.ichi2.anki.dialogs.ConfirmationDialog)15 IOException (java.io.IOException)15 Nullable (androidx.annotation.Nullable)14