diff --git a/Makefile b/Makefile
index 9cae8398a..791b9df50 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
RUNARGS :=
.SUFFIXES:
-BLACKARGS := -t py36 anki aqt
+BLACKARGS := -t py36 anki aqt tests
RUSTARGS := --release --strip
$(shell mkdir -p .build)
diff --git a/tests/shared.py b/tests/shared.py
index 70acfe4c0..1ddf13601 100644
--- a/tests/shared.py
+++ b/tests/shared.py
@@ -1,6 +1,7 @@
import tempfile, os, shutil
from anki import Collection as aopen
+
def assertException(exception, func):
found = False
try:
@@ -25,6 +26,7 @@ def getEmptyCol():
col = aopen(nam)
return col
+
getEmptyCol.master = ""
# Fallback for when the DB needs options passed in.
@@ -34,10 +36,12 @@ def getEmptyDeckWith(**kwargs):
os.unlink(nam)
return aopen(nam, **kwargs)
+
def getUpgradeDeckPath(name="anki12.anki"):
src = os.path.join(testDir, "support", name)
(fd, dst) = tempfile.mkstemp(suffix=".anki2")
shutil.copy(src, dst)
return dst
+
testDir = os.path.dirname(__file__)
diff --git a/tests/test_addons.py b/tests/test_addons.py
index 954ea8594..69bc54245 100644
--- a/tests/test_addons.py
+++ b/tests/test_addons.py
@@ -9,65 +9,48 @@ from aqt.addons import AddonManager
def test_readMinimalManifest():
assertReadManifest(
- '{"package": "yes", "name": "no"}',
- {"package": "yes", "name": "no"}
+ '{"package": "yes", "name": "no"}', {"package": "yes", "name": "no"}
)
def test_readExtraKeys():
assertReadManifest(
'{"package": "a", "name": "b", "mod": 3, "conflicts": ["d", "e"]}',
- {"package": "a", "name": "b", "mod": 3, "conflicts": ["d", "e"]}
+ {"package": "a", "name": "b", "mod": 3, "conflicts": ["d", "e"]},
)
def test_invalidManifest():
- assertReadManifest(
- '{"one": 1}',
- {}
- )
+ assertReadManifest('{"one": 1}', {})
def test_mustHaveName():
- assertReadManifest(
- '{"package": "something"}',
- {}
- )
+ assertReadManifest('{"package": "something"}', {})
def test_mustHavePackage():
- assertReadManifest(
- '{"name": "something"}',
- {}
- )
+ assertReadManifest('{"name": "something"}', {})
def test_invalidJson():
- assertReadManifest(
- 'this is not a JSON dictionary',
- {}
- )
+ assertReadManifest("this is not a JSON dictionary", {})
def test_missingManifest():
assertReadManifest(
- '{"package": "what", "name": "ever"}',
- {},
- nameInZip="not-manifest.bin"
+ '{"package": "what", "name": "ever"}', {}, nameInZip="not-manifest.bin"
)
def test_ignoreExtraKeys():
assertReadManifest(
- '{"package": "a", "name": "b", "game": "c"}',
- {"package": "a", "name": "b"}
+ '{"package": "a", "name": "b", "game": "c"}', {"package": "a", "name": "b"}
)
def test_conflictsMustBeStrings():
assertReadManifest(
- '{"package": "a", "name": "b", "conflicts": ["c", 4, {"d": "e"}]}',
- {}
+ '{"package": "a", "name": "b", "conflicts": ["c", 4, {"d": "e"}]}', {}
)
diff --git a/tests/test_cards.py b/tests/test_cards.py
index b0732b80b..6e519c84f 100644
--- a/tests/test_cards.py
+++ b/tests/test_cards.py
@@ -2,11 +2,12 @@
from tests.shared import getEmptyCol
+
def test_previewCards():
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = '1'
- f['Back'] = '2'
+ f["Front"] = "1"
+ f["Back"] = "2"
# non-empty and active
cards = deck.previewCards(f, 0)
assert len(cards) == 1
@@ -22,11 +23,12 @@ def test_previewCards():
# make sure we haven't accidentally added cards to the db
assert deck.cardCount() == 1
+
def test_delete():
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = '1'
- f['Back'] = '2'
+ f["Front"] = "1"
+ f["Back"] = "2"
deck.addNote(f)
cid = f.cards()[0].id
deck.reset()
@@ -38,62 +40,65 @@ def test_delete():
assert deck.db.scalar("select count() from cards") == 0
assert deck.db.scalar("select count() from graves") == 2
+
def test_misc():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = '1'
- f['Back'] = '2'
+ f["Front"] = "1"
+ f["Back"] = "2"
d.addNote(f)
c = f.cards()[0]
- id = d.models.current()['id']
- assert c.template()['ord'] == 0
+ id = d.models.current()["id"]
+ assert c.template()["ord"] == 0
+
def test_genrem():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = '1'
- f['Back'] = ''
+ f["Front"] = "1"
+ f["Back"] = ""
d.addNote(f)
assert len(f.cards()) == 1
m = d.models.current()
mm = d.models
# adding a new template should automatically create cards
t = mm.newTemplate("rev")
- t['qfmt'] = '{{Front}}'
- t['afmt'] = ""
+ t["qfmt"] = "{{Front}}"
+ t["afmt"] = ""
mm.addTemplate(m, t)
mm.save(m, templates=True)
assert len(f.cards()) == 2
# if the template is changed to remove cards, they'll be removed
- t['qfmt'] = "{{Back}}"
+ t["qfmt"] = "{{Back}}"
mm.save(m, templates=True)
d.remCards(d.emptyCids())
assert len(f.cards()) == 1
# if we add to the note, a card should be automatically generated
f.load()
- f['Back'] = "1"
+ f["Back"] = "1"
f.flush()
assert len(f.cards()) == 2
+
def test_gendeck():
d = getEmptyCol()
cloze = d.models.byName("Cloze")
d.models.setCurrent(cloze)
f = d.newNote()
- f['Text'] = '{{c1::one}}'
+ f["Text"] = "{{c1::one}}"
d.addNote(f)
assert d.cardCount() == 1
assert f.cards()[0].did == 1
# set the model to a new default deck
newId = d.decks.id("new")
- cloze['did'] = newId
+ cloze["did"] = newId
d.models.save(cloze, updateReqs=False)
# a newly generated card should share the first card's deck
- f['Text'] += '{{c2::two}}'
+ f["Text"] += "{{c2::two}}"
f.flush()
assert f.cards()[1].did == 1
# and same with multiple cards
- f['Text'] += '{{c3::three}}'
+ f["Text"] += "{{c3::three}}"
f.flush()
assert f.cards()[2].did == 1
# if one of the cards is in a different deck, it should revert to the
@@ -101,9 +106,6 @@ def test_gendeck():
c = f.cards()[1]
c.did = newId
c.flush()
- f['Text'] += '{{c4::four}}'
+ f["Text"] += "{{c4::four}}"
f.flush()
assert f.cards()[3].did == newId
-
-
-
diff --git a/tests/test_collection.py b/tests/test_collection.py
index 37304cd47..1868cf8b9 100644
--- a/tests/test_collection.py
+++ b/tests/test_collection.py
@@ -8,6 +8,7 @@ from anki.stdmodels import addBasicModel, models
from anki import Collection as aopen
+
def test_create_open():
(fd, path) = tempfile.mkstemp(suffix=".anki2", prefix="test_attachNew")
try:
@@ -32,27 +33,28 @@ def test_create_open():
dir = "c:\root.anki2"
else:
dir = "/attachroot.anki2"
- assertException(Exception,
- lambda: aopen(dir))
+ assertException(Exception, lambda: aopen(dir))
# reuse tmp file from before, test non-writeable file
os.chmod(newPath, 0)
- assertException(Exception,
- lambda: aopen(newPath))
+ assertException(Exception, lambda: aopen(newPath))
os.chmod(newPath, 0o666)
os.unlink(newPath)
+
def test_noteAddDelete():
deck = getEmptyCol()
# add a note
f = deck.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
n = deck.addNote(f)
assert n == 1
# test multiple cards - add another template
- m = deck.models.current(); mm = deck.models
+ m = deck.models.current()
+ mm = deck.models
t = mm.newTemplate("Reverse")
- t['qfmt'] = "{{Back}}"
- t['afmt'] = "{{Front}}"
+ t["qfmt"] = "{{Back}}"
+ t["afmt"] = "{{Front}}"
mm.addTemplate(m, t)
mm.save(m)
# the default save doesn't generate cards
@@ -63,7 +65,8 @@ def test_noteAddDelete():
assert deck.cardCount() == 2
# creating new notes should use both cards
f = deck.newNote()
- f['Front'] = "three"; f['Back'] = "four"
+ f["Front"] = "three"
+ f["Back"] = "four"
n = deck.addNote(f)
assert n == 2
assert deck.cardCount() == 4
@@ -74,36 +77,39 @@ def test_noteAddDelete():
assert not f.dupeOrEmpty()
# now let's make a duplicate
f2 = deck.newNote()
- f2['Front'] = "one"; f2['Back'] = ""
+ f2["Front"] = "one"
+ f2["Back"] = ""
assert f2.dupeOrEmpty()
# empty first field should not be permitted either
- f2['Front'] = " "
+ f2["Front"] = " "
assert f2.dupeOrEmpty()
+
def test_fieldChecksum():
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = "new"; f['Back'] = "new2"
+ f["Front"] = "new"
+ f["Back"] = "new2"
deck.addNote(f)
- assert deck.db.scalar(
- "select csum from notes") == int("c2a6b03f", 16)
+ assert deck.db.scalar("select csum from notes") == int("c2a6b03f", 16)
# changing the val should change the checksum
- f['Front'] = "newx"
+ f["Front"] = "newx"
f.flush()
- assert deck.db.scalar(
- "select csum from notes") == int("302811ae", 16)
+ assert deck.db.scalar("select csum from notes") == int("302811ae", 16)
+
def test_addDelTags():
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = "1"
+ f["Front"] = "1"
deck.addNote(f)
f2 = deck.newNote()
- f2['Front'] = "2"
+ f2["Front"] = "2"
deck.addNote(f2)
# adding for a given id
deck.tags.bulkAdd([f.id], "foo")
- f.load(); f2.load()
+ f.load()
+ f2.load()
assert "foo" in f.tags
assert "foo" not in f2.tags
# should be canonified
@@ -112,6 +118,7 @@ def test_addDelTags():
assert f.tags[0] == "aaa"
assert len(f.tags) == 2
+
def test_timestamps():
deck = getEmptyCol()
assert len(deck.models.models) == len(models)
@@ -119,23 +126,24 @@ def test_timestamps():
addBasicModel(deck)
assert len(deck.models.models) == 100 + len(models)
+
def test_furigana():
deck = getEmptyCol()
mm = deck.models
m = mm.current()
# filter should work
- m['tmpls'][0]['qfmt'] = '{{kana:Front}}'
+ m["tmpls"][0]["qfmt"] = "{{kana:Front}}"
mm.save(m)
n = deck.newNote()
- n['Front'] = 'foo[abc]'
+ n["Front"] = "foo[abc]"
deck.addNote(n)
c = n.cards()[0]
assert c.q().endswith("abc")
# and should avoid sound
- n['Front'] = 'foo[sound:abc.mp3]'
+ n["Front"] = "foo[sound:abc.mp3]"
n.flush()
assert "sound:" in c.q(reload=True)
# it shouldn't throw an error while people are editing
- m['tmpls'][0]['qfmt'] = '{{kana:}}'
+ m["tmpls"][0]["qfmt"] = "{{kana:}}"
mm.save(m)
c.q(reload=True)
diff --git a/tests/test_decks.py b/tests/test_decks.py
index 86aa8f50c..b53510d27 100644
--- a/tests/test_decks.py
+++ b/tests/test_decks.py
@@ -3,6 +3,7 @@
from anki.errors import DeckRenameError
from tests.shared import assertException, getEmptyCol
+
def test_basic():
deck = getEmptyCol()
# we start with a standard deck
@@ -34,21 +35,22 @@ def test_basic():
# parents with a different case should be handled correctly
deck.decks.id("ONE")
m = deck.models.current()
- m['did'] = deck.decks.id("one::two")
+ m["did"] = deck.decks.id("one::two")
deck.models.save(m, updateReqs=False)
n = deck.newNote()
- n['Front'] = "abc"
+ n["Front"] = "abc"
deck.addNote(n)
# this will error if child and parent case don't match
deck.sched.deckDueList()
+
def test_remove():
deck = getEmptyCol()
# create a new deck, and add a note/card to it
g1 = deck.decks.id("g1")
f = deck.newNote()
- f['Front'] = "1"
- f.model()['did'] = g1
+ f["Front"] = "1"
+ f.model()["did"] = g1
deck.addNote(f)
c = f.cards()[0]
assert c.did == g1
@@ -62,12 +64,14 @@ def test_remove():
assert deck.decks.name(c.did) == "[no deck]"
# let's create another deck and explicitly set the card to it
g2 = deck.decks.id("g2")
- c.did = g2; c.flush()
+ c.did = g2
+ c.flush()
# this time we'll delete the card/note too
deck.decks.rem(g2, cardsToo=True)
assert deck.cardCount() == 0
assert deck.noteCount() == 0
+
def test_rename():
d = getEmptyCol()
id = d.decks.id("hello::world")
@@ -80,8 +84,7 @@ def test_rename():
# create another deck
id = d.decks.id("tmp")
# we can't rename it if it conflicts
- assertException(
- Exception, lambda: d.decks.rename(d.decks.get(id), "foo"))
+ assertException(Exception, lambda: d.decks.rename(d.decks.get(id), "foo"))
# when renaming, the children should be renamed too
d.decks.id("one::two::three")
id = d.decks.id("one")
@@ -102,62 +105,66 @@ def test_rename():
assertException(DeckRenameError, lambda: d.decks.rename(child, "PARENT::child"))
-
def test_renameForDragAndDrop():
d = getEmptyCol()
def deckNames():
- return [ name for name in sorted(d.decks.allNames()) if name != 'Default' ]
+ return [name for name in sorted(d.decks.allNames()) if name != "Default"]
- languages_did = d.decks.id('Languages')
- chinese_did = d.decks.id('Chinese')
- hsk_did = d.decks.id('Chinese::HSK')
+ languages_did = d.decks.id("Languages")
+ chinese_did = d.decks.id("Chinese")
+ hsk_did = d.decks.id("Chinese::HSK")
# Renaming also renames children
d.decks.renameForDragAndDrop(chinese_did, languages_did)
- assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
+ assert deckNames() == ["Languages", "Languages::Chinese", "Languages::Chinese::HSK"]
# Dragging a deck onto itself is a no-op
d.decks.renameForDragAndDrop(languages_did, languages_did)
- assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
+ assert deckNames() == ["Languages", "Languages::Chinese", "Languages::Chinese::HSK"]
# Dragging a deck onto its parent is a no-op
d.decks.renameForDragAndDrop(hsk_did, chinese_did)
- assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
+ assert deckNames() == ["Languages", "Languages::Chinese", "Languages::Chinese::HSK"]
# Dragging a deck onto a descendant is a no-op
d.decks.renameForDragAndDrop(languages_did, hsk_did)
- assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
+ assert deckNames() == ["Languages", "Languages::Chinese", "Languages::Chinese::HSK"]
# Can drag a grandchild onto its grandparent. It becomes a child
d.decks.renameForDragAndDrop(hsk_did, languages_did)
- assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::HSK' ]
+ assert deckNames() == ["Languages", "Languages::Chinese", "Languages::HSK"]
# Can drag a deck onto its sibling
d.decks.renameForDragAndDrop(hsk_did, chinese_did)
- assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
+ assert deckNames() == ["Languages", "Languages::Chinese", "Languages::Chinese::HSK"]
# Can drag a deck back to the top level
d.decks.renameForDragAndDrop(chinese_did, None)
- assert deckNames() == [ 'Chinese', 'Chinese::HSK', 'Languages' ]
+ assert deckNames() == ["Chinese", "Chinese::HSK", "Languages"]
# Dragging a top level deck to the top level is a no-op
d.decks.renameForDragAndDrop(chinese_did, None)
- assert deckNames() == [ 'Chinese', 'Chinese::HSK', 'Languages' ]
+ assert deckNames() == ["Chinese", "Chinese::HSK", "Languages"]
# can't drack a deck where sibling have same name
new_hsk_did = d.decks.id("HSK")
- assertException(DeckRenameError, lambda: d.decks.renameForDragAndDrop(new_hsk_did, chinese_did))
+ assertException(
+ DeckRenameError, lambda: d.decks.renameForDragAndDrop(new_hsk_did, chinese_did)
+ )
d.decks.rem(new_hsk_did)
# can't drack a deck where sibling have same name different case
new_hsk_did = d.decks.id("hsk")
- assertException(DeckRenameError, lambda: d.decks.renameForDragAndDrop(new_hsk_did, chinese_did))
+ assertException(
+ DeckRenameError, lambda: d.decks.renameForDragAndDrop(new_hsk_did, chinese_did)
+ )
d.decks.rem(new_hsk_did)
# '' is a convenient alias for the top level DID
- d.decks.renameForDragAndDrop(hsk_did, '')
- assert deckNames() == [ 'Chinese', 'HSK', 'Languages' ]
+ d.decks.renameForDragAndDrop(hsk_did, "")
+ assert deckNames() == ["Chinese", "HSK", "Languages"]
+
def test_check():
d = getEmptyCol()
diff --git a/tests/test_exporting.py b/tests/test_exporting.py
index f1a14d800..f02754c3e 100644
--- a/tests/test_exporting.py
+++ b/tests/test_exporting.py
@@ -13,20 +13,26 @@ deck = None
ds = None
testDir = os.path.dirname(__file__)
+
def setup1():
global deck
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = "foo"; f['Back'] = "bar
"; f.tags = ["tag", "tag2"]
+ f["Front"] = "foo"
+ f["Back"] = "bar
"
+ f.tags = ["tag", "tag2"]
deck.addNote(f)
# with a different deck
f = deck.newNote()
- f['Front'] = "baz"; f['Back'] = "qux"
- f.model()['did'] = deck.decks.id("new deck")
+ f["Front"] = "baz"
+ f["Back"] = "qux"
+ f.model()["did"] = deck.decks.id("new deck")
deck.addNote(f)
+
##########################################################################
+
@with_setup(setup1)
def test_export_anki():
# create a new deck with its own conf to test conf copying
@@ -34,7 +40,7 @@ def test_export_anki():
dobj = deck.decks.get(did)
confId = deck.decks.confId("newconf")
conf = deck.decks.getConf(confId)
- conf['new']['perDay'] = 5
+ conf["new"]["perDay"] = 5
deck.decks.save(conf)
deck.decks.setConf(dobj, confId)
# export
@@ -46,7 +52,7 @@ def test_export_anki():
e.exportInto(newname)
# exporting should not have changed conf for original deck
conf = deck.decks.confForDid(did)
- assert conf['id'] != 1
+ assert conf["id"] != 1
# connect to new deck
d2 = aopen(newname)
assert d2.cardCount() == 2
@@ -54,10 +60,10 @@ def test_export_anki():
did = d2.decks.id("test", create=False)
assert did
conf2 = d2.decks.confForDid(did)
- assert conf2['new']['perDay'] == 20
+ assert conf2["new"]["perDay"] == 20
dobj = d2.decks.get(did)
# conf should be 1
- assert dobj['conf'] == 1
+ assert dobj["conf"] == 1
# try again, limited to a deck
fd, newname = tempfile.mkstemp(prefix="ankitest", suffix=".anki2")
newname = str(newname)
@@ -68,13 +74,14 @@ def test_export_anki():
d2 = aopen(newname)
assert d2.cardCount() == 1
+
@with_setup(setup1)
def test_export_ankipkg():
# add a test file to the media folder
with open(os.path.join(deck.media.dir(), "今日.mp3"), "w") as f:
f.write("test")
n = deck.newNote()
- n['Front'] = '[sound:今日.mp3]'
+ n["Front"] = "[sound:今日.mp3]"
deck.addNote(n)
e = AnkiPackageExporter(deck)
fd, newname = tempfile.mkstemp(prefix="ankitest", suffix=".apkg")
@@ -83,13 +90,14 @@ def test_export_ankipkg():
os.unlink(newname)
e.exportInto(newname)
+
@with_setup(setup1)
def test_export_anki_due():
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = "foo"
+ f["Front"] = "foo"
deck.addNote(f)
- deck.crt -= 86400*10
+ deck.crt -= 86400 * 10
deck.sched.reset()
c = deck.sched.getCard()
deck.sched.answerCard(c, 3)
@@ -115,6 +123,7 @@ def test_export_anki_due():
deck2.sched.reset()
assert c.due - deck2.sched.today == 1
+
# @with_setup(setup1)
# def test_export_textcard():
# e = TextCardExporter(deck)
@@ -124,6 +133,7 @@ def test_export_anki_due():
# e.includeTags = True
# e.exportInto(f)
+
@with_setup(setup1)
def test_export_textnote():
e = TextNoteExporter(deck)
@@ -138,5 +148,6 @@ def test_export_textnote():
e.exportInto(f)
assert open(f).readline() == "foo\tbar\n"
+
def test_exporters():
assert "*.apkg" in str(exporters())
diff --git a/tests/test_find.py b/tests/test_find.py
index cc7e91ede..f1a79edee 100644
--- a/tests/test_find.py
+++ b/tests/test_find.py
@@ -4,6 +4,7 @@ from nose2.tools.such import helper
from anki.find import Finder
from tests.shared import getEmptyCol
+
def test_parse():
f = Finder(None)
assert f._tokenize("hello world") == ["hello", "world"]
@@ -12,43 +13,54 @@ def test_parse():
assert f._tokenize("one --two") == ["one", "-", "two"]
assert f._tokenize("one - two") == ["one", "-", "two"]
assert f._tokenize("one or -two") == ["one", "or", "-", "two"]
- assert f._tokenize("'hello \"world\"'") == ["hello \"world\""]
+ assert f._tokenize("'hello \"world\"'") == ['hello "world"']
assert f._tokenize('"hello world"') == ["hello world"]
assert f._tokenize("one (two or ( three or four))") == [
- "one", "(", "two", "or", "(", "three", "or", "four",
- ")", ")"]
+ "one",
+ "(",
+ "two",
+ "or",
+ "(",
+ "three",
+ "or",
+ "four",
+ ")",
+ ")",
+ ]
assert f._tokenize("embedded'string") == ["embedded'string"]
assert f._tokenize("deck:'two words'") == ["deck:two words"]
+
def test_findCards():
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = 'dog'
- f['Back'] = 'cat'
+ f["Front"] = "dog"
+ f["Back"] = "cat"
f.tags.append("monkey animal_1 * %")
f1id = f.id
deck.addNote(f)
firstCardId = f.cards()[0].id
f = deck.newNote()
- f['Front'] = 'goats are fun'
- f['Back'] = 'sheep'
+ f["Front"] = "goats are fun"
+ f["Back"] = "sheep"
f.tags.append("sheep goat horse animal11")
deck.addNote(f)
f2id = f.id
f = deck.newNote()
- f['Front'] = 'cat'
- f['Back'] = 'sheep'
+ f["Front"] = "cat"
+ f["Back"] = "sheep"
deck.addNote(f)
catCard = f.cards()[0]
- m = deck.models.current(); mm = deck.models
+ m = deck.models.current()
+ mm = deck.models
t = mm.newTemplate("Reverse")
- t['qfmt'] = "{{Back}}"
- t['afmt'] = "{{Front}}"
+ t["qfmt"] = "{{Back}}"
+ t["afmt"] = "{{Front}}"
mm.addTemplate(m, t)
mm.save(m)
f = deck.newNote()
- f['Front'] = 'test'
- f['Back'] = 'foo bar'
+ f["Front"] = "test"
+ f["Back"] = "foo bar"
deck.addNote(f)
latestCardIds = [c.id for c in f.cards()]
# tag searches
@@ -66,9 +78,7 @@ def test_findCards():
assert len(deck.findCards("tag:sheep -tag:monkey")) == 1
assert len(deck.findCards("-tag:sheep")) == 4
deck.tags.bulkAdd(deck.db.list("select id from notes"), "foo bar")
- assert (len(deck.findCards("tag:foo")) ==
- len(deck.findCards("tag:bar")) ==
- 5)
+ assert len(deck.findCards("tag:foo")) == len(deck.findCards("tag:bar")) == 5
deck.tags.bulkRem(deck.db.list("select id from notes"), "foo")
assert len(deck.findCards("tag:foo")) == 0
assert len(deck.findCards("tag:bar")) == 5
@@ -86,7 +96,8 @@ def test_findCards():
c.flush()
assert deck.findCards("is:review") == [c.id]
assert deck.findCards("is:due") == []
- c.due = 0; c.queue = 2
+ c.due = 0
+ c.queue = 2
c.flush()
assert deck.findCards("is:due") == [c.id]
assert len(deck.findCards("-is:due")) == 4
@@ -97,7 +108,7 @@ def test_findCards():
assert deck.findCards("is:suspended") == [c.id]
# nids
assert deck.findCards("nid:54321") == []
- assert len(deck.findCards("nid:%d"%f.id)) == 2
+ assert len(deck.findCards("nid:%d" % f.id)) == 2
assert len(deck.findCards("nid:%d,%d" % (f1id, f2id))) == 2
# templates
with helper.assertRaises(Exception):
@@ -115,16 +126,16 @@ def test_findCards():
assert len(deck.findCards("front:do")) == 0
assert len(deck.findCards("front:*")) == 5
# ordering
- deck.conf['sortType'] = "noteCrt"
+ deck.conf["sortType"] = "noteCrt"
assert deck.findCards("front:*", order=True)[-1] in latestCardIds
assert deck.findCards("", order=True)[-1] in latestCardIds
- deck.conf['sortType'] = "noteFld"
+ deck.conf["sortType"] = "noteFld"
assert deck.findCards("", order=True)[0] == catCard.id
assert deck.findCards("", order=True)[-1] in latestCardIds
- deck.conf['sortType'] = "cardMod"
+ deck.conf["sortType"] = "cardMod"
assert deck.findCards("", order=True)[-1] in latestCardIds
assert deck.findCards("", order=True)[0] == firstCardId
- deck.conf['sortBackwards'] = True
+ deck.conf["sortBackwards"] = True
assert deck.findCards("", order=True)[0] in latestCardIds
# model
assert len(deck.findCards("note:basic")) == 5
@@ -140,25 +151,26 @@ def test_findCards():
deck.findCards("deck:*cefault")
# full search
f = deck.newNote()
- f['Front'] = 'helloworld'
- f['Back'] = 'abc'
+ f["Front"] = "helloworld"
+ f["Back"] = "abc"
deck.addNote(f)
# as it's the sort field, it matches
assert len(deck.findCards("helloworld")) == 2
- #assert len(deck.findCards("helloworld", full=True)) == 2
+ # assert len(deck.findCards("helloworld", full=True)) == 2
# if we put it on the back, it won't
- (f['Front'], f['Back']) = (f['Back'], f['Front'])
+ (f["Front"], f["Back"]) = (f["Back"], f["Front"])
f.flush()
assert len(deck.findCards("helloworld")) == 0
- #assert len(deck.findCards("helloworld", full=True)) == 2
- #assert len(deck.findCards("back:helloworld", full=True)) == 2
+ # assert len(deck.findCards("helloworld", full=True)) == 2
+ # assert len(deck.findCards("back:helloworld", full=True)) == 2
# searching for an invalid special tag should not error
with helper.assertRaises(Exception):
len(deck.findCards("is:invalid"))
# should be able to limit to parent deck, no children
id = deck.db.scalar("select id from cards limit 1")
- deck.db.execute("update cards set did = ? where id = ?",
- deck.decks.id("Default::Child"), id)
+ deck.db.execute(
+ "update cards set did = ? where id = ?", deck.decks.id("Default::Child"), id
+ )
assert len(deck.findCards("deck:default")) == 7
assert len(deck.findCards("deck:default::child")) == 1
assert len(deck.findCards("deck:default -deck:default::*")) == 6
@@ -166,7 +178,9 @@ def test_findCards():
id = deck.db.scalar("select id from cards limit 1")
deck.db.execute(
"update cards set queue=2, ivl=10, reps=20, due=30, factor=2200 "
- "where id = ?", id)
+ "where id = ?",
+ id,
+ )
assert len(deck.findCards("prop:ivl>5")) == 1
assert len(deck.findCards("prop:ivl<5")) > 1
assert len(deck.findCards("prop:ivl>=5")) == 1
@@ -205,8 +219,8 @@ def test_findCards():
# empty field
assert len(deck.findCards("front:")) == 0
f = deck.newNote()
- f['Front'] = ''
- f['Back'] = 'abc2'
+ f["Front"] = ""
+ f["Back"] = "abc2"
assert deck.addNote(f) == 1
assert len(deck.findCards("front:")) == 1
# OR searches and nesting
@@ -220,8 +234,7 @@ def test_findCards():
assert len(deck.findCards("(()")) == 0
# added
assert len(deck.findCards("added:0")) == 0
- deck.db.execute("update cards set id = id - 86400*1000 where id = ?",
- id)
+ deck.db.execute("update cards set id = id - 86400*1000 where id = ?", id)
assert len(deck.findCards("added:1")) == deck.cardCount() - 1
assert len(deck.findCards("added:2")) == deck.cardCount()
# flag
@@ -230,50 +243,58 @@ def test_findCards():
with helper.assertRaises(Exception):
deck.findCards("flag:12")
+
def test_findReplace():
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = 'foo'
- f['Back'] = 'bar'
+ f["Front"] = "foo"
+ f["Back"] = "bar"
deck.addNote(f)
f2 = deck.newNote()
- f2['Front'] = 'baz'
- f2['Back'] = 'foo'
+ f2["Front"] = "baz"
+ f2["Back"] = "foo"
deck.addNote(f2)
nids = [f.id, f2.id]
# should do nothing
assert deck.findReplace(nids, "abc", "123") == 0
# global replace
assert deck.findReplace(nids, "foo", "qux") == 2
- f.load(); assert f['Front'] == "qux"
- f2.load(); assert f2['Back'] == "qux"
+ f.load()
+ assert f["Front"] == "qux"
+ f2.load()
+ assert f2["Back"] == "qux"
# single field replace
assert deck.findReplace(nids, "qux", "foo", field="Front") == 1
- f.load(); assert f['Front'] == "foo"
- f2.load(); assert f2['Back'] == "qux"
+ f.load()
+ assert f["Front"] == "foo"
+ f2.load()
+ assert f2["Back"] == "qux"
# regex replace
assert deck.findReplace(nids, "B.r", "reg") == 0
- f.load(); assert f['Back'] != "reg"
+ f.load()
+ assert f["Back"] != "reg"
assert deck.findReplace(nids, "B.r", "reg", regex=True) == 1
- f.load(); assert f['Back'] == "reg"
+ f.load()
+ assert f["Back"] == "reg"
+
def test_findDupes():
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = 'foo'
- f['Back'] = 'bar'
+ f["Front"] = "foo"
+ f["Back"] = "bar"
deck.addNote(f)
f2 = deck.newNote()
- f2['Front'] = 'baz'
- f2['Back'] = 'bar'
+ f2["Front"] = "baz"
+ f2["Back"] = "bar"
deck.addNote(f2)
f3 = deck.newNote()
- f3['Front'] = 'quux'
- f3['Back'] = 'bar'
+ f3["Front"] = "quux"
+ f3["Back"] = "bar"
deck.addNote(f3)
f4 = deck.newNote()
- f4['Front'] = 'quuux'
- f4['Back'] = 'nope'
+ f4["Front"] = "quuux"
+ f4["Back"] = "nope"
deck.addNote(f4)
r = deck.findDupes("Back")
assert r[0][0] == "bar"
diff --git a/tests/test_flags.py b/tests/test_flags.py
index 11796b692..1fb988fc4 100644
--- a/tests/test_flags.py
+++ b/tests/test_flags.py
@@ -1,9 +1,11 @@
from tests.shared import assertException, getEmptyCol
+
def test_flags():
col = getEmptyCol()
n = col.newNote()
- n['Front'] = "one"; n['Back'] = "two"
+ n["Front"] = "one"
+ n["Back"] = "two"
cnt = col.addNote(n)
c = n.cards()[0]
# make sure higher bits are preserved
diff --git a/tests/test_importing.py b/tests/test_importing.py
index 67218d994..fb51a9383 100644
--- a/tests/test_importing.py
+++ b/tests/test_importing.py
@@ -1,22 +1,28 @@
# coding: utf-8
-import os
-from tests.shared import getUpgradeDeckPath, getEmptyCol
+import os
+from tests.shared import getUpgradeDeckPath, getEmptyCol
from anki.utils import ids2str
-from anki.importing import Anki2Importer, TextImporter, \
- SupermemoXmlImporter, MnemosyneImporter, AnkiPackageImporter
+from anki.importing import (
+ Anki2Importer,
+ TextImporter,
+ SupermemoXmlImporter,
+ MnemosyneImporter,
+ AnkiPackageImporter,
+)
testDir = os.path.dirname(__file__)
-srcNotes=None
-srcCards=None
+srcNotes = None
+srcCards = None
+
def test_anki2_mediadupes():
tmp = getEmptyCol()
# add a note that references a sound
n = tmp.newNote()
- n['Front'] = "[sound:foo.mp3]"
- mid = n.model()['id']
+ n["Front"] = "[sound:foo.mp3]"
+ mid = n.model()["id"]
tmp.addNote(n)
# add that sound to media folder
with open(os.path.join(tmp.media.dir(), "foo.mp3"), "w") as f:
@@ -41,8 +47,7 @@ def test_anki2_mediadupes():
f.write("bar")
imp = Anki2Importer(empty, tmp.path)
imp.run()
- assert sorted(os.listdir(empty.media.dir())) == [
- "foo.mp3", "foo_%s.mp3" % mid]
+ assert sorted(os.listdir(empty.media.dir())) == ["foo.mp3", "foo_%s.mp3" % mid]
n = empty.getNote(empty.db.scalar("select id from notes"))
assert "_" in n.fields[0]
# if the localized media file already exists, we rewrite the note and
@@ -52,25 +57,24 @@ def test_anki2_mediadupes():
f.write("bar")
imp = Anki2Importer(empty, tmp.path)
imp.run()
- assert sorted(os.listdir(empty.media.dir())) == [
- "foo.mp3", "foo_%s.mp3" % mid]
- assert sorted(os.listdir(empty.media.dir())) == [
- "foo.mp3", "foo_%s.mp3" % mid]
+ assert sorted(os.listdir(empty.media.dir())) == ["foo.mp3", "foo_%s.mp3" % mid]
+ assert sorted(os.listdir(empty.media.dir())) == ["foo.mp3", "foo_%s.mp3" % mid]
n = empty.getNote(empty.db.scalar("select id from notes"))
assert "_" in n.fields[0]
+
def test_apkg():
tmp = getEmptyCol()
apkg = str(os.path.join(testDir, "support/media.apkg"))
imp = AnkiPackageImporter(tmp, apkg)
assert os.listdir(tmp.media.dir()) == []
imp.run()
- assert os.listdir(tmp.media.dir()) == ['foo.wav']
+ assert os.listdir(tmp.media.dir()) == ["foo.wav"]
# importing again should be idempotent in terms of media
tmp.remCards(tmp.db.list("select id from cards"))
imp = AnkiPackageImporter(tmp, apkg)
imp.run()
- assert os.listdir(tmp.media.dir()) == ['foo.wav']
+ assert os.listdir(tmp.media.dir()) == ["foo.wav"]
# but if the local file has different data, it will rename
tmp.remCards(tmp.db.list("select id from cards"))
with open(os.path.join(tmp.media.dir(), "foo.wav"), "w") as f:
@@ -79,6 +83,7 @@ def test_apkg():
imp.run()
assert len(os.listdir(tmp.media.dir())) == 2
+
def test_anki2_diffmodel_templates():
# different from the above as this one tests only the template text being
# changed, not the number of cards/fields
@@ -94,11 +99,12 @@ def test_anki2_diffmodel_templates():
imp.dupeOnSchemaChange = True
imp.run()
# collection should contain the note we imported
- assert(dst.noteCount() == 1)
+ assert dst.noteCount() == 1
# the front template should contain the text added in the 2nd package
- tcid = dst.findCards("")[0] # only 1 note in collection
+ tcid = dst.findCards("")[0] # only 1 note in collection
tnote = dst.getCard(tcid).note()
- assert("Changed Front Template" in dst.findTemplates(tnote)[0]['qfmt'])
+ assert "Changed Front Template" in dst.findTemplates(tnote)[0]["qfmt"]
+
def test_anki2_updates():
# create a new empty deck
@@ -127,6 +133,7 @@ def test_anki2_updates():
assert dst.noteCount() == 1
assert dst.db.scalar("select flds from notes").startswith("goodbye")
+
def test_csv():
deck = getEmptyCol()
file = str(os.path.join(testDir, "support/text-2fields.txt"))
@@ -147,7 +154,7 @@ def test_csv():
n.flush()
i.run()
n.load()
- assert n.tags == ['test']
+ assert n.tags == ["test"]
# if add-only mode, count will be 0
i.importMode = 1
i.run()
@@ -161,6 +168,7 @@ def test_csv():
assert deck.cardCount() == 11
deck.close()
+
def test_csv2():
deck = getEmptyCol()
mm = deck.models
@@ -169,9 +177,9 @@ def test_csv2():
mm.addField(m, f)
mm.save(m)
n = deck.newNote()
- n['Front'] = "1"
- n['Back'] = "2"
- n['Three'] = "3"
+ n["Front"] = "1"
+ n["Back"] = "2"
+ n["Three"] = "3"
deck.addNote(n)
# an update with unmapped fields should not clobber those fields
file = str(os.path.join(testDir, "support/text-update.txt"))
@@ -179,16 +187,17 @@ def test_csv2():
i.initMapping()
i.run()
n.load()
- assert n['Front'] == "1"
- assert n['Back'] == "x"
- assert n['Three'] == "3"
+ assert n["Front"] == "1"
+ assert n["Back"] == "x"
+ assert n["Three"] == "3"
deck.close()
+
def test_supermemo_xml_01_unicode():
deck = getEmptyCol()
file = str(os.path.join(testDir, "support/supermemo1.xml"))
i = SupermemoXmlImporter(deck, file)
- #i.META.logToStdOutput = True
+ # i.META.logToStdOutput = True
i.run()
assert i.total == 1
cid = deck.db.scalar("select id from cards")
@@ -198,6 +207,7 @@ def test_supermemo_xml_01_unicode():
assert c.reps == 7
deck.close()
+
def test_mnemo():
deck = getEmptyCol()
file = str(os.path.join(testDir, "support/mnemo.db"))
diff --git a/tests/test_latex.py b/tests/test_latex.py
index 8b5937a20..5bd6c9dae 100644
--- a/tests/test_latex.py
+++ b/tests/test_latex.py
@@ -4,17 +4,19 @@ import os
import shutil
-from tests.shared import getEmptyCol
+from tests.shared import getEmptyCol
from anki.utils import stripHTML
+
def test_latex():
d = getEmptyCol()
# change latex cmd to simulate broken build
import anki.latex
+
anki.latex.pngCommands[0][0] = "nolatex"
# add a note with latex
f = d.newNote()
- f['Front'] = "[latex]hello[/latex]"
+ f["Front"] = "[latex]hello[/latex]"
d.addNote(f)
# but since latex couldn't run, there's nothing there
assert len(os.listdir(d.media.dir())) == 0
@@ -34,13 +36,13 @@ def test_latex():
assert ".png" in f.cards()[0].q()
# adding new notes should cause generation on question display
f = d.newNote()
- f['Front'] = "[latex]world[/latex]"
+ f["Front"] = "[latex]world[/latex]"
d.addNote(f)
f.cards()[0].q()
assert len(os.listdir(d.media.dir())) == 2
# another note with the same media should reuse
f = d.newNote()
- f['Front'] = " [latex]world[/latex]"
+ f["Front"] = " [latex]world[/latex]"
d.addNote(f)
assert len(os.listdir(d.media.dir())) == 2
oldcard = f.cards()[0]
@@ -49,7 +51,7 @@ def test_latex():
# missing media will show the latex
anki.latex.build = False
f = d.newNote()
- f['Front'] = "[latex]foo[/latex]"
+ f["Front"] = "[latex]foo[/latex]"
d.addNote(f)
assert len(os.listdir(d.media.dir())) == 2
assert stripHTML(f.cards()[0].q()) == "[latex]foo[/latex]"
@@ -86,10 +88,11 @@ def test_latex():
(result, msg) = _test_includes_bad_command("\\emph")
assert not result, msg
+
def _test_includes_bad_command(bad):
d = getEmptyCol()
f = d.newNote()
- f['Front'] = '[latex]%s[/latex]' % bad
+ f["Front"] = "[latex]%s[/latex]" % bad
d.addNote(f)
q = f.cards()[0].q()
return ("'%s' is not allowed on cards" % bad in q, "Card content: %s" % q)
diff --git a/tests/test_media.py b/tests/test_media.py
index e8198a543..6f23cd392 100644
--- a/tests/test_media.py
+++ b/tests/test_media.py
@@ -23,6 +23,7 @@ def test_add():
f.write("world")
assert d.media.addFile(path) == "foo (1).jpg"
+
def test_strings():
d = getEmptyCol()
mf = d.media.filesInStr
@@ -31,12 +32,16 @@ def test_strings():
assert mf(mid, "aoeu
ao") == ["foo.jpg"]
assert mf(mid, "aoeu
ao") == ["foo.jpg"]
assert mf(mid, "aoeu
ao") == [
- "foo.jpg", "bar.jpg"]
+ "foo.jpg",
+ "bar.jpg",
+ ]
assert mf(mid, "aoeu
ao") == ["foo.jpg"]
assert mf(mid, "
") == ["one", "two"]
- assert mf(mid, "aoeu
ao") == ["foo.jpg"]
- assert mf(mid, "aoeu
ao") == [
- "foo.jpg", "fo"]
+ assert mf(mid, 'aoeu
ao') == ["foo.jpg"]
+ assert mf(mid, 'aoeu
ao') == [
+ "foo.jpg",
+ "fo",
+ ]
assert mf(mid, "aou[sound:foo.mp3]aou") == ["foo.mp3"]
sp = d.media.strip
assert sp("aoeu") == "aoeu"
@@ -47,6 +52,7 @@ def test_strings():
assert es("
") == "
"
assert es('
') == '
'
+
def test_deckIntegration():
d = getEmptyCol()
# create a media dir
@@ -56,11 +62,13 @@ def test_deckIntegration():
d.media.addFile(file)
# add a note which references it
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "
"
+ f["Front"] = "one"
+ f["Back"] = "
"
d.addNote(f)
# and one which references a non-existent file
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "
"
+ f["Front"] = "one"
+ f["Back"] = "
"
d.addNote(f)
# and add another file which isn't used
with open(os.path.join(d.media.dir(), "foo.jpg"), "w") as f:
@@ -70,12 +78,16 @@ def test_deckIntegration():
assert ret[0] == ["fake2.png"]
assert ret[1] == ["foo.jpg"]
+
def test_changes():
d = getEmptyCol()
+
def added():
return d.media.db.execute("select fname from media where csum is not null")
+
def removed():
return d.media.db.execute("select fname from media where csum is null")
+
assert not list(added())
assert not list(removed())
# add a file
@@ -97,26 +109,27 @@ def test_changes():
assert not list(removed())
# but if we add another file, it will
time.sleep(1)
- with open(path+"2", "w") as f:
+ with open(path + "2", "w") as f:
f.write("yo")
d.media.findChanges()
assert len(list(added())) == 2
assert not list(removed())
# deletions should get noticed too
time.sleep(1)
- os.unlink(path+"2")
+ os.unlink(path + "2")
d.media.findChanges()
assert len(list(added())) == 1
assert len(list(removed())) == 1
+
def test_illegal():
d = getEmptyCol()
aString = "a:b|cd\\e/f\0g*h"
good = "abcdefgh"
assert d.media.stripIllegal(aString) == good
for c in aString:
- bad = d.media.hasIllegal("somestring"+c+"morestring")
+ bad = d.media.hasIllegal("somestring" + c + "morestring")
if bad:
- assert(c not in good)
+ assert c not in good
else:
- assert(c in good)
+ assert c in good
diff --git a/tests/test_models.py b/tests/test_models.py
index 823be124e..7deb26b44 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -6,39 +6,42 @@ from anki.consts import MODEL_CLOZE
from anki.utils import stripHTML, joinFields, isWin
import anki.template
+
def test_modelDelete():
deck = getEmptyCol()
f = deck.newNote()
- f['Front'] = '1'
- f['Back'] = '2'
+ f["Front"] = "1"
+ f["Back"] = "2"
deck.addNote(f)
assert deck.cardCount() == 1
deck.models.rem(deck.models.current())
assert deck.cardCount() == 0
+
def test_modelCopy():
deck = getEmptyCol()
m = deck.models.current()
m2 = deck.models.copy(m)
- assert m2['name'] == "Basic copy"
- assert m2['id'] != m['id']
- assert len(m2['flds']) == 2
- assert len(m['flds']) == 2
- assert len(m2['flds']) == len(m['flds'])
- assert len(m['tmpls']) == 1
- assert len(m2['tmpls']) == 1
+ assert m2["name"] == "Basic copy"
+ assert m2["id"] != m["id"]
+ assert len(m2["flds"]) == 2
+ assert len(m["flds"]) == 2
+ assert len(m2["flds"]) == len(m["flds"])
+ assert len(m["tmpls"]) == 1
+ assert len(m2["tmpls"]) == 1
assert deck.models.scmhash(m) == deck.models.scmhash(m2)
+
def test_fields():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = '1'
- f['Back'] = '2'
+ f["Front"] = "1"
+ f["Back"] = "2"
d.addNote(f)
m = d.models.current()
# make sure renaming a field updates the templates
- d.models.renameField(m, m['flds'][0], "NewFront")
- assert "{{NewFront}}" in m['tmpls'][0]['qfmt']
+ d.models.renameField(m, m["flds"][0], "NewFront")
+ assert "{{NewFront}}" in m["tmpls"][0]["qfmt"]
h = d.models.scmhash(m)
# add a field
f = d.models.newField("foo")
@@ -47,44 +50,46 @@ def test_fields():
assert d.models.scmhash(m) != h
# rename it
d.models.renameField(m, f, "bar")
- assert d.getNote(d.models.nids(m)[0])['bar'] == ''
+ assert d.getNote(d.models.nids(m)[0])["bar"] == ""
# delete back
- d.models.remField(m, m['flds'][1])
+ d.models.remField(m, m["flds"][1])
assert d.getNote(d.models.nids(m)[0]).fields == ["1", ""]
# move 0 -> 1
- d.models.moveField(m, m['flds'][0], 1)
+ d.models.moveField(m, m["flds"][0], 1)
assert d.getNote(d.models.nids(m)[0]).fields == ["", "1"]
# move 1 -> 0
- d.models.moveField(m, m['flds'][1], 0)
+ d.models.moveField(m, m["flds"][1], 0)
assert d.getNote(d.models.nids(m)[0]).fields == ["1", ""]
# add another and put in middle
f = d.models.newField("baz")
d.models.addField(m, f)
f = d.getNote(d.models.nids(m)[0])
- f['baz'] = "2"
+ f["baz"] = "2"
f.flush()
assert d.getNote(d.models.nids(m)[0]).fields == ["1", "", "2"]
# move 2 -> 1
- d.models.moveField(m, m['flds'][2], 1)
+ d.models.moveField(m, m["flds"][2], 1)
assert d.getNote(d.models.nids(m)[0]).fields == ["1", "2", ""]
# move 0 -> 2
- d.models.moveField(m, m['flds'][0], 2)
+ d.models.moveField(m, m["flds"][0], 2)
assert d.getNote(d.models.nids(m)[0]).fields == ["2", "", "1"]
# move 0 -> 1
- d.models.moveField(m, m['flds'][0], 1)
+ d.models.moveField(m, m["flds"][0], 1)
assert d.getNote(d.models.nids(m)[0]).fields == ["", "2", "1"]
+
def test_templates():
d = getEmptyCol()
- m = d.models.current(); mm = d.models
+ m = d.models.current()
+ mm = d.models
t = mm.newTemplate("Reverse")
- t['qfmt'] = "{{Back}}"
- t['afmt'] = "{{Front}}"
+ t["qfmt"] = "{{Back}}"
+ t["afmt"] = "{{Front}}"
mm.addTemplate(m, t)
mm.save(m)
f = d.newNote()
- f['Front'] = '1'
- f['Back'] = '2'
+ f["Front"] = "1"
+ f["Back"] = "2"
d.addNote(f)
assert d.cardCount() == 2
(c, c2) = f.cards()
@@ -93,11 +98,12 @@ def test_templates():
assert c2.ord == 1
# switch templates
d.models.moveTemplate(m, c.template(), 1)
- c.load(); c2.load()
+ c.load()
+ c2.load()
assert c.ord == 1
assert c2.ord == 0
# removing a template should delete its cards
- assert d.models.remTemplate(m, m['tmpls'][0])
+ assert d.models.remTemplate(m, m["tmpls"][0])
assert d.cardCount() == 1
# and should have updated the other cards' ordinals
c = f.cards()[0]
@@ -106,64 +112,67 @@ def test_templates():
# it shouldn't be possible to orphan notes by removing templates
t = mm.newTemplate("template name")
mm.addTemplate(m, t)
- assert not d.models.remTemplate(m, m['tmpls'][0])
+ assert not d.models.remTemplate(m, m["tmpls"][0])
+
def test_cloze_ordinals():
d = getEmptyCol()
d.models.setCurrent(d.models.byName("Cloze"))
- m = d.models.current(); mm = d.models
-
- #We replace the default Cloze template
+ m = d.models.current()
+ mm = d.models
+
+ # We replace the default Cloze template
t = mm.newTemplate("ChainedCloze")
- t['qfmt'] = "{{text:cloze:Text}}"
- t['afmt'] = "{{text:cloze:Text}}"
+ t["qfmt"] = "{{text:cloze:Text}}"
+ t["afmt"] = "{{text:cloze:Text}}"
mm.addTemplate(m, t)
mm.save(m)
- d.models.remTemplate(m, m['tmpls'][0])
-
+ d.models.remTemplate(m, m["tmpls"][0])
+
f = d.newNote()
- f['Text'] = '{{c1::firstQ::firstA}}{{c2::secondQ::secondA}}'
+ f["Text"] = "{{c1::firstQ::firstA}}{{c2::secondQ::secondA}}"
d.addNote(f)
assert d.cardCount() == 2
(c, c2) = f.cards()
# first card should have first ord
assert c.ord == 0
assert c2.ord == 1
-
+
def test_text():
d = getEmptyCol()
m = d.models.current()
- m['tmpls'][0]['qfmt'] = "{{text:Front}}"
+ m["tmpls"][0]["qfmt"] = "{{text:Front}}"
d.models.save(m)
f = d.newNote()
- f['Front'] = 'helloworld'
+ f["Front"] = "helloworld"
d.addNote(f)
assert "helloworld" in f.cards()[0].q()
+
def test_cloze():
d = getEmptyCol()
d.models.setCurrent(d.models.byName("Cloze"))
f = d.newNote()
- assert f.model()['name'] == "Cloze"
+ assert f.model()["name"] == "Cloze"
# a cloze model with no clozes is not empty
- f['Text'] = 'nothing'
+ f["Text"] = "nothing"
assert d.addNote(f)
# try with one cloze
f = d.newNote()
- f['Text'] = "hello {{c1::world}}"
+ f["Text"] = "hello {{c1::world}}"
assert d.addNote(f) == 1
assert "hello [...]" in f.cards()[0].q()
assert "hello world" in f.cards()[0].a()
# and with a comment
f = d.newNote()
- f['Text'] = "hello {{c1::world::typical}}"
+ f["Text"] = "hello {{c1::world::typical}}"
assert d.addNote(f) == 1
assert "[typical]" in f.cards()[0].q()
assert "world" in f.cards()[0].a()
# and with 2 clozes
f = d.newNote()
- f['Text'] = "hello {{c1::world}} {{c2::bar}}"
+ f["Text"] = "hello {{c1::world}} {{c2::bar}}"
assert d.addNote(f) == 2
(c1, c2) = f.cards()
assert "[...] bar" in c1.q()
@@ -173,25 +182,27 @@ def test_cloze():
# if there are multiple answers for a single cloze, they are given in a
# list
f = d.newNote()
- f['Text'] = "a {{c1::b}} {{c1::c}}"
+ f["Text"] = "a {{c1::b}} {{c1::c}}"
assert d.addNote(f) == 1
- assert "b c" in (
- f.cards()[0].a())
+ assert "b c" in (f.cards()[0].a())
# if we add another cloze, a card should be generated
cnt = d.cardCount()
- f['Text'] = "{{c2::hello}} {{c1::foo}}"
+ f["Text"] = "{{c2::hello}} {{c1::foo}}"
f.flush()
assert d.cardCount() == cnt + 1
# 0 or negative indices are not supported
- f['Text'] += "{{c0::zero}} {{c-1:foo}}"
+ f["Text"] += "{{c0::zero}} {{c-1:foo}}"
f.flush()
assert len(f.cards()) == 2
+
def test_cloze_mathjax():
d = getEmptyCol()
d.models.setCurrent(d.models.byName("Cloze"))
f = d.newNote()
- f['Text'] = r'{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) {{c4::blah}} {{c5::text with \(x^2\) jax}}'
+ f[
+ "Text"
+ ] = r"{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) {{c4::blah}} {{c5::text with \(x^2\) jax}}"
assert d.addNote(f)
assert len(f.cards()) == 5
assert "class=cloze" in f.cards()[0].q()
@@ -201,56 +212,70 @@ def test_cloze_mathjax():
assert "class=cloze" in f.cards()[4].q()
f = d.newNote()
- f['Text'] = r'\(a\) {{c1::b}} \[ {{c1::c}} \]'
+ f["Text"] = r"\(a\) {{c1::b}} \[ {{c1::c}} \]"
assert d.addNote(f)
assert len(f.cards()) == 1
- assert f.cards()[0].q().endswith('\(a\) [...] \[ [...] \]')
+ assert f.cards()[0].q().endswith("\(a\) [...] \[ [...] \]")
def test_chained_mods():
d = getEmptyCol()
d.models.setCurrent(d.models.byName("Cloze"))
- m = d.models.current(); mm = d.models
-
- #We replace the default Cloze template
+ m = d.models.current()
+ mm = d.models
+
+ # We replace the default Cloze template
t = mm.newTemplate("ChainedCloze")
- t['qfmt'] = "{{cloze:text:Text}}"
- t['afmt'] = "{{cloze:text:Text}}"
+ t["qfmt"] = "{{cloze:text:Text}}"
+ t["afmt"] = "{{cloze:text:Text}}"
mm.addTemplate(m, t)
mm.save(m)
- d.models.remTemplate(m, m['tmpls'][0])
-
+ d.models.remTemplate(m, m["tmpls"][0])
+
f = d.newNote()
- q1 = 'phrase'
- a1 = 'sentence'
- q2 = 'en chaine'
- a2 = 'chained'
- f['Text'] = "This {{c1::%s::%s}} demonstrates {{c1::%s::%s}} clozes." % (q1,a1,q2,a2)
+ q1 = 'phrase'
+ a1 = "sentence"
+ q2 = 'en chaine'
+ a2 = "chained"
+ f["Text"] = "This {{c1::%s::%s}} demonstrates {{c1::%s::%s}} clozes." % (
+ q1,
+ a1,
+ q2,
+ a2,
+ )
assert d.addNote(f) == 1
- assert "This [sentence] demonstrates [chained] clozes." in f.cards()[0].q()
- assert "This phrase demonstrates en chaine clozes." in f.cards()[0].a()
+ assert (
+ "This [sentence] demonstrates [chained] clozes."
+ in f.cards()[0].q()
+ )
+ assert (
+ "This phrase demonstrates en chaine clozes."
+ in f.cards()[0].a()
+ )
+
def test_modelChange():
deck = getEmptyCol()
basic = deck.models.byName("Basic")
cloze = deck.models.byName("Cloze")
# enable second template and add a note
- m = deck.models.current(); mm = deck.models
+ m = deck.models.current()
+ mm = deck.models
t = mm.newTemplate("Reverse")
- t['qfmt'] = "{{Back}}"
- t['afmt'] = "{{Front}}"
+ t["qfmt"] = "{{Back}}"
+ t["afmt"] = "{{Front}}"
mm.addTemplate(m, t)
mm.save(m)
f = deck.newNote()
- f['Front'] = 'f'
- f['Back'] = 'b123'
+ f["Front"] = "f"
+ f["Back"] = "b123"
deck.addNote(f)
# switch fields
map = {0: 1, 1: 0}
deck.models.change(basic, [f.id], basic, map, None)
f.load()
- assert f['Front'] == 'b123'
- assert f['Back'] == 'f'
+ assert f["Front"] == "b123"
+ assert f["Back"] == "f"
# switch cards
c0 = f.cards()[0]
c1 = f.cards()[1]
@@ -259,7 +284,9 @@ def test_modelChange():
assert c0.ord == 0
assert c1.ord == 1
deck.models.change(basic, [f.id], basic, None, map)
- f.load(); c0.load(); c1.load()
+ f.load()
+ c0.load()
+ c1.load()
assert "f" in c0.q()
assert "b123" in c1.q()
assert c0.ord == 1
@@ -283,30 +310,31 @@ def test_modelChange():
# but we have two cards, as a new one was generated
assert len(f.cards()) == 2
# an unmapped field becomes blank
- assert f['Front'] == 'b123'
- assert f['Back'] == 'f'
+ assert f["Front"] == "b123"
+ assert f["Back"] == "f"
deck.models.change(basic, [f.id], basic, map, None)
f.load()
- assert f['Front'] == ''
- assert f['Back'] == 'f'
+ assert f["Front"] == ""
+ assert f["Back"] == "f"
# another note to try model conversion
f = deck.newNote()
- f['Front'] = 'f2'
- f['Back'] = 'b2'
+ f["Front"] = "f2"
+ f["Back"] = "b2"
deck.addNote(f)
assert deck.models.useCount(basic) == 2
assert deck.models.useCount(cloze) == 0
map = {0: 0, 1: 1}
deck.models.change(basic, [f.id], cloze, map, map)
f.load()
- assert f['Text'] == "f2"
+ assert f["Text"] == "f2"
assert len(f.cards()) == 2
# back the other way, with deletion of second ord
- deck.models.remTemplate(basic, basic['tmpls'][1])
+ deck.models.remTemplate(basic, basic["tmpls"][1])
assert deck.db.scalar("select count() from cards where nid = ?", f.id) == 2
deck.models.change(cloze, [f.id], basic, map, map)
assert deck.db.scalar("select count() from cards where nid = ?", f.id) == 1
+
def test_templates():
d = dict(Foo="x", Bar="y")
assert anki.template.render("{{Foo}}", d) == "x"
@@ -315,68 +343,72 @@ def test_templates():
assert anki.template.render("{{#Bar}}{{#Foo}}{{Foo}}{{/Foo}}{{/Bar}}", d) == "x"
assert anki.template.render("{{#Baz}}{{#Foo}}{{Foo}}{{/Foo}}{{/Baz}}", d) == ""
+
def test_availOrds():
d = getEmptyCol()
- m = d.models.current(); mm = d.models
- t = m['tmpls'][0]
+ m = d.models.current()
+ mm = d.models
+ t = m["tmpls"][0]
f = d.newNote()
- f['Front'] = "1"
+ f["Front"] = "1"
# simple templates
assert mm.availOrds(m, joinFields(f.fields)) == [0]
- t['qfmt'] = "{{Back}}"
+ t["qfmt"] = "{{Back}}"
mm.save(m, templates=True)
assert not mm.availOrds(m, joinFields(f.fields))
# AND
- t['qfmt'] = "{{#Front}}{{#Back}}{{Front}}{{/Back}}{{/Front}}"
+ t["qfmt"] = "{{#Front}}{{#Back}}{{Front}}{{/Back}}{{/Front}}"
mm.save(m, templates=True)
assert not mm.availOrds(m, joinFields(f.fields))
- t['qfmt'] = "{{#Front}}\n{{#Back}}\n{{Front}}\n{{/Back}}\n{{/Front}}"
+ t["qfmt"] = "{{#Front}}\n{{#Back}}\n{{Front}}\n{{/Back}}\n{{/Front}}"
mm.save(m, templates=True)
assert not mm.availOrds(m, joinFields(f.fields))
# OR
- t['qfmt'] = "{{Front}}\n{{Back}}"
+ t["qfmt"] = "{{Front}}\n{{Back}}"
mm.save(m, templates=True)
assert mm.availOrds(m, joinFields(f.fields)) == [0]
- t['Front'] = ""
- t['Back'] = "1"
+ t["Front"] = ""
+ t["Back"] = "1"
assert mm.availOrds(m, joinFields(f.fields)) == [0]
+
def test_req():
def reqSize(model):
- if model['type'] == MODEL_CLOZE:
+ if model["type"] == MODEL_CLOZE:
return
- assert (len(model['tmpls']) == len(model['req']))
+ assert len(model["tmpls"]) == len(model["req"])
d = getEmptyCol()
mm = d.models
basic = mm.byName("Basic")
- assert 'req' in basic
+ assert "req" in basic
reqSize(basic)
- r = basic['req'][0]
+ r = basic["req"][0]
assert r[0] == 0
assert r[1] in ("any", "all")
assert r[2] == [0]
opt = mm.byName("Basic (optional reversed card)")
reqSize(opt)
- r = opt['req'][0]
+ r = opt["req"][0]
assert r[1] in ("any", "all")
assert r[2] == [0]
- assert opt['req'][1] == [1, 'all', [1, 2]]
- #testing any
- opt['tmpls'][1]['qfmt'] = "{{Back}}{{Add Reverse}}"
+ assert opt["req"][1] == [1, "all", [1, 2]]
+ # testing any
+ opt["tmpls"][1]["qfmt"] = "{{Back}}{{Add Reverse}}"
mm.save(opt, templates=True)
- assert opt['req'][1] == [1, 'any', [1, 2]]
- #testing None
- opt['tmpls'][1]['qfmt'] = "{{^Add Reverse}}{{Back}}{{/Add Reverse}}"
+ assert opt["req"][1] == [1, "any", [1, 2]]
+ # testing None
+ opt["tmpls"][1]["qfmt"] = "{{^Add Reverse}}{{Back}}{{/Add Reverse}}"
mm.save(opt, templates=True)
- assert opt['req'][1] == [1, 'none', []]
+ assert opt["req"][1] == [1, "none", []]
opt = mm.byName("Basic (type in the answer)")
reqSize(opt)
- r = opt['req'][0]
+ r = opt["req"][0]
assert r[1] in ("any", "all")
assert r[2] == [0]
+
# def test_updatereqs_performance():
# import time
# d = getEmptyCol()
diff --git a/tests/test_schedv1.py b/tests/test_schedv1.py
index 4a2c00b3b..ad426164d 100644
--- a/tests/test_schedv1.py
+++ b/tests/test_schedv1.py
@@ -5,35 +5,41 @@ import copy
from anki.consts import STARTING_FACTOR
from tests.shared import getEmptyCol as getEmptyColOrig
-from anki.utils import intTime
+from anki.utils import intTime
from anki.hooks import addHook
+
def getEmptyCol():
col = getEmptyColOrig()
col.changeSchedulerVer(1)
return col
+
def test_clock():
d = getEmptyCol()
- if (d.sched.dayCutoff - intTime()) < 10*60:
+ if (d.sched.dayCutoff - intTime()) < 10 * 60:
raise Exception("Unit tests will fail around the day rollover.")
+
def checkRevIvl(d, c, targetIvl):
min, max = d.sched._fuzzIvlRange(targetIvl)
return min <= c.ivl <= max
+
def test_basics():
d = getEmptyCol()
d.reset()
assert not d.sched.getCard()
+
def test_new():
d = getEmptyCol()
d.reset()
assert d.sched.newCount == 0
# add a note
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
d.reset()
assert d.sched.newCount == 1
@@ -71,15 +77,16 @@ def test_new():
# assert qs[n] in c.q()
# d.sched.answerCard(c, 2)
+
def test_newLimits():
d = getEmptyCol()
# add some notes
g2 = d.decks.id("Default::foo")
for i in range(30):
f = d.newNote()
- f['Front'] = str(i)
+ f["Front"] = str(i)
if i > 4:
- f.model()['did'] = g2
+ f.model()["did"] = g2
d.addNote(f)
# give the child deck a different configuration
c2 = d.decks.confId("new conf")
@@ -92,33 +99,36 @@ def test_newLimits():
assert c.did == 1
# limit the parent to 10 cards, meaning we get 10 in total
conf1 = d.decks.confForDid(1)
- conf1['new']['perDay'] = 10
+ conf1["new"]["perDay"] = 10
d.reset()
assert d.sched.newCount == 10
# if we limit child to 4, we should get 9
conf2 = d.decks.confForDid(g2)
- conf2['new']['perDay'] = 4
+ conf2["new"]["perDay"] = 4
d.reset()
assert d.sched.newCount == 9
+
def test_newBoxes():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
d.reset()
c = d.sched.getCard()
- d.sched._cardConf(c)['new']['delays'] = [1,2,3,4,5]
+ d.sched._cardConf(c)["new"]["delays"] = [1, 2, 3, 4, 5]
d.sched.answerCard(c, 2)
# should handle gracefully
- d.sched._cardConf(c)['new']['delays'] = [1]
+ d.sched._cardConf(c)["new"]["delays"] = [1]
d.sched.answerCard(c, 2)
+
def test_learn():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
f = d.addNote(f)
# set as a learn card and rebuild queues
d.db.execute("update cards set queue=0, type=0")
@@ -126,12 +136,12 @@ def test_learn():
# sched.getCard should return it, since it's due in the past
c = d.sched.getCard()
assert c
- d.sched._cardConf(c)['new']['delays'] = [0.5, 3, 10]
+ d.sched._cardConf(c)["new"]["delays"] = [0.5, 3, 10]
# fail it
d.sched.answerCard(c, 1)
# it should have three reps left to graduation
- assert c.left%1000 == 3
- assert c.left//1000 == 3
+ assert c.left % 1000 == 3
+ assert c.left // 1000 == 3
# it should by due in 30 seconds
t = round(c.due - time.time())
assert t >= 25 and t <= 40
@@ -139,8 +149,8 @@ def test_learn():
d.sched.answerCard(c, 2)
# it should by due in 3 minutes
assert round(c.due - time.time()) in (179, 180)
- assert c.left%1000 == 2
- assert c.left//1000 == 2
+ assert c.left % 1000 == 2
+ assert c.left // 1000 == 2
# check log is accurate
log = d.db.first("select * from revlog order by id desc")
assert log[3] == 2
@@ -150,8 +160,8 @@ def test_learn():
d.sched.answerCard(c, 2)
# it should by due in 10 minutes
assert round(c.due - time.time()) in (599, 600)
- assert c.left%1000 == 1
- assert c.left//1000 == 1
+ assert c.left % 1000 == 1
+ assert c.left // 1000 == 1
# the next pass should graduate the card
assert c.queue == 1
assert c.type == 1
@@ -159,7 +169,7 @@ def test_learn():
assert c.queue == 2
assert c.type == 2
# should be due tomorrow, with an interval of 1
- assert c.due == d.sched.today+1
+ assert c.due == d.sched.today + 1
assert c.ivl == 1
# or normal removal
c.type = 0
@@ -188,14 +198,15 @@ def test_learn():
assert c.queue == 2
assert c.due == 321
+
def test_learn_collapsed():
d = getEmptyCol()
# add 2 notes
f = d.newNote()
- f['Front'] = "1"
+ f["Front"] = "1"
f = d.addNote(f)
f = d.newNote()
- f['Front'] = "2"
+ f["Front"] = "2"
f = d.addNote(f)
# set as a learn card and rebuild queues
d.db.execute("update cards set queue=0, type=0")
@@ -214,27 +225,28 @@ def test_learn_collapsed():
c = d.sched.getCard()
assert not c.q().endswith("2")
+
def test_learn_day():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
f = d.addNote(f)
d.sched.reset()
c = d.sched.getCard()
- d.sched._cardConf(c)['new']['delays'] = [1, 10, 1440, 2880]
+ d.sched._cardConf(c)["new"]["delays"] = [1, 10, 1440, 2880]
# pass it
d.sched.answerCard(c, 2)
# two reps to graduate, 1 more today
- assert c.left%1000 == 3
- assert c.left//1000 == 1
+ assert c.left % 1000 == 3
+ assert c.left // 1000 == 1
assert d.sched.counts() == (0, 1, 0)
c = d.sched.getCard()
ni = d.sched.nextIvl
assert ni(c, 2) == 86400
# answering it will place it in queue 3
d.sched.answerCard(c, 2)
- assert c.due == d.sched.today+1
+ assert c.due == d.sched.today + 1
assert c.queue == 3
assert not d.sched.getCard()
# for testing, move it back a day
@@ -244,7 +256,7 @@ def test_learn_day():
assert d.sched.counts() == (0, 1, 0)
c = d.sched.getCard()
# nextIvl should work
- assert ni(c, 2) == 86400*2
+ assert ni(c, 2) == 86400 * 2
# if we fail it, it should be back in the correct queue
d.sched.answerCard(c, 1)
assert c.queue == 1
@@ -266,17 +278,19 @@ def test_learn_day():
c.flush()
d.reset()
assert d.sched.counts() == (0, 0, 1)
- d.sched._cardConf(c)['lapse']['delays'] = [1440]
+ d.sched._cardConf(c)["lapse"]["delays"] = [1440]
c = d.sched.getCard()
d.sched.answerCard(c, 1)
assert c.queue == 3
assert d.sched.counts() == (0, 0, 0)
+
def test_reviews():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
# set the card up as a review card, due 8 days ago
c = f.cards()[0]
@@ -295,7 +309,7 @@ def test_reviews():
##################################################
# different delay to new
d.reset()
- d.sched._cardConf(c)['lapse']['delays'] = [2, 20]
+ d.sched._cardConf(c)["lapse"]["delays"] = [2, 20]
d.sched.answerCard(c, 1)
assert c.queue == 1
# it should be due tomorrow, with an interval of 1
@@ -313,7 +327,7 @@ def test_reviews():
# check ests.
ni = d.sched.nextIvl
assert ni(c, 1) == 120
- assert ni(c, 2) == 20*60
+ assert ni(c, 2) == 20 * 60
# try again with an ease of 2 instead
##################################################
c = copy.copy(cardcopy)
@@ -355,8 +369,10 @@ def test_reviews():
c.flush()
# steup hook
hooked = []
+
def onLeech(card):
hooked.append(1)
+
addHook("leech", onLeech)
d.sched.answerCard(c, 1)
assert hooked
@@ -364,10 +380,11 @@ def test_reviews():
c.load()
assert c.queue == -1
+
def test_button_spacing():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# 1 day ivl review card due now
c = f.cards()[0]
@@ -384,13 +401,14 @@ def test_button_spacing():
assert ni(c, 3) == "3 days"
assert ni(c, 4) == "4 days"
+
def test_overdue_lapse():
# disabled in commit 3069729776990980f34c25be66410e947e9d51a2
return
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# simulate a review that was lapsed and is now due for its normal review
c = f.cards()[0]
@@ -419,13 +437,15 @@ def test_overdue_lapse():
d.sched.reset()
assert d.sched.counts() == (0, 0, 1)
+
def test_finished():
d = getEmptyCol()
# nothing due
assert "Congratulations" in d.sched.finishedMsg()
assert "limit" not in d.sched.finishedMsg()
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
# have a new card
assert "new cards available" in d.sched.finishedMsg()
@@ -438,44 +458,46 @@ def test_finished():
assert "Congratulations" in d.sched.finishedMsg()
assert "limit" not in d.sched.finishedMsg()
+
def test_nextIvl():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
d.reset()
conf = d.decks.confForDid(1)
- conf['new']['delays'] = [0.5, 3, 10]
- conf['lapse']['delays'] = [1, 5, 9]
+ conf["new"]["delays"] = [0.5, 3, 10]
+ conf["lapse"]["delays"] = [1, 5, 9]
c = d.sched.getCard()
# new cards
##################################################
ni = d.sched.nextIvl
assert ni(c, 1) == 30
assert ni(c, 2) == 180
- assert ni(c, 3) == 4*86400
+ assert ni(c, 3) == 4 * 86400
d.sched.answerCard(c, 1)
# cards in learning
##################################################
assert ni(c, 1) == 30
assert ni(c, 2) == 180
- assert ni(c, 3) == 4*86400
+ assert ni(c, 3) == 4 * 86400
d.sched.answerCard(c, 2)
assert ni(c, 1) == 30
assert ni(c, 2) == 600
- assert ni(c, 3) == 4*86400
+ assert ni(c, 3) == 4 * 86400
d.sched.answerCard(c, 2)
# normal graduation is tomorrow
- assert ni(c, 2) == 1*86400
- assert ni(c, 3) == 4*86400
+ assert ni(c, 2) == 1 * 86400
+ assert ni(c, 3) == 4 * 86400
# lapsed cards
##################################################
c.type = 2
c.ivl = 100
c.factor = STARTING_FACTOR
assert ni(c, 1) == 60
- assert ni(c, 2) == 100*86400
- assert ni(c, 3) == 100*86400
+ assert ni(c, 2) == 100 * 86400
+ assert ni(c, 3) == 100 * 86400
# review cards
##################################################
c.queue = 2
@@ -484,8 +506,8 @@ def test_nextIvl():
# failing it should put it at 60s
assert ni(c, 1) == 60
# or 1 day if relearn is false
- d.sched._cardConf(c)['lapse']['delays']=[]
- assert ni(c, 1) == 1*86400
+ d.sched._cardConf(c)["lapse"]["delays"] = []
+ assert ni(c, 1) == 1 * 86400
# (* 100 1.2 86400)10368000.0
assert ni(c, 2) == 10368000
# (* 100 2.5 86400)21600000.0
@@ -494,10 +516,11 @@ def test_nextIvl():
assert ni(c, 4) == 28080000
assert d.sched.nextIvlStr(c, 4) == "10.8 months"
+
def test_misc():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
# burying
@@ -508,10 +531,11 @@ def test_misc():
d.reset()
assert d.sched.getCard()
+
def test_suspend():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
# suspending
@@ -525,7 +549,11 @@ def test_suspend():
d.reset()
assert d.sched.getCard()
# should cope with rev cards being relearnt
- c.due = 0; c.ivl = 100; c.type = 2; c.queue = 2; c.flush()
+ c.due = 0
+ c.ivl = 100
+ c.type = 2
+ c.queue = 2
+ c.flush()
d.reset()
c = d.sched.getCard()
d.sched.answerCard(c, 1)
@@ -551,10 +579,11 @@ def test_suspend():
assert c.due == 1
assert c.did == 1
+
def test_cram():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
c.ivl = 100
@@ -566,7 +595,7 @@ def test_cram():
c.startTimer()
c.flush()
d.reset()
- assert d.sched.counts() == (0,0,0)
+ assert d.sched.counts() == (0, 0, 0)
cardcopy = copy.copy(c)
# create a dynamic deck and refresh it
did = d.decks.newDyn("Cram")
@@ -575,18 +604,18 @@ def test_cram():
# should appear as new in the deck list
assert sorted(d.sched.deckDueList())[0][4] == 1
# and should appear in the counts
- assert d.sched.counts() == (1,0,0)
+ assert d.sched.counts() == (1, 0, 0)
# grab it and check estimates
c = d.sched.getCard()
assert d.sched.answerButtons(c) == 2
assert d.sched.nextIvl(c, 1) == 600
- assert d.sched.nextIvl(c, 2) == 138*60*60*24
+ assert d.sched.nextIvl(c, 2) == 138 * 60 * 60 * 24
cram = d.decks.get(did)
- cram['delays'] = [1, 10]
+ cram["delays"] = [1, 10]
assert d.sched.answerButtons(c) == 3
assert d.sched.nextIvl(c, 1) == 60
assert d.sched.nextIvl(c, 2) == 600
- assert d.sched.nextIvl(c, 3) == 138*60*60*24
+ assert d.sched.nextIvl(c, 3) == 138 * 60 * 60 * 24
d.sched.answerCard(c, 2)
# elapsed time was 75 days
# factor = 2.5+1.2/2 = 1.85
@@ -595,12 +624,11 @@ def test_cram():
assert c.odue == 138
assert c.queue == 1
# should be logged as a cram rep
- assert d.db.scalar(
- "select type from revlog order by id desc limit 1") == 3
+ assert d.db.scalar("select type from revlog order by id desc limit 1") == 3
# check ivls again
assert d.sched.nextIvl(c, 1) == 60
- assert d.sched.nextIvl(c, 2) == 138*60*60*24
- assert d.sched.nextIvl(c, 3) == 138*60*60*24
+ assert d.sched.nextIvl(c, 2) == 138 * 60 * 60 * 24
+ assert d.sched.nextIvl(c, 3) == 138 * 60 * 60 * 24
# when it graduates, due is updated
c = d.sched.getCard()
d.sched.answerCard(c, 2)
@@ -616,7 +644,7 @@ def test_cram():
# check ivls again - passing should be idempotent
assert d.sched.nextIvl(c, 1) == 60
assert d.sched.nextIvl(c, 2) == 600
- assert d.sched.nextIvl(c, 3) == 138*60*60*24
+ assert d.sched.nextIvl(c, 3) == 138 * 60 * 60 * 24
d.sched.answerCard(c, 2)
assert c.ivl == 138
assert c.odue == 138
@@ -630,20 +658,20 @@ def test_cram():
assert len(d.sched.deckDueList()) == 1
c.load()
assert c.ivl == 1
- assert c.due == d.sched.today+1
+ assert c.due == d.sched.today + 1
# make it due
d.reset()
- assert d.sched.counts() == (0,0,0)
+ assert d.sched.counts() == (0, 0, 0)
c.due = -5
c.ivl = 100
c.flush()
d.reset()
- assert d.sched.counts() == (0,0,1)
+ assert d.sched.counts() == (0, 0, 1)
# cram again
did = d.decks.newDyn("Cram")
d.sched.rebuildDyn(did)
d.reset()
- assert d.sched.counts() == (0,0,1)
+ assert d.sched.counts() == (0, 0, 1)
c.load()
assert d.sched.answerButtons(c) == 4
# add a sibling so we can test minSpace, etc
@@ -661,10 +689,11 @@ def test_cram():
# it should have been moved back to the original deck
assert c.did == 1
+
def test_cram_rem():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
oldDue = f.cards()[0].due
did = d.decks.newDyn("Cram")
@@ -681,16 +710,17 @@ def test_cram_rem():
assert c.type == c.queue == 0
assert c.due == oldDue
+
def test_cram_resched():
# add card
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# cram deck
did = d.decks.newDyn("Cram")
cram = d.decks.get(did)
- cram['resched'] = False
+ cram["resched"] = False
d.sched.rebuildDyn(did)
d.reset()
# graduate should return it to new
@@ -786,22 +816,25 @@ def test_cram_resched():
# d.sched.answerCard(c, 2)
# print c.__dict__
+
def test_ordcycle():
d = getEmptyCol()
# add two more templates and set second active
- m = d.models.current(); mm = d.models
+ m = d.models.current()
+ mm = d.models
t = mm.newTemplate("Reverse")
- t['qfmt'] = "{{Back}}"
- t['afmt'] = "{{Front}}"
+ t["qfmt"] = "{{Back}}"
+ t["afmt"] = "{{Front}}"
mm.addTemplate(m, t)
t = mm.newTemplate("f2")
- t['qfmt'] = "{{Front}}"
- t['afmt'] = "{{Back}}"
+ t["qfmt"] = "{{Front}}"
+ t["afmt"] = "{{Back}}"
mm.addTemplate(m, t)
mm.save(m)
# create a new note; it should have 3 cards
f = d.newNote()
- f['Front'] = "1"; f['Back'] = "1"
+ f["Front"] = "1"
+ f["Back"] = "1"
d.addNote(f)
assert d.cardCount() == 3
d.reset()
@@ -810,10 +843,12 @@ def test_ordcycle():
assert d.sched.getCard().ord == 1
assert d.sched.getCard().ord == 2
+
def test_counts_idx():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
d.reset()
assert d.sched.counts() == (1, 0, 0)
@@ -832,10 +867,11 @@ def test_counts_idx():
d.sched.answerCard(c, 1)
assert d.sched.counts() == (0, 2, 0)
+
def test_repCounts():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
d.reset()
# lrnReps should be accurate on pass/fail
@@ -853,7 +889,7 @@ def test_repCounts():
d.sched.answerCard(d.sched.getCard(), 2)
assert d.sched.counts() == (0, 0, 0)
f = d.newNote()
- f['Front'] = "two"
+ f["Front"] = "two"
d.addNote(f)
d.reset()
# initial pass should be correct too
@@ -865,14 +901,14 @@ def test_repCounts():
assert d.sched.counts() == (0, 0, 0)
# immediate graduate should work
f = d.newNote()
- f['Front'] = "three"
+ f["Front"] = "three"
d.addNote(f)
d.reset()
d.sched.answerCard(d.sched.getCard(), 3)
assert d.sched.counts() == (0, 0, 0)
# and failing a review should too
f = d.newNote()
- f['Front'] = "three"
+ f["Front"] = "three"
d.addNote(f)
c = f.cards()[0]
c.type = 2
@@ -884,12 +920,13 @@ def test_repCounts():
d.sched.answerCard(d.sched.getCard(), 1)
assert d.sched.counts() == (0, 1, 0)
+
def test_timing():
d = getEmptyCol()
# add a few review cards, due today
for i in range(5):
f = d.newNote()
- f['Front'] = "num"+str(i)
+ f["Front"] = "num" + str(i)
d.addNote(f)
c = f.cards()[0]
c.type = 2
@@ -900,7 +937,7 @@ def test_timing():
d.reset()
c = d.sched.getCard()
# set a a fail delay of 1 second so we don't have to wait
- d.sched._cardConf(c)['lapse']['delays'][0] = 1/60.0
+ d.sched._cardConf(c)["lapse"]["delays"][0] = 1 / 60.0
d.sched.answerCard(c, 1)
# the next card should be another review
c = d.sched.getCard()
@@ -910,11 +947,12 @@ def test_timing():
c = d.sched.getCard()
assert c.queue == 1
+
def test_collapse():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
d.reset()
# test collapsing
@@ -924,16 +962,17 @@ def test_collapse():
d.sched.answerCard(c, 3)
assert not d.sched.getCard()
+
def test_deckDue():
d = getEmptyCol()
# add a note with default deck
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# and one that's a child
f = d.newNote()
- f['Front'] = "two"
- default1 = f.model()['did'] = d.decks.id("Default::1")
+ f["Front"] = "two"
+ default1 = f.model()["did"] = d.decks.id("Default::1")
d.addNote(f)
# make it a review card
c = f.cards()[0]
@@ -942,13 +981,13 @@ def test_deckDue():
c.flush()
# add one more with a new deck
f = d.newNote()
- f['Front'] = "two"
- foobar = f.model()['did'] = d.decks.id("foo::bar")
+ f["Front"] = "two"
+ foobar = f.model()["did"] = d.decks.id("foo::bar")
d.addNote(f)
# and one that's a sibling
f = d.newNote()
- f['Front'] = "three"
- foobaz = f.model()['did'] = d.decks.id("foo::baz")
+ f["Front"] = "three"
+ foobaz = f.model()["did"] = d.decks.id("foo::baz")
d.addNote(f)
d.reset()
assert len(d.decks.decks) == 5
@@ -970,10 +1009,12 @@ def test_deckDue():
assert tree[0][5][0][2] == 1
assert tree[0][5][0][4] == 0
# code should not fail if a card has an invalid deck
- c.did = 12345; c.flush()
+ c.did = 12345
+ c.flush()
d.sched.deckDueList()
d.sched.deckDueTree()
+
def test_deckTree():
d = getEmptyCol()
d.decks.id("new::b::c")
@@ -983,75 +1024,80 @@ def test_deckTree():
names.remove("new")
assert "new" not in names
+
def test_deckFlow():
d = getEmptyCol()
# add a note with default deck
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# and one that's a child
f = d.newNote()
- f['Front'] = "two"
- default1 = f.model()['did'] = d.decks.id("Default::2")
+ f["Front"] = "two"
+ default1 = f.model()["did"] = d.decks.id("Default::2")
d.addNote(f)
# and another that's higher up
f = d.newNote()
- f['Front'] = "three"
- default1 = f.model()['did'] = d.decks.id("Default::1")
+ f["Front"] = "three"
+ default1 = f.model()["did"] = d.decks.id("Default::1")
d.addNote(f)
# should get top level one first, then ::1, then ::2
d.reset()
- assert d.sched.counts() == (3,0,0)
+ assert d.sched.counts() == (3, 0, 0)
for i in "one", "three", "two":
c = d.sched.getCard()
- assert c.note()['Front'] == i
+ assert c.note()["Front"] == i
d.sched.answerCard(c, 2)
+
def test_reorder():
d = getEmptyCol()
# add a note with default deck
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
f2 = d.newNote()
- f2['Front'] = "two"
+ f2["Front"] = "two"
d.addNote(f2)
assert f2.cards()[0].due == 2
- found=False
+ found = False
# 50/50 chance of being reordered
for i in range(20):
d.sched.randomizeCards(1)
if f.cards()[0].due != f.id:
- found=True
+ found = True
break
assert found
d.sched.orderCards(1)
assert f.cards()[0].due == 1
# shifting
f3 = d.newNote()
- f3['Front'] = "three"
+ f3["Front"] = "three"
d.addNote(f3)
f4 = d.newNote()
- f4['Front'] = "four"
+ f4["Front"] = "four"
d.addNote(f4)
assert f.cards()[0].due == 1
assert f2.cards()[0].due == 2
assert f3.cards()[0].due == 3
assert f4.cards()[0].due == 4
- d.sched.sortCards([
- f3.cards()[0].id, f4.cards()[0].id], start=1, shift=True)
+ d.sched.sortCards([f3.cards()[0].id, f4.cards()[0].id], start=1, shift=True)
assert f.cards()[0].due == 3
assert f2.cards()[0].due == 4
assert f3.cards()[0].due == 1
assert f4.cards()[0].due == 2
+
def test_forget():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
- c.queue = 2; c.type = 2; c.ivl = 100; c.due = 0
+ c.queue = 2
+ c.type = 2
+ c.ivl = 100
+ c.due = 0
c.flush()
d.reset()
assert d.sched.counts() == (0, 0, 1)
@@ -1059,10 +1105,11 @@ def test_forget():
d.reset()
assert d.sched.counts() == (1, 0, 0)
+
def test_resched():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
d.sched.reschedCards([c.id], 0, 0)
@@ -1072,14 +1119,15 @@ def test_resched():
assert c.queue == c.type == 2
d.sched.reschedCards([c.id], 1, 1)
c.load()
- assert c.due == d.sched.today+1
+ assert c.due == d.sched.today + 1
assert c.ivl == +1
+
def test_norelearn():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
c.type = 2
@@ -1093,13 +1141,15 @@ def test_norelearn():
c.flush()
d.reset()
d.sched.answerCard(c, 1)
- d.sched._cardConf(c)['lapse']['delays'] = []
+ d.sched._cardConf(c)["lapse"]["delays"] = []
d.sched.answerCard(c, 1)
+
def test_failmult():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
c = f.cards()[0]
c.type = 2
@@ -1111,7 +1161,7 @@ def test_failmult():
c.lapses = 1
c.startTimer()
c.flush()
- d.sched._cardConf(c)['lapse']['mult'] = 0.5
+ d.sched._cardConf(c)["lapse"]["mult"] = 0.5
c = d.sched.getCard()
d.sched.answerCard(c, 1)
assert c.ivl == 50
diff --git a/tests/test_schedv2.py b/tests/test_schedv2.py
index f3825027b..a1eb012cd 100644
--- a/tests/test_schedv2.py
+++ b/tests/test_schedv2.py
@@ -5,38 +5,45 @@ import copy
from anki.consts import STARTING_FACTOR
from tests.shared import getEmptyCol
-from anki.utils import intTime
+from anki.utils import intTime
from anki.hooks import addHook
# Between 2-4AM, shift the time back so test assumptions hold.
lt = time.localtime()
if lt.tm_hour > 2 and lt.tm_hour < 4:
orig_time = time.time
+
def adjusted_time():
- return orig_time() - 60*60*2
+ return orig_time() - 60 * 60 * 2
+
time.time = adjusted_time
+
def test_clock():
d = getEmptyCol()
- if (d.sched.dayCutoff - intTime()) < 10*60:
+ if (d.sched.dayCutoff - intTime()) < 10 * 60:
raise Exception("Unit tests will fail around the day rollover.")
+
def checkRevIvl(d, c, targetIvl):
min, max = d.sched._fuzzIvlRange(targetIvl)
return min <= c.ivl <= max
+
def test_basics():
d = getEmptyCol()
d.reset()
assert not d.sched.getCard()
+
def test_new():
d = getEmptyCol()
d.reset()
assert d.sched.newCount == 0
# add a note
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
d.reset()
assert d.sched.newCount == 1
@@ -74,15 +81,16 @@ def test_new():
# assert qs[n] in c.q()
# d.sched.answerCard(c, 2)
+
def test_newLimits():
d = getEmptyCol()
# add some notes
g2 = d.decks.id("Default::foo")
for i in range(30):
f = d.newNote()
- f['Front'] = str(i)
+ f["Front"] = str(i)
if i > 4:
- f.model()['did'] = g2
+ f.model()["did"] = g2
d.addNote(f)
# give the child deck a different configuration
c2 = d.decks.confId("new conf")
@@ -95,33 +103,36 @@ def test_newLimits():
assert c.did == 1
# limit the parent to 10 cards, meaning we get 10 in total
conf1 = d.decks.confForDid(1)
- conf1['new']['perDay'] = 10
+ conf1["new"]["perDay"] = 10
d.reset()
assert d.sched.newCount == 10
# if we limit child to 4, we should get 9
conf2 = d.decks.confForDid(g2)
- conf2['new']['perDay'] = 4
+ conf2["new"]["perDay"] = 4
d.reset()
assert d.sched.newCount == 9
+
def test_newBoxes():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
d.reset()
c = d.sched.getCard()
- d.sched._cardConf(c)['new']['delays'] = [1,2,3,4,5]
+ d.sched._cardConf(c)["new"]["delays"] = [1, 2, 3, 4, 5]
d.sched.answerCard(c, 2)
# should handle gracefully
- d.sched._cardConf(c)['new']['delays'] = [1]
+ d.sched._cardConf(c)["new"]["delays"] = [1]
d.sched.answerCard(c, 2)
+
def test_learn():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
f = d.addNote(f)
# set as a learn card and rebuild queues
d.db.execute("update cards set queue=0, type=0")
@@ -129,12 +140,12 @@ def test_learn():
# sched.getCard should return it, since it's due in the past
c = d.sched.getCard()
assert c
- d.sched._cardConf(c)['new']['delays'] = [0.5, 3, 10]
+ d.sched._cardConf(c)["new"]["delays"] = [0.5, 3, 10]
# fail it
d.sched.answerCard(c, 1)
# it should have three reps left to graduation
- assert c.left%1000 == 3
- assert c.left//1000 == 3
+ assert c.left % 1000 == 3
+ assert c.left // 1000 == 3
# it should by due in 30 seconds
t = round(c.due - time.time())
assert t >= 25 and t <= 40
@@ -142,9 +153,9 @@ def test_learn():
d.sched.answerCard(c, 3)
# it should by due in 3 minutes
dueIn = c.due - time.time()
- assert 179 <= dueIn <= 180*1.25
- assert c.left%1000 == 2
- assert c.left//1000 == 2
+ assert 179 <= dueIn <= 180 * 1.25
+ assert c.left % 1000 == 2
+ assert c.left // 1000 == 2
# check log is accurate
log = d.db.first("select * from revlog order by id desc")
assert log[3] == 3
@@ -154,9 +165,9 @@ def test_learn():
d.sched.answerCard(c, 3)
# it should by due in 10 minutes
dueIn = c.due - time.time()
- assert 599 <= dueIn <= 600*1.25
- assert c.left%1000 == 1
- assert c.left//1000 == 1
+ assert 599 <= dueIn <= 600 * 1.25
+ assert c.left % 1000 == 1
+ assert c.left // 1000 == 1
# the next pass should graduate the card
assert c.queue == 1
assert c.type == 1
@@ -164,7 +175,7 @@ def test_learn():
assert c.queue == 2
assert c.type == 2
# should be due tomorrow, with an interval of 1
- assert c.due == d.sched.today+1
+ assert c.due == d.sched.today + 1
assert c.ivl == 1
# or normal removal
c.type = 0
@@ -176,10 +187,11 @@ def test_learn():
# revlog should have been updated each time
assert d.db.scalar("select count() from revlog where type = 0") == 5
+
def test_relearn():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
c.ivl = 100
@@ -201,10 +213,11 @@ def test_relearn():
assert c.ivl == 2
assert c.due == d.sched.today + c.ivl
+
def test_relearn_no_steps():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
c.ivl = 100
@@ -213,7 +226,7 @@ def test_relearn_no_steps():
c.flush()
conf = d.decks.confForDid(1)
- conf['lapse']['delays'] = []
+ conf["lapse"]["delays"] = []
d.decks.save(conf)
# fail the card
@@ -222,14 +235,15 @@ def test_relearn_no_steps():
d.sched.answerCard(c, 1)
assert c.type == c.queue == 2
+
def test_learn_collapsed():
d = getEmptyCol()
# add 2 notes
f = d.newNote()
- f['Front'] = "1"
+ f["Front"] = "1"
f = d.addNote(f)
f = d.newNote()
- f['Front'] = "2"
+ f["Front"] = "2"
f = d.addNote(f)
# set as a learn card and rebuild queues
d.db.execute("update cards set queue=0, type=0")
@@ -248,27 +262,28 @@ def test_learn_collapsed():
c = d.sched.getCard()
assert not c.q().endswith("2")
+
def test_learn_day():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
f = d.addNote(f)
d.sched.reset()
c = d.sched.getCard()
- d.sched._cardConf(c)['new']['delays'] = [1, 10, 1440, 2880]
+ d.sched._cardConf(c)["new"]["delays"] = [1, 10, 1440, 2880]
# pass it
d.sched.answerCard(c, 3)
# two reps to graduate, 1 more today
- assert c.left%1000 == 3
- assert c.left//1000 == 1
+ assert c.left % 1000 == 3
+ assert c.left // 1000 == 1
assert d.sched.counts() == (0, 1, 0)
c = d.sched.getCard()
ni = d.sched.nextIvl
assert ni(c, 3) == 86400
# answering it will place it in queue 3
d.sched.answerCard(c, 3)
- assert c.due == d.sched.today+1
+ assert c.due == d.sched.today + 1
assert c.queue == 3
assert not d.sched.getCard()
# for testing, move it back a day
@@ -278,7 +293,7 @@ def test_learn_day():
assert d.sched.counts() == (0, 1, 0)
c = d.sched.getCard()
# nextIvl should work
- assert ni(c, 3) == 86400*2
+ assert ni(c, 3) == 86400 * 2
# if we fail it, it should be back in the correct queue
d.sched.answerCard(c, 1)
assert c.queue == 1
@@ -300,17 +315,19 @@ def test_learn_day():
c.flush()
d.reset()
assert d.sched.counts() == (0, 0, 1)
- d.sched._cardConf(c)['lapse']['delays'] = [1440]
+ d.sched._cardConf(c)["lapse"]["delays"] = [1440]
c = d.sched.getCard()
d.sched.answerCard(c, 1)
assert c.queue == 3
assert d.sched.counts() == (0, 0, 0)
+
def test_reviews():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
# set the card up as a review card, due 8 days ago
c = f.cards()[0]
@@ -367,8 +384,10 @@ def test_reviews():
c.flush()
# steup hook
hooked = []
+
def onLeech(card):
hooked.append(1)
+
addHook("leech", onLeech)
d.sched.answerCard(c, 1)
assert hooked
@@ -376,6 +395,7 @@ def test_reviews():
c.load()
assert c.queue == -1
+
def test_review_limits():
d = getEmptyCol()
@@ -385,21 +405,22 @@ def test_review_limits():
pconf = d.decks.getConf(d.decks.confId("parentConf"))
cconf = d.decks.getConf(d.decks.confId("childConf"))
- pconf['rev']['perDay'] = 5
+ pconf["rev"]["perDay"] = 5
d.decks.updateConf(pconf)
- d.decks.setConf(parent, pconf['id'])
- cconf['rev']['perDay'] = 10
+ d.decks.setConf(parent, pconf["id"])
+ cconf["rev"]["perDay"] = 10
d.decks.updateConf(cconf)
- d.decks.setConf(child, cconf['id'])
+ d.decks.setConf(child, cconf["id"])
m = d.models.current()
- m['did'] = child['id']
+ m["did"] = child["id"]
d.models.save(m, updateReqs=False)
# add some cards
for i in range(20):
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
# make them reviews
@@ -410,11 +431,11 @@ def test_review_limits():
tree = d.sched.deckDueTree()
# (('Default', 1, 0, 0, 0, ()), ('parent', 1514457677462, 5, 0, 0, (('child', 1514457677463, 5, 0, 0, ()),)))
- assert tree[1][2] == 5 # parent
- assert tree[1][5][0][2] == 5 # child
+ assert tree[1][2] == 5 # parent
+ assert tree[1][5][0][2] == 5 # child
# .counts() should match
- d.decks.select(child['id'])
+ d.decks.select(child["id"])
d.sched.reset()
assert d.sched.counts() == (0, 0, 5)
@@ -424,24 +445,25 @@ def test_review_limits():
assert d.sched.counts() == (0, 0, 4)
tree = d.sched.deckDueTree()
- assert tree[1][2] == 4 # parent
- assert tree[1][5][0][2] == 4 # child
+ assert tree[1][2] == 4 # parent
+ assert tree[1][5][0][2] == 4 # child
# switch limits
- d.decks.setConf(parent, cconf['id'])
- d.decks.setConf(child, pconf['id'])
- d.decks.select(parent['id'])
+ d.decks.setConf(parent, cconf["id"])
+ d.decks.setConf(child, pconf["id"])
+ d.decks.select(parent["id"])
d.sched.reset()
# child limits do not affect the parent
tree = d.sched.deckDueTree()
- assert tree[1][2] == 9 # parent
- assert tree[1][5][0][2] == 4 # child
+ assert tree[1][2] == 9 # parent
+ assert tree[1][5][0][2] == 4 # child
+
def test_button_spacing():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# 1 day ivl review card due now
c = f.cards()[0]
@@ -460,16 +482,17 @@ def test_button_spacing():
# if hard factor is <= 1, then hard may not increase
conf = d.decks.confForDid(1)
- conf['rev']['hardFactor'] = 1
+ conf["rev"]["hardFactor"] = 1
assert ni(c, 2) == "1 day"
+
def test_overdue_lapse():
# disabled in commit 3069729776990980f34c25be66410e947e9d51a2
return
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# simulate a review that was lapsed and is now due for its normal review
c = f.cards()[0]
@@ -498,13 +521,15 @@ def test_overdue_lapse():
d.sched.reset()
assert d.sched.counts() == (0, 0, 1)
+
def test_finished():
d = getEmptyCol()
# nothing due
assert "Congratulations" in d.sched.finishedMsg()
assert "limit" not in d.sched.finishedMsg()
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
# have a new card
assert "new cards available" in d.sched.finishedMsg()
@@ -517,47 +542,49 @@ def test_finished():
assert "Congratulations" in d.sched.finishedMsg()
assert "limit" not in d.sched.finishedMsg()
+
def test_nextIvl():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
d.reset()
conf = d.decks.confForDid(1)
- conf['new']['delays'] = [0.5, 3, 10]
- conf['lapse']['delays'] = [1, 5, 9]
+ conf["new"]["delays"] = [0.5, 3, 10]
+ conf["lapse"]["delays"] = [1, 5, 9]
c = d.sched.getCard()
# new cards
##################################################
ni = d.sched.nextIvl
assert ni(c, 1) == 30
- assert ni(c, 2) == (30+180)//2
+ assert ni(c, 2) == (30 + 180) // 2
assert ni(c, 3) == 180
- assert ni(c, 4) == 4*86400
+ assert ni(c, 4) == 4 * 86400
d.sched.answerCard(c, 1)
# cards in learning
##################################################
assert ni(c, 1) == 30
- assert ni(c, 2) == (30+180)//2
+ assert ni(c, 2) == (30 + 180) // 2
assert ni(c, 3) == 180
- assert ni(c, 4) == 4*86400
+ assert ni(c, 4) == 4 * 86400
d.sched.answerCard(c, 3)
assert ni(c, 1) == 30
- assert ni(c, 2) == (180+600)//2
+ assert ni(c, 2) == (180 + 600) // 2
assert ni(c, 3) == 600
- assert ni(c, 4) == 4*86400
+ assert ni(c, 4) == 4 * 86400
d.sched.answerCard(c, 3)
# normal graduation is tomorrow
- assert ni(c, 3) == 1*86400
- assert ni(c, 4) == 4*86400
+ assert ni(c, 3) == 1 * 86400
+ assert ni(c, 4) == 4 * 86400
# lapsed cards
##################################################
c.type = 2
c.ivl = 100
c.factor = STARTING_FACTOR
assert ni(c, 1) == 60
- assert ni(c, 3) == 100*86400
- assert ni(c, 4) == 101*86400
+ assert ni(c, 3) == 100 * 86400
+ assert ni(c, 4) == 101 * 86400
# review cards
##################################################
c.queue = 2
@@ -566,8 +593,8 @@ def test_nextIvl():
# failing it should put it at 60s
assert ni(c, 1) == 60
# or 1 day if relearn is false
- d.sched._cardConf(c)['lapse']['delays']=[]
- assert ni(c, 1) == 1*86400
+ d.sched._cardConf(c)["lapse"]["delays"] = []
+ assert ni(c, 1) == 1 * 86400
# (* 100 1.2 86400)10368000.0
assert ni(c, 2) == 10368000
# (* 100 2.5 86400)21600000.0
@@ -576,14 +603,15 @@ def test_nextIvl():
assert ni(c, 4) == 28080000
assert d.sched.nextIvlStr(c, 4) == "10.8 months"
+
def test_bury():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
f = d.newNote()
- f['Front'] = "two"
+ f["Front"] = "two"
d.addNote(f)
c2 = f.cards()[0]
# burying
@@ -598,11 +626,14 @@ def test_bury():
assert not d.sched.getCard()
d.sched.unburyCardsForDeck(type="manual")
- c.load(); assert c.queue == 0
- c2.load(); assert c2.queue == -2
+ c.load()
+ assert c.queue == 0
+ c2.load()
+ assert c2.queue == -2
d.sched.unburyCardsForDeck(type="siblings")
- c2.load(); assert c2.queue == 0
+ c2.load()
+ assert c2.queue == 0
d.sched.buryCards([c.id, c2.id])
d.sched.unburyCardsForDeck(type="all")
@@ -611,10 +642,11 @@ def test_bury():
assert d.sched.counts() == (2, 0, 0)
+
def test_suspend():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
# suspending
@@ -628,7 +660,11 @@ def test_suspend():
d.reset()
assert d.sched.getCard()
# should cope with rev cards being relearnt
- c.due = 0; c.ivl = 100; c.type = 2; c.queue = 2; c.flush()
+ c.due = 0
+ c.ivl = 100
+ c.type = 2
+ c.queue = 2
+ c.flush()
d.reset()
c = d.sched.getCard()
d.sched.answerCard(c, 1)
@@ -656,10 +692,11 @@ def test_suspend():
assert c.did != 1
assert c.odue == 1
+
def test_filt_reviewing_early_normal():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
c.ivl = 100
@@ -671,7 +708,7 @@ def test_filt_reviewing_early_normal():
c.startTimer()
c.flush()
d.reset()
- assert d.sched.counts() == (0,0,0)
+ assert d.sched.counts() == (0, 0, 0)
# create a dynamic deck and refresh it
did = d.decks.newDyn("Cram")
d.sched.rebuildDyn(did)
@@ -679,14 +716,14 @@ def test_filt_reviewing_early_normal():
# should appear as normal in the deck list
assert sorted(d.sched.deckDueList())[0][2] == 1
# and should appear in the counts
- assert d.sched.counts() == (0,0,1)
+ assert d.sched.counts() == (0, 0, 1)
# grab it and check estimates
c = d.sched.getCard()
assert d.sched.answerButtons(c) == 4
assert d.sched.nextIvl(c, 1) == 600
- assert d.sched.nextIvl(c, 2) == int(75*1.2)*86400
- assert d.sched.nextIvl(c, 3) == int(75*2.5)*86400
- assert d.sched.nextIvl(c, 4) == int(75*2.5*1.15)*86400
+ assert d.sched.nextIvl(c, 2) == int(75 * 1.2) * 86400
+ assert d.sched.nextIvl(c, 3) == int(75 * 2.5) * 86400
+ assert d.sched.nextIvl(c, 4) == int(75 * 2.5 * 1.15) * 86400
# answer 'good'
d.sched.answerCard(c, 3)
@@ -696,8 +733,7 @@ def test_filt_reviewing_early_normal():
# should not be in learning
assert c.queue == 2
# should be logged as a cram rep
- assert d.db.scalar(
- "select type from revlog order by id desc limit 1") == 3
+ assert d.db.scalar("select type from revlog order by id desc limit 1") == 3
# due in 75 days, so it's been waiting 25 days
c.ivl = 100
@@ -707,20 +743,21 @@ def test_filt_reviewing_early_normal():
d.reset()
c = d.sched.getCard()
- assert d.sched.nextIvl(c, 2) == 60*86400
- assert d.sched.nextIvl(c, 3) == 100*86400
- assert d.sched.nextIvl(c, 4) == 114*86400
+ assert d.sched.nextIvl(c, 2) == 60 * 86400
+ assert d.sched.nextIvl(c, 3) == 100 * 86400
+ assert d.sched.nextIvl(c, 4) == 114 * 86400
+
def test_filt_keep_lrn_state():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# fail the card outside filtered deck
c = d.sched.getCard()
- d.sched._cardConf(c)['new']['delays'] = [1, 10, 61]
+ d.sched._cardConf(c)["new"]["delays"] = [1, 10, 61]
d.decks.save()
d.sched.answerCard(c, 1)
@@ -744,30 +781,31 @@ def test_filt_keep_lrn_state():
# should be able to advance learning steps
d.sched.answerCard(c, 3)
# should be due at least an hour in the future
- assert c.due - intTime() > 60*60
+ assert c.due - intTime() > 60 * 60
# emptying the deck preserves learning state
d.sched.emptyDyn(did)
c.load()
assert c.type == c.queue == 1
assert c.left == 1001
- assert c.due - intTime() > 60*60
+ assert c.due - intTime() > 60 * 60
+
def test_preview():
# add cards
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
orig = copy.copy(c)
f2 = d.newNote()
- f2['Front'] = "two"
+ f2["Front"] = "two"
d.addNote(f2)
# cram deck
did = d.decks.newDyn("Cram")
cram = d.decks.get(did)
- cram['resched'] = False
+ cram["resched"] = False
d.sched.rebuildDyn(did)
d.reset()
# grab the first card
@@ -801,22 +839,25 @@ def test_preview():
assert c.reps == 0
assert c.type == 0
+
def test_ordcycle():
d = getEmptyCol()
# add two more templates and set second active
- m = d.models.current(); mm = d.models
+ m = d.models.current()
+ mm = d.models
t = mm.newTemplate("Reverse")
- t['qfmt'] = "{{Back}}"
- t['afmt'] = "{{Front}}"
+ t["qfmt"] = "{{Back}}"
+ t["afmt"] = "{{Front}}"
mm.addTemplate(m, t)
t = mm.newTemplate("f2")
- t['qfmt'] = "{{Front}}"
- t['afmt'] = "{{Back}}"
+ t["qfmt"] = "{{Front}}"
+ t["afmt"] = "{{Back}}"
mm.addTemplate(m, t)
mm.save(m)
# create a new note; it should have 3 cards
f = d.newNote()
- f['Front'] = "1"; f['Back'] = "1"
+ f["Front"] = "1"
+ f["Back"] = "1"
d.addNote(f)
assert d.cardCount() == 3
d.reset()
@@ -825,10 +866,12 @@ def test_ordcycle():
assert d.sched.getCard().ord == 1
assert d.sched.getCard().ord == 2
+
def test_counts_idx():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
d.reset()
assert d.sched.counts() == (1, 0, 0)
@@ -847,10 +890,11 @@ def test_counts_idx():
d.sched.answerCard(c, 1)
assert d.sched.counts() == (0, 1, 0)
+
def test_repCounts():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
d.reset()
# lrnReps should be accurate on pass/fail
@@ -868,7 +912,7 @@ def test_repCounts():
d.sched.answerCard(d.sched.getCard(), 3)
assert d.sched.counts() == (0, 0, 0)
f = d.newNote()
- f['Front'] = "two"
+ f["Front"] = "two"
d.addNote(f)
d.reset()
# initial pass should be correct too
@@ -880,14 +924,14 @@ def test_repCounts():
assert d.sched.counts() == (0, 0, 0)
# immediate graduate should work
f = d.newNote()
- f['Front'] = "three"
+ f["Front"] = "three"
d.addNote(f)
d.reset()
d.sched.answerCard(d.sched.getCard(), 4)
assert d.sched.counts() == (0, 0, 0)
# and failing a review should too
f = d.newNote()
- f['Front'] = "three"
+ f["Front"] = "three"
d.addNote(f)
c = f.cards()[0]
c.type = 2
@@ -899,12 +943,13 @@ def test_repCounts():
d.sched.answerCard(d.sched.getCard(), 1)
assert d.sched.counts() == (0, 1, 0)
+
def test_timing():
d = getEmptyCol()
# add a few review cards, due today
for i in range(5):
f = d.newNote()
- f['Front'] = "num"+str(i)
+ f["Front"] = "num" + str(i)
d.addNote(f)
c = f.cards()[0]
c.type = 2
@@ -925,11 +970,12 @@ def test_timing():
c = d.sched.getCard()
assert c.queue == 1
+
def test_collapse():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
d.reset()
# test collapsing
@@ -939,16 +985,17 @@ def test_collapse():
d.sched.answerCard(c, 4)
assert not d.sched.getCard()
+
def test_deckDue():
d = getEmptyCol()
# add a note with default deck
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# and one that's a child
f = d.newNote()
- f['Front'] = "two"
- default1 = f.model()['did'] = d.decks.id("Default::1")
+ f["Front"] = "two"
+ default1 = f.model()["did"] = d.decks.id("Default::1")
d.addNote(f)
# make it a review card
c = f.cards()[0]
@@ -957,13 +1004,13 @@ def test_deckDue():
c.flush()
# add one more with a new deck
f = d.newNote()
- f['Front'] = "two"
- foobar = f.model()['did'] = d.decks.id("foo::bar")
+ f["Front"] = "two"
+ foobar = f.model()["did"] = d.decks.id("foo::bar")
d.addNote(f)
# and one that's a sibling
f = d.newNote()
- f['Front'] = "three"
- foobaz = f.model()['did'] = d.decks.id("foo::baz")
+ f["Front"] = "three"
+ foobaz = f.model()["did"] = d.decks.id("foo::baz")
d.addNote(f)
d.reset()
assert len(d.decks.decks) == 5
@@ -985,10 +1032,12 @@ def test_deckDue():
assert tree[0][5][0][2] == 1
assert tree[0][5][0][4] == 0
# code should not fail if a card has an invalid deck
- c.did = 12345; c.flush()
+ c.did = 12345
+ c.flush()
d.sched.deckDueList()
d.sched.deckDueTree()
+
def test_deckTree():
d = getEmptyCol()
d.decks.id("new::b::c")
@@ -998,75 +1047,80 @@ def test_deckTree():
names.remove("new")
assert "new" not in names
+
def test_deckFlow():
d = getEmptyCol()
# add a note with default deck
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
# and one that's a child
f = d.newNote()
- f['Front'] = "two"
- default1 = f.model()['did'] = d.decks.id("Default::2")
+ f["Front"] = "two"
+ default1 = f.model()["did"] = d.decks.id("Default::2")
d.addNote(f)
# and another that's higher up
f = d.newNote()
- f['Front'] = "three"
- default1 = f.model()['did'] = d.decks.id("Default::1")
+ f["Front"] = "three"
+ default1 = f.model()["did"] = d.decks.id("Default::1")
d.addNote(f)
# should get top level one first, then ::1, then ::2
d.reset()
- assert d.sched.counts() == (3,0,0)
+ assert d.sched.counts() == (3, 0, 0)
for i in "one", "three", "two":
c = d.sched.getCard()
- assert c.note()['Front'] == i
+ assert c.note()["Front"] == i
d.sched.answerCard(c, 3)
+
def test_reorder():
d = getEmptyCol()
# add a note with default deck
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
f2 = d.newNote()
- f2['Front'] = "two"
+ f2["Front"] = "two"
d.addNote(f2)
assert f2.cards()[0].due == 2
- found=False
+ found = False
# 50/50 chance of being reordered
for i in range(20):
d.sched.randomizeCards(1)
if f.cards()[0].due != f.id:
- found=True
+ found = True
break
assert found
d.sched.orderCards(1)
assert f.cards()[0].due == 1
# shifting
f3 = d.newNote()
- f3['Front'] = "three"
+ f3["Front"] = "three"
d.addNote(f3)
f4 = d.newNote()
- f4['Front'] = "four"
+ f4["Front"] = "four"
d.addNote(f4)
assert f.cards()[0].due == 1
assert f2.cards()[0].due == 2
assert f3.cards()[0].due == 3
assert f4.cards()[0].due == 4
- d.sched.sortCards([
- f3.cards()[0].id, f4.cards()[0].id], start=1, shift=True)
+ d.sched.sortCards([f3.cards()[0].id, f4.cards()[0].id], start=1, shift=True)
assert f.cards()[0].due == 3
assert f2.cards()[0].due == 4
assert f3.cards()[0].due == 1
assert f4.cards()[0].due == 2
+
def test_forget():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
- c.queue = 2; c.type = 2; c.ivl = 100; c.due = 0
+ c.queue = 2
+ c.type = 2
+ c.ivl = 100
+ c.due = 0
c.flush()
d.reset()
assert d.sched.counts() == (0, 0, 1)
@@ -1074,10 +1128,11 @@ def test_forget():
d.reset()
assert d.sched.counts() == (1, 0, 0)
+
def test_resched():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
d.sched.reschedCards([c.id], 0, 0)
@@ -1087,14 +1142,15 @@ def test_resched():
assert c.queue == c.type == 2
d.sched.reschedCards([c.id], 1, 1)
c.load()
- assert c.due == d.sched.today+1
+ assert c.due == d.sched.today + 1
assert c.ivl == +1
+
def test_norelearn():
d = getEmptyCol()
# add a note
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
c = f.cards()[0]
c.type = 2
@@ -1108,13 +1164,15 @@ def test_norelearn():
c.flush()
d.reset()
d.sched.answerCard(c, 1)
- d.sched._cardConf(c)['lapse']['delays'] = []
+ d.sched._cardConf(c)["lapse"]["delays"] = []
d.sched.answerCard(c, 1)
+
def test_failmult():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
c = f.cards()[0]
c.type = 2
@@ -1126,19 +1184,20 @@ def test_failmult():
c.lapses = 1
c.startTimer()
c.flush()
- d.sched._cardConf(c)['lapse']['mult'] = 0.5
+ d.sched._cardConf(c)["lapse"]["mult"] = 0.5
c = d.sched.getCard()
d.sched.answerCard(c, 1)
assert c.ivl == 50
d.sched.answerCard(c, 1)
assert c.ivl == 25
+
def test_moveVersions():
col = getEmptyCol()
col.changeSchedulerVer(1)
n = col.newNote()
- n['Front'] = "one"
+ n["Front"] = "one"
col.addNote(n)
# make it a learning card
@@ -1176,8 +1235,10 @@ def test_moveVersions():
col.changeSchedulerVer(2)
# card with 100 day interval, answering again
col.sched.reschedCards([c.id], 100, 100)
- c.load(); c.due = 0; c.flush()
- col.sched._cardConf(c)['lapse']['mult'] = 0.5
+ c.load()
+ c.due = 0
+ c.flush()
+ col.sched._cardConf(c)["lapse"]["mult"] = 0.5
col.sched.reset()
c = col.sched.getCard()
col.sched.answerCard(c, 1)
@@ -1186,6 +1247,7 @@ def test_moveVersions():
c.load()
assert c.due == 50
+
# cards with a due date earlier than the collection should retain
# their due date when removed
def test_negativeDueFilter():
@@ -1193,7 +1255,8 @@ def test_negativeDueFilter():
# card due prior to collection date
f = d.newNote()
- f['Front'] = "one"; f['Back'] = "two"
+ f["Front"] = "one"
+ f["Back"] = "two"
d.addNote(f)
c = f.cards()[0]
c.due = -5
@@ -1209,4 +1272,3 @@ def test_negativeDueFilter():
c.load()
assert c.due == -5
-
diff --git a/tests/test_stats.py b/tests/test_stats.py
index b7212d1e5..587050878 100644
--- a/tests/test_stats.py
+++ b/tests/test_stats.py
@@ -1,12 +1,13 @@
# coding: utf-8
-import os
-from tests.shared import getEmptyCol
+import os
+from tests.shared import getEmptyCol
+
def test_stats():
d = getEmptyCol()
f = d.newNote()
- f['Front'] = "foo"
+ f["Front"] = "foo"
d.addNote(f)
c = f.cards()[0]
# card stats
@@ -17,12 +18,15 @@ def test_stats():
d.sched.answerCard(c, 2)
assert d.cardStats(c)
+
def test_graphs_empty():
d = getEmptyCol()
assert d.stats().report()
+
def test_graphs():
from anki import Collection as aopen
+
d = aopen(os.path.expanduser("~/test.anki2"))
g = d.stats()
rep = g.report()
diff --git a/tests/test_template.py b/tests/test_template.py
index 7132c1240..5a61e11f8 100644
--- a/tests/test_template.py
+++ b/tests/test_template.py
@@ -2,15 +2,18 @@ from anki.template import Template
def test_remove_formatting_from_mathjax():
- t = Template('')
- assert t._removeFormattingFromMathjax(r'\(2^{{c3::2}}\)', 3) == r'\(2^{{C3::2}}\)'
+ t = Template("")
+ assert t._removeFormattingFromMathjax(r"\(2^{{c3::2}}\)", 3) == r"\(2^{{C3::2}}\)"
- txt = (r'{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) '
- r'{{c4::blah}} {{c5::text with \(x^2\) jax}}')
+ txt = (
+ r"{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) "
+ r"{{c4::blah}} {{c5::text with \(x^2\) jax}}"
+ )
# Cloze 2 is not in MathJax, so it should not get protected against
# formatting.
assert t._removeFormattingFromMathjax(txt, 2) == txt
- txt = r'\(a\) {{c1::b}} \[ {{c1::c}} \]'
+ txt = r"\(a\) {{c1::b}} \[ {{c1::c}} \]"
assert t._removeFormattingFromMathjax(txt, 1) == (
- r'\(a\) {{c1::b}} \[ {{C1::c}} \]')
+ r"\(a\) {{c1::b}} \[ {{C1::c}} \]"
+ )
diff --git a/tests/test_undo.py b/tests/test_undo.py
index 457112987..1fdfff8f7 100644
--- a/tests/test_undo.py
+++ b/tests/test_undo.py
@@ -1,16 +1,17 @@
# coding: utf-8
import time
-from tests.shared import getEmptyCol
+from tests.shared import getEmptyCol
from anki.consts import *
+
def test_op():
d = getEmptyCol()
# should have no undo by default
assert not d.undoName()
# let's adjust a study option
d.save("studyopts")
- d.conf['abc'] = 5
+ d.conf["abc"] = 5
# it should be listed as undoable
assert d.undoName() == "studyopts"
# with about 5 minutes until it's clobbered
@@ -18,7 +19,7 @@ def test_op():
# undoing should restore the old value
d.undo()
assert not d.undoName()
- assert 'abc' not in d.conf
+ assert "abc" not in d.conf
# an (auto)save will clear the undo
d.save("foo")
assert d.undoName() == "foo"
@@ -27,7 +28,7 @@ def test_op():
# and a review will, too
d.save("add")
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
d.reset()
assert d.undoName() == "add"
@@ -35,11 +36,12 @@ def test_op():
d.sched.answerCard(c, 2)
assert d.undoName() == "Review"
+
def test_review():
d = getEmptyCol()
- d.conf['counts'] = COUNT_REMAINING
+ d.conf["counts"] = COUNT_REMAINING
f = d.newNote()
- f['Front'] = "one"
+ f["Front"] = "one"
d.addNote(f)
d.reset()
assert not d.undoName()
@@ -62,7 +64,7 @@ def test_review():
assert not d.undoName()
# we should be able to undo multiple answers too
f = d.newNote()
- f['Front'] = "two"
+ f["Front"] = "two"
d.addNote(f)
d.reset()
assert d.sched.counts() == (2, 0, 0)
@@ -85,5 +87,3 @@ def test_review():
assert d.undoName() == "foo"
d.undo()
assert not d.undoName()
-
-
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 96d787a83..8ac52d4f1 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -2,6 +2,7 @@
from anki.utils import fmtTimeSpan
+
def test_fmtTimeSpan():
assert fmtTimeSpan(5) == "5 seconds"
assert fmtTimeSpan(5, inTime=True) == "in 5 seconds"