317 lines
11 KiB
Python
317 lines
11 KiB
Python
# coding: utf-8
|
|
import pytest
|
|
|
|
from anki.consts import *
|
|
from anki.find import Finder
|
|
from tests.shared import getEmptyCol
|
|
|
|
|
|
class DummyCollection:
|
|
def weakref(self):
|
|
return None
|
|
|
|
|
|
def test_parse():
|
|
f = Finder(DummyCollection())
|
|
assert f._tokenize("hello world") == ["hello", "world"]
|
|
assert f._tokenize("hello world") == ["hello", "world"]
|
|
assert f._tokenize("one -two") == ["one", "-", "two"]
|
|
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("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.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.tags.append("sheep goat horse animal11")
|
|
deck.addNote(f)
|
|
f2id = f.id
|
|
f = deck.newNote()
|
|
f["Front"] = "cat"
|
|
f["Back"] = "sheep"
|
|
deck.addNote(f)
|
|
catCard = f.cards()[0]
|
|
m = deck.models.current()
|
|
mm = deck.models
|
|
t = mm.newTemplate("Reverse")
|
|
t["qfmt"] = "{{Back}}"
|
|
t["afmt"] = "{{Front}}"
|
|
mm.addTemplate(m, t)
|
|
mm.save(m)
|
|
f = deck.newNote()
|
|
f["Front"] = "test"
|
|
f["Back"] = "foo bar"
|
|
deck.addNote(f)
|
|
latestCardIds = [c.id for c in f.cards()]
|
|
# tag searches
|
|
assert len(deck.findCards("tag:*")) == 5
|
|
assert len(deck.findCards("tag:\\*")) == 1
|
|
assert len(deck.findCards("tag:%")) == 5
|
|
assert len(deck.findCards("tag:\\%")) == 1
|
|
assert len(deck.findCards("tag:animal_1")) == 2
|
|
assert len(deck.findCards("tag:animal\\_1")) == 1
|
|
assert not deck.findCards("tag:donkey")
|
|
assert len(deck.findCards("tag:sheep")) == 1
|
|
assert len(deck.findCards("tag:sheep tag:goat")) == 1
|
|
assert len(deck.findCards("tag:sheep tag:monkey")) == 0
|
|
assert len(deck.findCards("tag:monkey")) == 1
|
|
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
|
|
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
|
|
# text searches
|
|
assert len(deck.findCards("cat")) == 2
|
|
assert len(deck.findCards("cat -dog")) == 1
|
|
assert len(deck.findCards("cat -dog")) == 1
|
|
assert len(deck.findCards("are goats")) == 1
|
|
assert len(deck.findCards('"are goats"')) == 0
|
|
assert len(deck.findCards('"goats are"')) == 1
|
|
# card states
|
|
c = f.cards()[0]
|
|
c.queue = c.type = CARD_TYPE_REV
|
|
assert deck.findCards("is:review") == []
|
|
c.flush()
|
|
assert deck.findCards("is:review") == [c.id]
|
|
assert deck.findCards("is:due") == []
|
|
c.due = 0
|
|
c.queue = QUEUE_TYPE_REV
|
|
c.flush()
|
|
assert deck.findCards("is:due") == [c.id]
|
|
assert len(deck.findCards("-is:due")) == 4
|
|
c.queue = -1
|
|
# ensure this card gets a later mod time
|
|
c.flush()
|
|
deck.db.execute("update cards set mod = mod + 1 where id = ?", c.id)
|
|
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,%d" % (f1id, f2id))) == 2
|
|
# templates
|
|
with pytest.raises(Exception):
|
|
deck.findCards("card:foo")
|
|
assert len(deck.findCards("'card:card 1'")) == 4
|
|
assert len(deck.findCards("card:reverse")) == 1
|
|
assert len(deck.findCards("card:1")) == 4
|
|
assert len(deck.findCards("card:2")) == 1
|
|
# fields
|
|
assert len(deck.findCards("front:dog")) == 1
|
|
assert len(deck.findCards("-front:dog")) == 4
|
|
assert len(deck.findCards("front:sheep")) == 0
|
|
assert len(deck.findCards("back:sheep")) == 2
|
|
assert len(deck.findCards("-back:sheep")) == 3
|
|
assert len(deck.findCards("front:do")) == 0
|
|
assert len(deck.findCards("front:*")) == 5
|
|
# ordering
|
|
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"
|
|
assert deck.findCards("", order=True)[0] == catCard.id
|
|
assert deck.findCards("", order=True)[-1] in latestCardIds
|
|
deck.conf["sortType"] = "cardMod"
|
|
assert deck.findCards("", order=True)[-1] in latestCardIds
|
|
assert deck.findCards("", order=True)[0] == firstCardId
|
|
deck.conf["sortBackwards"] = True
|
|
assert deck.findCards("", order=True)[0] in latestCardIds
|
|
# model
|
|
assert len(deck.findCards("note:basic")) == 5
|
|
assert len(deck.findCards("-note:basic")) == 0
|
|
assert len(deck.findCards("-note:foo")) == 5
|
|
# deck
|
|
assert len(deck.findCards("deck:default")) == 5
|
|
assert len(deck.findCards("-deck:default")) == 0
|
|
assert len(deck.findCards("-deck:foo")) == 5
|
|
assert len(deck.findCards("deck:def*")) == 5
|
|
assert len(deck.findCards("deck:*EFAULT")) == 5
|
|
with pytest.raises(Exception):
|
|
deck.findCards("deck:*cefault")
|
|
# full search
|
|
f = deck.newNote()
|
|
f["Front"] = "hello<b>world</b>"
|
|
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
|
|
# if we put it on the back, it won't
|
|
(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
|
|
# searching for an invalid special tag should not error
|
|
with pytest.raises(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
|
|
)
|
|
assert len(deck.findCards("deck:default")) == 7
|
|
assert len(deck.findCards("deck:default::child")) == 1
|
|
assert len(deck.findCards("deck:default -deck:default::*")) == 6
|
|
# properties
|
|
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,
|
|
)
|
|
assert len(deck.findCards("prop:ivl>5")) == 1
|
|
assert len(deck.findCards("prop:ivl<5")) > 1
|
|
assert len(deck.findCards("prop:ivl>=5")) == 1
|
|
assert len(deck.findCards("prop:ivl=9")) == 0
|
|
assert len(deck.findCards("prop:ivl=10")) == 1
|
|
assert len(deck.findCards("prop:ivl!=10")) > 1
|
|
assert len(deck.findCards("prop:due>0")) == 1
|
|
# due dates should work
|
|
deck.sched.today = 15
|
|
assert len(deck.findCards("prop:due=14")) == 0
|
|
assert len(deck.findCards("prop:due=15")) == 1
|
|
assert len(deck.findCards("prop:due=16")) == 0
|
|
# including negatives
|
|
deck.sched.today = 32
|
|
assert len(deck.findCards("prop:due=-1")) == 0
|
|
assert len(deck.findCards("prop:due=-2")) == 1
|
|
# ease factors
|
|
assert len(deck.findCards("prop:ease=2.3")) == 0
|
|
assert len(deck.findCards("prop:ease=2.2")) == 1
|
|
assert len(deck.findCards("prop:ease>2")) == 1
|
|
assert len(deck.findCards("-prop:ease>2")) > 1
|
|
# recently failed
|
|
assert len(deck.findCards("rated:1:1")) == 0
|
|
assert len(deck.findCards("rated:1:2")) == 0
|
|
c = deck.sched.getCard()
|
|
deck.sched.answerCard(c, 2)
|
|
assert len(deck.findCards("rated:1:1")) == 0
|
|
assert len(deck.findCards("rated:1:2")) == 1
|
|
c = deck.sched.getCard()
|
|
deck.sched.answerCard(c, 1)
|
|
assert len(deck.findCards("rated:1:1")) == 1
|
|
assert len(deck.findCards("rated:1:2")) == 1
|
|
assert len(deck.findCards("rated:1")) == 2
|
|
assert len(deck.findCards("rated:0:2")) == 0
|
|
assert len(deck.findCards("rated:2:2")) == 1
|
|
# empty field
|
|
assert len(deck.findCards("front:")) == 0
|
|
f = deck.newNote()
|
|
f["Front"] = ""
|
|
f["Back"] = "abc2"
|
|
assert deck.addNote(f) == 1
|
|
assert len(deck.findCards("front:")) == 1
|
|
# OR searches and nesting
|
|
assert len(deck.findCards("tag:monkey or tag:sheep")) == 2
|
|
assert len(deck.findCards("(tag:monkey OR tag:sheep)")) == 2
|
|
assert len(deck.findCards("-(tag:monkey OR tag:sheep)")) == 6
|
|
assert len(deck.findCards("tag:monkey or (tag:sheep sheep)")) == 2
|
|
assert len(deck.findCards("tag:monkey or (tag:sheep octopus)")) == 1
|
|
# invalid grouping shouldn't error
|
|
assert len(deck.findCards(")")) == 0
|
|
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)
|
|
assert len(deck.findCards("added:1")) == deck.cardCount() - 1
|
|
assert len(deck.findCards("added:2")) == deck.cardCount()
|
|
# flag
|
|
with pytest.raises(Exception):
|
|
deck.findCards("flag:01")
|
|
with pytest.raises(Exception):
|
|
deck.findCards("flag:12")
|
|
|
|
|
|
def test_findReplace():
|
|
deck = getEmptyCol()
|
|
f = deck.newNote()
|
|
f["Front"] = "foo"
|
|
f["Back"] = "bar"
|
|
deck.addNote(f)
|
|
f2 = deck.newNote()
|
|
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"
|
|
# single field replace
|
|
assert deck.findReplace(nids, "qux", "foo", field="Front") == 1
|
|
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"
|
|
assert deck.findReplace(nids, "B.r", "reg", regex=True) == 1
|
|
f.load()
|
|
assert f["Back"] == "reg"
|
|
|
|
|
|
def test_findDupes():
|
|
deck = getEmptyCol()
|
|
f = deck.newNote()
|
|
f["Front"] = "foo"
|
|
f["Back"] = "bar"
|
|
deck.addNote(f)
|
|
f2 = deck.newNote()
|
|
f2["Front"] = "baz"
|
|
f2["Back"] = "bar"
|
|
deck.addNote(f2)
|
|
f3 = deck.newNote()
|
|
f3["Front"] = "quux"
|
|
f3["Back"] = "bar"
|
|
deck.addNote(f3)
|
|
f4 = deck.newNote()
|
|
f4["Front"] = "quuux"
|
|
f4["Back"] = "nope"
|
|
deck.addNote(f4)
|
|
r = deck.findDupes("Back")
|
|
assert r[0][0] == "bar"
|
|
assert len(r[0][1]) == 3
|
|
# valid search
|
|
r = deck.findDupes("Back", "bar")
|
|
assert r[0][0] == "bar"
|
|
assert len(r[0][1]) == 3
|
|
# excludes everything
|
|
r = deck.findDupes("Back", "invalid")
|
|
assert not r
|
|
# front isn't dupe
|
|
assert deck.findDupes("Front") == []
|