Search in sources :

Example 61 with Time

use of com.ichi2.libanki.utils.Time in project AnkiChinaAndroid by ankichinateam.

the class SchedTest method test_cram.

@Test
public void test_cram() throws Exception {
    Collection col = getColV1();
    Note note = col.newNote();
    note.setItem("Front", "one");
    col.addNote(note);
    Card c = note.cards().get(0);
    c.setIvl(100);
    c.setQueue(CARD_TYPE_REV);
    c.setType(QUEUE_TYPE_REV);
    // due in 25 days, so it's been waiting 75 days
    c.setDue(col.getSched().getToday() + 25);
    c.setMod(1);
    c.setFactor(STARTING_FACTOR);
    c.startTimer();
    c.flush();
    col.reset();
    assertArrayEquals(new int[] { 0, 0, 0 }, col.getSched().counts());
    Card cardcopy = c.clone();
    // create a dynamic deck and refresh it
    long did = col.getDecks().newDyn("Cram");
    col.getSched().rebuildDyn(did);
    col.reset();
    // should appear as new in the deck list
    // todo: which sort
    // and should appear in the counts
    assertArrayEquals(new int[] { 1, 0, 0 }, col.getSched().counts());
    // grab it and check estimates
    c = col.getSched().getCard();
    assertEquals(2, col.getSched().answerButtons(c));
    assertEquals(600, col.getSched().nextIvl(c, 1));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 2));
    Deck cram = col.getDecks().get(did);
    cram.put("delays", new JSONArray(new double[] { 1, 10 }));
    col.getDecks().save(cram);
    assertEquals(3, col.getSched().answerButtons(c));
    assertEquals(60, col.getSched().nextIvl(c, 1));
    assertEquals(600, col.getSched().nextIvl(c, 2));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 3));
    col.getSched().answerCard(c, 2);
    // elapsed time was 75 days
    // factor = 2.5+1.2/2 = 1.85
    // int(75*1.85) = 138
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getODue());
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    // should be logged as a cram rep
    assertEquals(3, col.getDb().queryLongScalar("select type from revlog order by id desc limit 1"));
    // check ivls again
    assertEquals(60, col.getSched().nextIvl(c, 1));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 2));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 3));
    // when it graduates, due is updated
    c = col.getSched().getCard();
    col.getSched().answerCard(c, 2);
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getDue());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // and it will have moved back to the previous deck
    assertEquals(1, c.getDid());
    // cram the deck again
    col.getSched().rebuildDyn(did);
    col.reset();
    c = col.getSched().getCard();
    // check ivls again - passing should be idempotent
    assertEquals(60, col.getSched().nextIvl(c, 1));
    assertEquals(600, col.getSched().nextIvl(c, 2));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 3));
    col.getSched().answerCard(c, 2);
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getODue());
    // fail
    col.getSched().answerCard(c, 1);
    assertEquals(60, col.getSched().nextIvl(c, 1));
    assertEquals(600, col.getSched().nextIvl(c, 2));
    assertEquals(SECONDS_PER_DAY, col.getSched().nextIvl(c, 3));
    // delete the deck, returning the card mid-study
    col.getDecks().rem(col.getDecks().selected());
    assertEquals(1, col.getSched().deckDueTree().size());
    c.load();
    assertEquals(1, c.getIvl());
    assertEquals(col.getSched().getToday() + 1, c.getDue());
    // make it due
    col.reset();
    assertArrayEquals(new int[] { 0, 0, 0 }, col.getSched().counts());
    c.setDue(-5);
    c.setIvl(100);
    c.flush();
    col.reset();
    assertArrayEquals(new int[] { 0, 0, 1 }, col.getSched().counts());
    // cram again
    did = col.getDecks().newDyn("Cram");
    col.getSched().rebuildDyn(did);
    col.reset();
    assertArrayEquals(new int[] { 0, 0, 1 }, col.getSched().counts());
    c.load();
    assertEquals(4, col.getSched().answerButtons(c));
    // add a sibling so we can test minSpace, etc
    Card c2 = c.clone();
    c2.setId(0);
    c2.setOrd(1);
    c2.setDue(325);
    c2.flush();
    // should be able to answer it
    c = col.getSched().getCard();
    col.getSched().answerCard(c, 4);
    // it should have been moved back to the original deck
    assertEquals(1, c.getDid());
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) Deck(com.ichi2.libanki.Deck) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 62 with Time

use of com.ichi2.libanki.utils.Time in project Anki-Android by ankidroid.

the class ACRATest method testCrashReportLimit.

@Test
public void testCrashReportLimit() throws Exception {
    // To test ACRA switch on  reporting, plant a production tree, and trigger a report
    Timber.plant(new AnkiDroidApp.ProductionCrashReportingTree());
    // set up as if the user had prefs saved to full auto
    setReportConfig(FEEDBACK_REPORT_ALWAYS);
    // If the user is set to always, then it's production, with interaction mode toast
    // will be useful with ACRA 5.2.0
    setAcraConfig("Production");
    // The same class/method combo is only sent once, so we face a new method each time (should test that system later)
    Exception crash = new Exception("testCrashReportSend at " + System.currentTimeMillis());
    StackTraceElement[] trace = new StackTraceElement[] { new StackTraceElement("Class", "Method" + (int) System.currentTimeMillis(), "File", (int) System.currentTimeMillis()) };
    crash.setStackTrace(trace);
    // one send should work
    CrashReportData crashData = new CrashReportDataFactory(getTestContext(), AnkiDroidApp.getInstance().getAcraCoreConfigBuilder().build()).createCrashData(new ReportBuilder().exception(crash));
    assertTrue(new LimitingReportAdministrator().shouldSendReport(getTestContext(), AnkiDroidApp.getInstance().getAcraCoreConfigBuilder().build(), crashData));
    // A second send should not work
    assertFalse(new LimitingReportAdministrator().shouldSendReport(getTestContext(), AnkiDroidApp.getInstance().getAcraCoreConfigBuilder().build(), crashData));
    // Now let's clear data
    AnkiDroidApp.deleteACRALimiterData(getTestContext());
    // A third send should work again
    assertTrue(new LimitingReportAdministrator().shouldSendReport(getTestContext(), AnkiDroidApp.getInstance().getAcraCoreConfigBuilder().build(), crashData));
}
Also used : CrashReportData(org.acra.data.CrashReportData) ReportBuilder(org.acra.builder.ReportBuilder) LimitingReportAdministrator(org.acra.config.LimitingReportAdministrator) AnkiDroidApp(com.ichi2.anki.AnkiDroidApp) CrashReportDataFactory(org.acra.data.CrashReportDataFactory) ACRAConfigurationException(org.acra.config.ACRAConfigurationException) Test(org.junit.Test) UiThreadTest(androidx.test.annotation.UiThreadTest)

Example 63 with Time

use of com.ichi2.libanki.utils.Time in project Anki-Android by ankidroid.

the class Syncer method sync.

public Pair<ConnectionResultType, Object> sync(Connection con) throws UnknownHttpResponseException {
    mSyncMsg = "";
    // if the deck has any pending changes, flush them first and bump mod time
    mCol.getSched()._updateCutoff();
    mCol.save();
    // step 1: login & metadata
    Response ret = mRemoteServer.meta();
    if (ret == null) {
        return null;
    }
    int returntype = ret.code();
    if (returntype == 403) {
        return new Pair<>(BAD_AUTH, null);
    }
    try {
        mCol.getDb().getDatabase().beginTransaction();
        try {
            Timber.i("Sync: getting meta data from server");
            JSONObject rMeta = new JSONObject(ret.body().string());
            mCol.log("rmeta", rMeta);
            mSyncMsg = rMeta.getString("msg");
            if (!rMeta.getBoolean("cont")) {
                // Don't add syncMsg; it can be fetched by UI code using the accessor
                return new Pair<>(SERVER_ABORT, null);
            } else {
            // don't abort, but ui should show messages after sync finishes
            // and require confirmation if it's non-empty
            }
            throwExceptionIfCancelled(con);
            long rscm = rMeta.getLong("scm");
            int rts = rMeta.getInt("ts");
            long rMod = rMeta.getLong("mod");
            mMaxUsn = rMeta.getInt("usn");
            // skip uname, AnkiDroid already stores and shows it
            trySetHostNum(rMeta);
            Timber.i("Sync: building local meta data");
            JSONObject lMeta = meta();
            mCol.log("lmeta", lMeta);
            long lMod = lMeta.getLong("mod");
            mMinUsn = lMeta.getInt("usn");
            long lscm = lMeta.getLong("scm");
            int lts = lMeta.getInt("ts");
            long diff = Math.abs(rts - lts);
            if (diff > 300) {
                mCol.log("clock off");
                return new Pair<>(CLOCK_OFF, diff);
            }
            if (lMod == rMod) {
                Timber.i("Sync: no changes - returning");
                mCol.log("no changes");
                return new Pair<>(NO_CHANGES, null);
            } else if (lscm != rscm) {
                Timber.i("Sync: full sync necessary - returning");
                mCol.log("schema diff");
                return new Pair<>(FULL_SYNC, null);
            }
            mLNewer = lMod > rMod;
            // step 1.5: check collection is valid
            if (!mCol.basicCheck()) {
                mCol.log("basic check");
                return new Pair<>(BASIC_CHECK_FAILED, null);
            }
            throwExceptionIfCancelled(con);
            // step 2: deletions
            publishProgress(con, R.string.sync_deletions_message);
            Timber.i("Sync: collection removed data");
            JSONObject lrem = removed();
            JSONObject o = new JSONObject();
            o.put("minUsn", mMinUsn);
            o.put("lnewer", mLNewer);
            o.put("graves", lrem);
            Timber.i("Sync: sending and receiving removed data");
            JSONObject rrem = mRemoteServer.start(o);
            Timber.i("Sync: applying removed data");
            throwExceptionIfCancelled(con);
            remove(rrem);
            // ... and small objects
            publishProgress(con, R.string.sync_small_objects_message);
            Timber.i("Sync: collection small changes");
            JSONObject lchg = changes();
            JSONObject sch = new JSONObject();
            sch.put("changes", lchg);
            Timber.i("Sync: sending and receiving small changes");
            JSONObject rchg = mRemoteServer.applyChanges(sch);
            throwExceptionIfCancelled(con);
            Timber.i("Sync: merging small changes");
            try {
                mergeChanges(lchg, rchg);
            } catch (UnexpectedSchemaChange e) {
                Timber.w(e);
                mRemoteServer.abort();
                _forceFullSync();
            }
            // step 3: stream large tables from server
            publishProgress(con, R.string.sync_download_chunk);
            while (true) {
                throwExceptionIfCancelled(con);
                Timber.i("Sync: downloading chunked data");
                JSONObject chunk = mRemoteServer.chunk();
                mCol.log("server chunk", chunk);
                Timber.i("Sync: applying chunked data");
                applyChunk(chunk);
                if (chunk.getBoolean("done")) {
                    break;
                }
            }
            // step 4: stream to server
            publishProgress(con, R.string.sync_upload_chunk);
            while (true) {
                throwExceptionIfCancelled(con);
                Timber.i("Sync: collecting chunked data");
                JSONObject chunk = chunk();
                mCol.log("client chunk", chunk);
                JSONObject sech = new JSONObject();
                sech.put("chunk", chunk);
                Timber.i("Sync: sending chunked data");
                mRemoteServer.applyChunk(sech);
                if (chunk.getBoolean("done")) {
                    break;
                }
            }
            // step 5: sanity check
            JSONObject c = sanityCheck();
            JSONObject sanity = mRemoteServer.sanityCheck2(c);
            if (sanity == null || !"ok".equals(sanity.optString("status", "bad"))) {
                return sanityCheckError(c, sanity);
            }
            // finalize
            publishProgress(con, R.string.sync_finish_message);
            Timber.i("Sync: sending finish command");
            long mod = mRemoteServer.finish();
            if (mod == 0) {
                return new Pair<>(FINISH_ERROR, null);
            }
            Timber.i("Sync: finishing");
            finish(mod);
            publishProgress(con, R.string.sync_writing_db);
            mCol.getDb().getDatabase().setTransactionSuccessful();
        } finally {
            DB.safeEndInTransaction(mCol.getDb());
        }
    } catch (IllegalStateException e) {
        throw new RuntimeException(e);
    } catch (OutOfMemoryError e) {
        AnkiDroidApp.sendExceptionReport(e, "Syncer-sync");
        Timber.w(e);
        return new Pair<>(OUT_OF_MEMORY_ERROR, null);
    } catch (IOException e) {
        AnkiDroidApp.sendExceptionReport(e, "Syncer-sync");
        Timber.w(e);
        return new Pair<>(IO_EXCEPTION, null);
    }
    return new Pair<>(SUCCESS, null);
}
Also used : Response(okhttp3.Response) JSONObject(com.ichi2.utils.JSONObject) IOException(java.io.IOException) Pair(android.util.Pair)

Example 64 with Time

use of com.ichi2.libanki.utils.Time in project Anki-Android by ankidroid.

the class SchedV2Test method test_preview.

@Test
public void test_preview() throws Exception {
    // add cards
    Collection col = getColV2();
    Note note = col.newNote();
    note.setItem("Front", "one");
    col.addNote(note);
    Card c = note.cards().get(0);
    Card orig = c.clone();
    Note note2 = col.newNote();
    note2.setItem("Front", "two");
    col.addNote(note2);
    // cram deck
    long did = addDynamicDeck("Cram");
    Deck cram = col.getDecks().get(did);
    cram.put("resched", false);
    col.getDecks().save(cram);
    col.getSched().rebuildDyn(did);
    col.reset();
    // grab the first card
    c = getCard();
    assertEquals(2, col.getSched().answerButtons(c));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(0, col.getSched().nextIvl(c, BUTTON_TWO));
    // failing it will push its due time back
    long due = c.getDue();
    col.getSched().answerCard(c, BUTTON_ONE);
    assertNotEquals(c.getDue(), due);
    // the other card should come next
    Card c2 = getCard();
    assertNotEquals(c2.getId(), c.getId());
    // passing it will remove it
    col.getSched().answerCard(c2, BUTTON_TWO);
    assertEquals(QUEUE_TYPE_NEW, c2.getQueue());
    assertEquals(0, c2.getReps());
    assertEquals(CARD_TYPE_NEW, c2.getType());
    // the other card should appear again
    c = getCard();
    assertEquals(orig.getId(), c.getId());
    // emptying the filtered deck should restore card
    col.getSched().emptyDyn(did);
    c.load();
    assertEquals(QUEUE_TYPE_NEW, c.getQueue());
    assertEquals(0, c.getReps());
    assertEquals(CARD_TYPE_NEW, c.getType());
}
Also used : Note(com.ichi2.libanki.Note) Collection(com.ichi2.libanki.Collection) Deck(com.ichi2.libanki.Deck) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 65 with Time

use of com.ichi2.libanki.utils.Time in project Anki-Android by ankidroid.

the class SchedTest method test_cram.

@Test
public void test_cram() throws Exception {
    Collection col = getColV1();
    Note note = col.newNote();
    note.setItem("Front", "one");
    col.addNote(note);
    Card c = note.cards().get(0);
    c.setIvl(100);
    c.setQueue(QUEUE_TYPE_REV);
    c.setType(CARD_TYPE_REV);
    // due in 25 days, so it's been waiting 75 days
    c.setDue(col.getSched().getToday() + 25);
    c.setMod(1);
    c.setFactor(STARTING_FACTOR);
    c.startTimer();
    c.flush();
    col.reset();
    assertEquals(new Counts(0, 0, 0), col.getSched().counts());
    Card cardcopy = c.clone();
    // create a dynamic deck and refresh it
    long did = addDynamicDeck("Cram");
    col.getSched().rebuildDyn(did);
    col.reset();
    // should appear as new in the deck list
    // todo: which sort
    // and should appear in the counts
    assertEquals(new Counts(1, 0, 0), col.getSched().counts());
    // grab it and check estimates
    c = getCard();
    assertEquals(2, col.getSched().answerButtons(c));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_TWO));
    Deck cram = col.getDecks().get(did);
    cram.put("delays", new JSONArray(new double[] { 1, 10 }));
    col.getDecks().save(cram);
    assertEquals(3, col.getSched().answerButtons(c));
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_THREE));
    col.getSched().answerCard(c, BUTTON_TWO);
    // elapsed time was 75 days
    // factor = 2.5+1.2/2 = 1.85
    // int(75*1.85) = 138
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getODue());
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    // should be logged as a cram rep
    assertEquals(3, col.getDb().queryLongScalar("select type from revlog order by id desc limit 1"));
    // check ivls again
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_THREE));
    // when it graduates, due is updated
    c = getCard();
    col.getSched().answerCard(c, BUTTON_TWO);
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getDue());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // and it will have moved back to the previous deck
    assertEquals(1, c.getDid());
    // cram the deck again
    col.getSched().rebuildDyn(did);
    col.reset();
    c = getCard();
    // check ivls again - passing should be idempotent
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, BUTTON_THREE));
    col.getSched().answerCard(c, BUTTON_TWO);
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getODue());
    // fail
    col.getSched().answerCard(c, BUTTON_ONE);
    assertEquals(60, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_TWO));
    assertEquals(SECONDS_PER_DAY, col.getSched().nextIvl(c, BUTTON_THREE));
    // delete the deck, returning the card mid-study
    col.getDecks().rem(col.getDecks().selected());
    assertEquals(1, col.getSched().deckDueTree().size());
    c.load();
    assertEquals(1, c.getIvl());
    assertEquals(col.getSched().getToday() + 1, c.getDue());
    // make it due
    col.reset();
    assertEquals(new Counts(0, 0, 0), col.getSched().counts());
    c.setDue(-5);
    c.setIvl(100);
    c.flush();
    col.reset();
    assertEquals(new Counts(0, 0, 1), col.getSched().counts());
    // cram again
    did = addDynamicDeck("Cram");
    col.getSched().rebuildDyn(did);
    col.reset();
    assertEquals(new Counts(0, 0, 1), col.getSched().counts());
    c.load();
    assertEquals(4, col.getSched().answerButtons(c));
    // add a sibling so we can test minSpace, etc
    Card c2 = c.clone();
    c2.setId(0);
    c2.setOrd(1);
    c2.setDue(325);
    c2.flush();
    // should be able to answer it
    c = getCard();
    col.getSched().answerCard(c, BUTTON_FOUR);
    // it should have been moved back to the original deck
    assertEquals(1, c.getDid());
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) Deck(com.ichi2.libanki.Deck) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Aggregations

Test (org.junit.Test)27 Collection (com.ichi2.libanki.Collection)26 JSONObject (com.ichi2.utils.JSONObject)26 RobolectricTest (com.ichi2.anki.RobolectricTest)19 Card (com.ichi2.libanki.Card)19 Note (com.ichi2.libanki.Note)15 JSONArray (com.ichi2.utils.JSONArray)15 Deck (com.ichi2.libanki.Deck)11 ArrayList (java.util.ArrayList)11 DeckConfig (com.ichi2.libanki.DeckConfig)10 IOException (java.io.IOException)10 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)9 HashMap (java.util.HashMap)9 Cursor (android.database.Cursor)8 Nullable (androidx.annotation.Nullable)8 JSONException (com.ichi2.utils.JSONException)8 SharedPreferences (android.content.SharedPreferences)7 Resources (android.content.res.Resources)7 File (java.io.File)7 View (android.view.View)5