Search in sources :

Example 6 with LEFT

use of com.ichi2.anim.ActivityTransitionAnimation.LEFT in project AnkiChinaAndroid by ankichinateam.

the class SchedTest method test_reviewsV1.

@Test
public void test_reviewsV1() throws Exception {
    Collection col = getColV1();
    // add a note
    Note note = col.newNote();
    note.setItem("Front", "one");
    note.setItem("Back", "two");
    col.addNote(note);
    // set the card up as a review card, due 8 days ago
    Card c = note.cards().get(0);
    c.setType(CARD_TYPE_REV);
    c.setQueue(QUEUE_TYPE_REV);
    c.setDue(col.getSched().getToday() - 8);
    c.setFactor(STARTING_FACTOR);
    c.setReps(3);
    c.setLapses(1);
    c.setIvl(100);
    c.startTimer();
    c.flush();
    // save it for later use as well
    Card cardcopy = c.clone();
    // failing it should put it in the learn queue with the default options
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    // different delay to new
    col.reset();
    DeckConfig conf = col.getSched()._cardConf(c);
    conf.getJSONObject("lapse").put("delays", new JSONArray(new double[] { 2, 20 }));
    col.getDecks().save(conf);
    col.getSched().answerCard(c, 1);
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    // it should be due tomorrow, with an interval of 1
    assertEquals(col.getSched().getToday() + 1, c.getODue());
    assertEquals(1, c.getIvl());
    // but because it's in the learn queue, its current due time should be in
    // the future
    assertThat(c.getDue(), is(greaterThanOrEqualTo(col.getTime().intTime())));
    assertThat(c.getDue() - col.getTime().intTime(), is(greaterThan(118L)));
    // factor should have been decremented
    assertEquals(2300, c.getFactor());
    // check counters
    assertEquals(2, c.getLapses());
    assertEquals(4, c.getReps());
    // check ests.
    assertEquals(120, col.getSched().nextIvl(c, 1));
    assertEquals(20 * 60, col.getSched().nextIvl(c, 2));
    // try again with an ease of 2 instead
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.getSched().answerCard(c, 2);
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // the new interval should be (100 + 8/4) * 1.2 = 122
    assertTrue(checkRevIvl(col, c, 122));
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    // factor should have been decremented
    assertEquals(2350, c.getFactor());
    // check counters
    assertEquals(1, c.getLapses());
    assertEquals(4, c.getReps());
    // ease 3
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.getSched().answerCard(c, 3);
    // the new interval should be (100 + 8/2) * 2.5 = 260
    assertTrue(checkRevIvl(col, c, 260));
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    // factor should have been left alone
    assertEquals(STARTING_FACTOR, c.getFactor());
    // ease 4
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.getSched().answerCard(c, 4);
    // the new interval should be (100 + 8) * 2.5 * 1.3 = 351
    assertTrue(checkRevIvl(col, c, 351));
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    // factor should have been increased
    assertEquals(2650, c.getFactor());
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 7 with LEFT

use of com.ichi2.anim.ActivityTransitionAnimation.LEFT in project AnkiChinaAndroid by ankichinateam.

the class SchedTest method test_learnV1.

@Test
public void test_learnV1() throws Exception {
    Collection col = getColV1();
    // add a note
    Note note = col.newNote();
    note.setItem("Front", "one");
    note.setItem("Back", "two");
    col.addNote(note);
    // set as a learn card and rebuild queues
    col.getDb().execute("update cards set queue=0, type=0");
    col.reset();
    // sched.getCard should return it, since it's due in the past
    Card c = col.getSched().getCard();
    assertNotNull(c);
    DeckConfig conf = col.getSched()._cardConf(c);
    conf.getJSONObject("new").put("delays", new JSONArray(new double[] { 0.5, 3, 10 }));
    col.getDecks().save(conf);
    // fail it
    col.getSched().answerCard(c, 1);
    // it should have three reps left to graduation
    assertEquals(3, c.getLeft() % 1000);
    assertEquals(3, c.getLeft() / 1000);
    // it should be due in 30 seconds
    long t = Math.round(c.getDue() - col.getTime().intTime());
    assertThat(t, is(greaterThanOrEqualTo(25L)));
    assertThat(t, is(lessThanOrEqualTo(40L)));
    // pass it once
    col.getSched().answerCard(c, 2);
    // it should be due in 3 minutes
    assertEquals(Math.round(c.getDue() - col.getTime().intTime()), 179, 1);
    assertEquals(2, c.getLeft() % 1000);
    assertEquals(2, c.getLeft() / 1000);
    // check log is accurate
    Cursor log = col.getDb().getDatabase().query("select * from revlog order by id desc");
    assertTrue(log.moveToFirst());
    assertEquals(2, log.getInt(3));
    assertEquals(-180, log.getInt(4));
    assertEquals(-30, log.getInt(5));
    // pass again
    col.getSched().answerCard(c, 2);
    // it should be due in 10 minutes
    assertEquals(c.getDue() - col.getTime().intTime(), 599, 1);
    assertEquals(1, c.getLeft() % 1000);
    assertEquals(1, c.getLeft() / 1000);
    // the next pass should graduate the card
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    assertEquals(CARD_TYPE_LRN, c.getType());
    col.getSched().answerCard(c, 2);
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertEquals(CARD_TYPE_REV, c.getType());
    // should be due tomorrow, with an interval of 1
    assertEquals(col.getSched().getToday() + 1, c.getDue());
    assertEquals(1, c.getIvl());
    // or normal removal
    c.setType(CARD_TYPE_NEW);
    c.setQueue(QUEUE_TYPE_LRN);
    col.getSched().answerCard(c, 3);
    assertEquals(CARD_TYPE_REV, c.getType());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertTrue(checkRevIvl(col, c, 4));
    // revlog should have been updated each time
    assertEquals(5, col.getDb().queryScalar("select count() from revlog where type = 0"));
    // now failed card handling
    c.setType(CARD_TYPE_REV);
    c.setQueue(QUEUE_TYPE_LRN);
    c.setODue(123);
    col.getSched().answerCard(c, 3);
    assertEquals(123, c.getDue());
    assertEquals(CARD_TYPE_REV, c.getType());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // we should be able to remove manually, too
    c.setType(CARD_TYPE_REV);
    c.setQueue(QUEUE_TYPE_LRN);
    c.setODue(321);
    c.flush();
    ((Sched) col.getSched()).removeLrn();
    c.load();
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertEquals(321, c.getDue());
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) Cursor(android.database.Cursor) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 8 with LEFT

use of com.ichi2.anim.ActivityTransitionAnimation.LEFT 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 9 with LEFT

use of com.ichi2.anim.ActivityTransitionAnimation.LEFT in project AnkiChinaAndroid by ankichinateam.

the class SchedV2Test method test_learnV2.

@Test
public void test_learnV2() throws Exception {
    Collection col = getColV2();
    // add a note
    Note note = col.newNote();
    note.setItem("Front", "one");
    note.setItem("Back", "two");
    col.addNote(note);
    // set as a learn card and rebuild queues
    col.getDb().execute("update cards set queue=0, type=0");
    col.reset();
    // sched.getCard should return it, since it's due in the past
    Card c = col.getSched().getCard();
    assertNotNull(c);
    DeckConfig conf = col.getSched()._cardConf(c);
    conf.getJSONObject("new").put("delays", new JSONArray(new double[] { 0.5, 3, 10 }));
    col.getDecks().save(conf);
    // fail it
    col.getSched().answerCard(c, 1);
    // it should have three reps left to graduation
    assertEquals(3, c.getLeft() % 1000);
    assertEquals(3, c.getLeft() / 1000);
    // it should be due in 30 seconds
    long t = Math.round(c.getDue() - col.getTime().intTime());
    assertThat(t, is(greaterThanOrEqualTo(25L)));
    assertThat(t, is(lessThanOrEqualTo(40L)));
    // pass it once
    col.getSched().answerCard(c, 3);
    // it should be due in 3 minutes
    long dueIn = c.getDue() - col.getTime().intTime();
    assertThat(dueIn, is(greaterThanOrEqualTo(178L)));
    assertThat(dueIn, is(lessThanOrEqualTo((long) (180 * 1.25))));
    assertEquals(2, c.getLeft() % 1000);
    assertEquals(2, c.getLeft() / 1000);
    // check log is accurate
    Cursor log = col.getDb().getDatabase().query("select * from revlog order by id desc");
    assertTrue(log.moveToFirst());
    assertEquals(3, log.getInt(3));
    assertEquals(-180, log.getInt(4));
    assertEquals(-30, log.getInt(5));
    // pass again
    col.getSched().answerCard(c, 3);
    // it should be due in 10 minutes
    dueIn = c.getDue() - col.getTime().intTime();
    assertThat(dueIn, is(greaterThanOrEqualTo(599L)));
    assertThat(dueIn, is(lessThanOrEqualTo((long) (600 * 1.25))));
    assertEquals(1, c.getLeft() % 1000);
    assertEquals(1, c.getLeft() / 1000);
    // the next pass should graduate the card
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    assertEquals(CARD_TYPE_LRN, c.getType());
    col.getSched().answerCard(c, 3);
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertEquals(CARD_TYPE_REV, c.getType());
    // should be due tomorrow, with an interval of 1
    assertEquals(col.getSched().getToday() + 1, c.getDue());
    assertEquals(1, c.getIvl());
    // or normal removal
    c.setType(CARD_TYPE_NEW);
    c.setQueue(QUEUE_TYPE_LRN);
    col.getSched().answerCard(c, 4);
    assertEquals(CARD_TYPE_REV, c.getType());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertTrue(checkRevIvl(col, c, 4));
    // revlog should have been updated each time
    assertEquals(5, col.getDb().queryScalar("select count() from revlog where type = 0"));
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) Cursor(android.database.Cursor) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 10 with LEFT

use of com.ichi2.anim.ActivityTransitionAnimation.LEFT in project AnkiChinaAndroid by ankichinateam.

the class SchedV2Test method filteredDeckSchedulingOptionsRegressionTest.

/**
 * Reported by /u/CarelessSecretary9 on reddit:
 */
@Test
public void filteredDeckSchedulingOptionsRegressionTest() {
    Collection col = getCol();
    col.setCrt(1587852900L);
    // 30 minutes learn ahead. required as we have 20m delay
    col.getConf().put("collapseTime", 1800);
    long homeDeckId = addDeck("Poorretention");
    DeckConfig homeDeckConf = col.getDecks().confForDid(homeDeckId);
    JSONObject lapse = homeDeckConf.getJSONObject("lapse");
    lapse.put("minInt", 2);
    lapse.put("mult", 0.7d);
    lapse.put("delays", new JSONArray("[20]"));
    ensureLapseMatchesSppliedAnkiDesktopConfig(lapse);
    col.flush();
    long dynId = addDynamicDeck("Dyn");
    /*
        >>> pp(self.reviewer.card)
        {'data': '', 'did': 1587939535230, 'due': 0, 'factor': 1300, 'flags': 0, 'id': 1510928829863, 'ivl': 25,
        'lapses': 5, 'left': 1004, 'mod': 1587921512, 'nid': 1510928805161, 'odid': 1587920944107,
        'odue': 0, 'ord': 0, 'queue': 2, 'reps': 22, 'type': 2, 'usn': -1}

         */
    Note n = addNoteUsingBasicModel("Hello", "World");
    Card c = getOnlyElement(n.cards());
    c.setType(Consts.CARD_TYPE_REV);
    c.setQueue(Consts.QUEUE_TYPE_REV);
    c.setIvl(25);
    c.setDue(0);
    c.setLapses(5);
    c.setFactor(1300);
    c.setLeft(1004);
    c.setODid(homeDeckId);
    c.setDid(dynId);
    c.flush();
    SchedV2 v2 = new SchedV2(col);
    Card schedCard = v2.getCard();
    assertThat(schedCard, Matchers.notNullValue());
    v2.answerCard(schedCard, Consts.BUTTON_ONE);
    assertThat("The lapsed card should now be counted as lrn", v2.mLrnCount, is(1));
    Card after = v2.getCard();
    assertThat("A card should be returned ", after, Matchers.notNullValue());
    /* Data from Anki - pp(self.reviewer.card)
        {'data': '', 'did': 1587939535230, 'due': 1587941137, 'factor': 1300,
        'flags': 0, 'id': 1510928829863, 'ivl': 17, 'lapses': 6, 'left': 1001,
        'mod': 1587939720, 'nid': 1510928805161, 'odid': 1587920944107, 'odue': 0,
        'ord': 0, 'queue': 1, 'reps': 23, 'type': 3, 'usn': -1}
         */
    assertThat(after.getType(), is(Consts.CARD_TYPE_RELEARNING));
    assertThat(after.getQueue(), is(Consts.QUEUE_TYPE_LRN));
    assertThat(after.getLeft(), is(1001));
    assertThat("ivl is reduced by 70%", after.getIvl(), is(17));
    assertThat("One lapse is added", after.getLapses(), is(6));
    assertThat(v2.answerButtons(after), is(4));
    long one = v2.nextIvl(after, Consts.BUTTON_ONE);
    long two = v2.nextIvl(after, Consts.BUTTON_TWO);
    long three = v2.nextIvl(after, Consts.BUTTON_THREE);
    long four = v2.nextIvl(after, Consts.BUTTON_FOUR);
    // 20 mins
    assertThat("Again should pick the current step", one, is(1200L));
    // 30 mins
    assertThat("Repeating single step - 20 minutes * 1.5", two, is(1800L));
    // 17 days
    assertThat("Good should take the reduced interval (25 * 0.7)", three, is(1468800L));
    // 18 days
    assertThat("Easy should have a bonus day over good", four, is(1555200L));
}
Also used : JSONObject(com.ichi2.utils.JSONObject) Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Aggregations

JSONArray (com.ichi2.utils.JSONArray)16 Card (com.ichi2.libanki.Card)14 Collection (com.ichi2.libanki.Collection)12 JSONObject (com.ichi2.utils.JSONObject)12 DeckConfig (com.ichi2.libanki.DeckConfig)11 Note (com.ichi2.libanki.Note)11 Test (org.junit.Test)11 RobolectricTest (com.ichi2.anki.RobolectricTest)10 Cursor (android.database.Cursor)8 ArrayList (java.util.ArrayList)8 Nullable (androidx.annotation.Nullable)7 JSONException (com.ichi2.utils.JSONException)7 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)5 Model (com.ichi2.libanki.Model)4 IOException (java.io.IOException)4 SuppressLint (android.annotation.SuppressLint)3 Intent (android.content.Intent)3 SharedPreferences (android.content.SharedPreferences)3 Menu (android.view.Menu)3 View (android.view.View)3