mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00

The parsing step is considerably slower in Python, but if parsing is moved out of the test function, Python wins at 45ms to Rust's 67ms on 10,000 rounds, presumably due to the overhead of serializing to Protobuf. Not enough of a difference to justify the inclusion of extra dependencies and duplicating the lookup code in any case.
194 lines
5.2 KiB
Python
194 lines
5.2 KiB
Python
# coding: utf-8
|
|
|
|
import os
|
|
import tempfile
|
|
|
|
from anki import Collection as aopen
|
|
from anki.rsbackend import StringsGroup
|
|
from anki.stdmodels import addBasicModel, models
|
|
from anki.utils import isWin
|
|
from tests.shared import assertException, getEmptyCol
|
|
|
|
|
|
def test_create_open():
|
|
(fd, path) = tempfile.mkstemp(suffix=".anki2", prefix="test_attachNew")
|
|
try:
|
|
os.close(fd)
|
|
os.unlink(path)
|
|
except OSError:
|
|
pass
|
|
deck = aopen(path)
|
|
# for open()
|
|
newPath = deck.path
|
|
deck.close()
|
|
newMod = deck.mod
|
|
del deck
|
|
|
|
# reopen
|
|
deck = aopen(newPath)
|
|
assert deck.mod == newMod
|
|
deck.close()
|
|
|
|
# non-writeable dir
|
|
if isWin:
|
|
dir = "c:\root.anki2"
|
|
else:
|
|
dir = "/attachroot.anki2"
|
|
assertException(Exception, lambda: aopen(dir))
|
|
# reuse tmp file from before, test non-writeable file
|
|
os.chmod(newPath, 0)
|
|
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"
|
|
n = deck.addNote(f)
|
|
assert n == 1
|
|
# test multiple cards - add another template
|
|
m = deck.models.current()
|
|
mm = deck.models
|
|
t = mm.newTemplate("Reverse")
|
|
t["qfmt"] = "{{Back}}"
|
|
t["afmt"] = "{{Front}}"
|
|
mm.addTemplate(m, t)
|
|
mm.save(m)
|
|
# the default save doesn't generate cards
|
|
assert deck.cardCount() == 1
|
|
# but when templates are edited such as in the card layout screen, it
|
|
# should generate cards on close
|
|
mm.save(m, templates=True, updateReqs=False)
|
|
assert deck.cardCount() == 2
|
|
# creating new notes should use both cards
|
|
f = deck.newNote()
|
|
f["Front"] = "three"
|
|
f["Back"] = "four"
|
|
n = deck.addNote(f)
|
|
assert n == 2
|
|
assert deck.cardCount() == 4
|
|
# check q/a generation
|
|
c0 = f.cards()[0]
|
|
assert "three" in c0.q()
|
|
# it should not be a duplicate
|
|
assert not f.dupeOrEmpty()
|
|
# now let's make a duplicate
|
|
f2 = deck.newNote()
|
|
f2["Front"] = "one"
|
|
f2["Back"] = ""
|
|
assert f2.dupeOrEmpty()
|
|
# empty first field should not be permitted either
|
|
f2["Front"] = " "
|
|
assert f2.dupeOrEmpty()
|
|
|
|
|
|
def test_fieldChecksum():
|
|
deck = getEmptyCol()
|
|
f = deck.newNote()
|
|
f["Front"] = "new"
|
|
f["Back"] = "new2"
|
|
deck.addNote(f)
|
|
assert deck.db.scalar("select csum from notes") == int("c2a6b03f", 16)
|
|
# changing the val should change the checksum
|
|
f["Front"] = "newx"
|
|
f.flush()
|
|
assert deck.db.scalar("select csum from notes") == int("302811ae", 16)
|
|
|
|
|
|
def test_addDelTags():
|
|
deck = getEmptyCol()
|
|
f = deck.newNote()
|
|
f["Front"] = "1"
|
|
deck.addNote(f)
|
|
f2 = deck.newNote()
|
|
f2["Front"] = "2"
|
|
deck.addNote(f2)
|
|
# adding for a given id
|
|
deck.tags.bulkAdd([f.id], "foo")
|
|
f.load()
|
|
f2.load()
|
|
assert "foo" in f.tags
|
|
assert "foo" not in f2.tags
|
|
# should be canonified
|
|
deck.tags.bulkAdd([f.id], "foo aaa")
|
|
f.load()
|
|
assert f.tags[0] == "aaa"
|
|
assert len(f.tags) == 2
|
|
|
|
|
|
def test_timestamps():
|
|
deck = getEmptyCol()
|
|
assert len(deck.models.models) == len(models)
|
|
for i in range(100):
|
|
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}}"
|
|
mm.save(m)
|
|
n = deck.newNote()
|
|
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.flush()
|
|
assert "anki:play" in c.q(reload=True)
|
|
# it shouldn't throw an error while people are editing
|
|
m["tmpls"][0]["qfmt"] = "{{kana:}}"
|
|
mm.save(m)
|
|
c.q(reload=True)
|
|
|
|
|
|
def test_translate():
|
|
d = getEmptyCol()
|
|
tr = d.backend.translate
|
|
|
|
# strip off unicode separators
|
|
def no_uni(s: str) -> str:
|
|
return s.replace("\u2068", "").replace("\u2069", "")
|
|
|
|
def test_backend():
|
|
assert tr(StringsGroup.TEST, "valid-key") == "a valid key"
|
|
assert "invalid-key" in tr(StringsGroup.TEST, "invalid-key")
|
|
assert no_uni(tr(StringsGroup.TEST, "plural", hats=1)) == "You have 1 hat."
|
|
assert no_uni(tr(StringsGroup.TEST, "plural", hats=2)) == "You have 2 hats."
|
|
|
|
import time
|
|
t = time.time()
|
|
test_backend()
|
|
print(time.time() - t)
|
|
|
|
from fluent.runtime import FluentBundle, FluentResource
|
|
|
|
test_path = os.path.join(
|
|
os.path.dirname(__file__), "../../rslib/tests/support/test.ftl"
|
|
)
|
|
|
|
def python_render(bundle, key, **kwargs):
|
|
try:
|
|
return bundle.format_pattern(bundle.get_message(key).value, kwargs)[0]
|
|
except:
|
|
return f"Missing key: {key}"
|
|
|
|
def test_python():
|
|
bundle = FluentBundle(["en-US"])
|
|
bundle.add_resource(FluentResource(open(test_path).read()))
|
|
assert python_render(bundle, "valid-key") == "a valid key"
|
|
assert "invalid-key" in python_render(bundle, "invalid-key")
|
|
assert no_uni(python_render(bundle, "plural", hats=1)) == "You have 1 hat."
|
|
assert no_uni(python_render(bundle, "plural", hats=2)) == "You have 2 hats."
|
|
|
|
t = time.time()
|
|
test_python()
|
|
print(time.time() - t)
|