diff --git a/tests/support/anki12.anki b/tests/support/anki12.anki
new file mode 100644
index 000000000..f39aeda6c
Binary files /dev/null and b/tests/support/anki12.anki differ
diff --git a/tests/support/dingsbums.xml b/tests/support/dingsbums.xml
new file mode 100644
index 000000000..a00bd17df
--- /dev/null
+++ b/tests/support/dingsbums.xml
@@ -0,0 +1,50 @@
+
+
+Deutsch - ItalienischRick Gruber-RiemerKlett Grund- und AufbauwortschatzDeutschItalienischAttributesLektionKategoriOthersErklärungUnregelmässigen_USen_USen_USen_USen_USen_USen_USen_USen_US1000222falsefalse
+
+RegelmässigUnregelmässig
+avereessereavere oder essere
+-are (regelmässig)-ere (regelmässig)-ire (regelmässig)Unregelmässig
+illa
+RegelmässigUnregelmässigNur EinzahlNur Mehrzahl
+
+
+
+
+
+
+
+
+
+
+
+
+Rest
+Harenberg Kalender Italienisch 2007
+50. Restaurant, Café, Hotel
+Berlitz Kalender 2005
+A
+
+
+Default
+
+
+entfernen, beseitigenallontanare
+dann; damals, also; früherallora
+Schüler, Zöglingallievo
+lustig, heiterallegro
+sich in einer unbequemen Situation befindenessere un pesche four d' aqua
+das ist mir egalme ne fregoGeste: unter dem Kinn mit der Hand vonhinten nach vorne reiben
+Wirtinostessa
+
+
+2361915312
+1961915312
+1251251100
+2021915312
+1881251100
+1241811000
+1761915312
+1241811000
+
+
diff --git a/tests/support/fake.png b/tests/support/fake.png
new file mode 100644
index 000000000..8baef1b4a
--- /dev/null
+++ b/tests/support/fake.png
@@ -0,0 +1 @@
+abc
diff --git a/tests/support/supermemo1.xml b/tests/support/supermemo1.xml
new file mode 100644
index 000000000..d65652107
--- /dev/null
+++ b/tests/support/supermemo1.xml
@@ -0,0 +1,89 @@
+
+
+ 3572
+
+
+ 1
+
+ Topic
+
+
+
+
+
+
+
+
+ 40326
+
+ aoeu
+
+ Topic
+
+
+ 40327
+
+ 1-400
+
+ Topic
+
+
+ 40615
+
+ aoeu
+
+ Topic
+
+
+ 10247
+
+ Item
+
+
+ aoeu
+
+ aoeu
+
+
+
+ 1844
+
+ 7
+
+ 0
+
+ 19.09.2002
+
+ 5,701
+
+ 2,452
+
+
+
+
+
+ Topic
+
+
+ aoeu
+
+
+
+
+ 0
+
+ 0
+
+ 0
+
+ 04.08.2000
+
+ 3,000
+
+ 0,000
+
+
+
+
+
+
diff --git a/tests/support/syncing-media/1.anki b/tests/support/syncing-media/1.anki
new file mode 100644
index 000000000..05940936a
Binary files /dev/null and b/tests/support/syncing-media/1.anki differ
diff --git a/tests/support/syncing-media/1.media/834a227f8d0abc4e2193f08138e59885.png b/tests/support/syncing-media/1.media/834a227f8d0abc4e2193f08138e59885.png
new file mode 100644
index 000000000..80bca2188
Binary files /dev/null and b/tests/support/syncing-media/1.media/834a227f8d0abc4e2193f08138e59885.png differ
diff --git a/tests/support/syncing-media/1.media/c4ad64a7afe9b09602cdf91e18897959.png b/tests/support/syncing-media/1.media/c4ad64a7afe9b09602cdf91e18897959.png
new file mode 100644
index 000000000..3cbfaf0e5
Binary files /dev/null and b/tests/support/syncing-media/1.media/c4ad64a7afe9b09602cdf91e18897959.png differ
diff --git a/tests/support/syncing-media/2.anki b/tests/support/syncing-media/2.anki
new file mode 100644
index 000000000..13a981b88
Binary files /dev/null and b/tests/support/syncing-media/2.anki differ
diff --git a/tests/support/syncing-media/2.media/22161b29b0c18e068038021f54eee1ee.png b/tests/support/syncing-media/2.media/22161b29b0c18e068038021f54eee1ee.png
new file mode 100644
index 000000000..f4a2ba43f
Binary files /dev/null and b/tests/support/syncing-media/2.media/22161b29b0c18e068038021f54eee1ee.png differ
diff --git a/tests/support/test.mem b/tests/support/test.mem
new file mode 100644
index 000000000..71668e56b
--- /dev/null
+++ b/tests/support/test.mem
@@ -0,0 +1,219 @@
+--- Mnemosyne Data Base --- Format Version 1 ---
+(lp1
+(imnemosyne.core.mnemosyne_core
+StartTime
+p2
+(dp3
+S'time'
+p4
+F1183141800
+sba(lp5
+(imnemosyne.core.mnemosyne_core
+Category
+p6
+(dp7
+S'active'
+p8
+I01
+sS'name'
+p9
+V
+p10
+sba(imnemosyne.core.mnemosyne_core
+Category
+p11
+(dp12
+S'active'
+p13
+I01
+sS'name'
+p14
+Vfoo
+p15
+sba(imnemosyne.core.mnemosyne_core
+Category
+p16
+(dp17
+g13
+I01
+sg14
+Vbaz, quux
+p18
+sbaa(lp19
+(imnemosyne.core.mnemosyne_core
+Item
+p20
+(dp21
+S'a'
+Vbar
+p22
+sS'last_rep'
+p23
+L34L
+sS'ret_reps'
+p24
+I0
+sS'cat'
+p25
+g16
+sS'q'
+Vfoo
+p26
+sS'grade'
+p27
+I0
+sS'acq_reps'
+p28
+I1
+sS'ret_reps_since_lapse'
+p29
+I0
+sS'easiness'
+p30
+F2.5
+sS'lapses'
+p31
+I0
+sS'acq_reps_since_lapse'
+p32
+I1
+sS'next_rep'
+p33
+L34L
+sS'id'
+p34
+S'9f401476'
+p35
+sba(imnemosyne.core.mnemosyne_core
+Item
+p36
+(dp37
+S'a'
+Vfoo
+p38
+sg23
+L34L
+sg24
+I0
+sg25
+g6
+sS'q'
+Vbar
+p39
+sg27
+I0
+sg28
+I1
+sg29
+I0
+sg30
+F2.5
+sg31
+I0
+sg32
+I1
+sg33
+L34L
+sg34
+S'a869958d'
+p40
+sba(imnemosyne.core.mnemosyne_core
+Item
+p41
+(dp42
+S'a'
+Vquux
+p43
+sg23
+L34L
+sg24
+I0
+sg25
+g11
+sS'q'
+Vbaz
+p44
+sg27
+I5
+sg28
+I2
+sg29
+I0
+sg30
+F2.5
+sg31
+I0
+sg32
+I2
+sg33
+L35L
+sg34
+S'74651aa3'
+p45
+sba(imnemosyne.core.mnemosyne_core
+Item
+p46
+(dp47
+S'a'
+Vbaz
+p48
+sg23
+L34L
+sg24
+I0
+sg25
+g6
+sS'q'
+Vquux
+p49
+sg27
+I0
+sg28
+I1
+sg29
+I0
+sg30
+F2.5
+sg31
+I0
+sg32
+I1
+sg33
+L34L
+sg34
+S'0bd8f10b'
+p50
+sba(imnemosyne.core.mnemosyne_core
+Item
+p51
+(dp52
+S'a'
+Vcard
+p53
+sg23
+L34L
+sg24
+I0
+sg25
+g11
+sS'q'
+Vonesided
+p54
+sg27
+I0
+sg28
+I1
+sg29
+I0
+sg30
+F2.5
+sg31
+I0
+sg32
+I1
+sg33
+L34L
+sg34
+S'bb503cf1'
+p55
+sbaa.
\ No newline at end of file
diff --git a/tests/support/text-2fields.txt b/tests/support/text-2fields.txt
new file mode 100644
index 000000000..b72afcaba
--- /dev/null
+++ b/tests/support/text-2fields.txt
@@ -0,0 +1,11 @@
+# this is a test file
+
+食べる to eat
+飲む to drink
+テスト test
+to eat 食べる
+飲む to drink
+多すぎる too many fields
+not, enough, fields
+遊ぶ
+ to play
diff --git a/tests/support/text-tags.txt b/tests/support/text-tags.txt
new file mode 100644
index 000000000..c7f68a8ce
--- /dev/null
+++ b/tests/support/text-tags.txt
@@ -0,0 +1,2 @@
+foo bar baz,qux
+foo2 bar2 baz2
diff --git a/tests/support/text-update.txt b/tests/support/text-update.txt
new file mode 100644
index 000000000..f87113172
--- /dev/null
+++ b/tests/support/text-update.txt
@@ -0,0 +1,11 @@
+# this is a test file
+
+食べる to ate
+む to drink
+テスト testing
+to eat 食べる
+飲む to drink
+多すぎる too many fields
+not, enough, fields
+遊ぶ
+ to play
diff --git a/tests/test_find.py b/tests/test_find.py
new file mode 100644
index 000000000..92f55a89a
--- /dev/null
+++ b/tests/test_find.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+
+from tests.shared import getEmptyDeck
+
+def test_findCards():
+ deck = getEmptyDeck()
+ f = deck.newFact()
+ f['Front'] = u'dog'
+ f['Back'] = u'cat'
+ f.addTags(u"monkey")
+ deck.addFact(f)
+ f = deck.newFact()
+ f['Front'] = u'goats are fun'
+ f['Back'] = u'sheep'
+ f.addTags(u"sheep goat horse")
+ deck.addFact(f)
+ f = deck.newFact()
+ f['Front'] = u'cat'
+ f['Back'] = u'sheep'
+ deck.addFact(f)
+ 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")) == 2
+ 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
+ deck.addTags(deck.db.list("select id from cards"), "foo bar")
+ assert (len(deck.findCards("tag:foo")) ==
+ len(deck.findCards("tag:bar")) ==
+ 3)
+ deck.deleteTags(deck.db.list("select id from cards"), "foo")
+ assert len(deck.findCards("tag:foo")) == 0
+ assert len(deck.findCards("tag:bar")) == 3
diff --git a/tests/test_models.py b/tests/test_models.py
new file mode 100644
index 000000000..eb6a12070
--- /dev/null
+++ b/tests/test_models.py
@@ -0,0 +1,97 @@
+# coding: utf-8
+
+from tests.shared import getEmptyDeck
+from anki.models import Model, Template, Field
+from anki.utils import stripHTML
+
+def test_modelDelete():
+ deck = getEmptyDeck()
+ f = deck.newFact()
+ f['Front'] = u'1'
+ f['Back'] = u'2'
+ deck.addFact(f)
+ assert deck.cardCount() == 1
+ deck.deleteModel(deck.conf['currentModelId'])
+ assert deck.cardCount() == 0
+
+def test_modelCopy():
+ deck = getEmptyDeck()
+ m = deck.currentModel()
+ m2 = m.copy()
+ assert m2.name == "Basic copy"
+ assert m2.id != m.id
+ assert m2.fields[0].id != m.fields[0].id
+ assert m2.templates[0].id != m.templates[0].id
+ assert len(m2.fields) == 2
+ assert len(m.fields) == 2
+ assert len(m2.fields) == len(m.fields)
+ assert len(m.templates) == 2
+ assert len(m2.templates) == 2
+
+def test_modelChange():
+ deck = getEmptyDeck()
+ m2 = deck.currentModel()
+ # taken from jp support plugin
+ m1 = Model(deck)
+ m1.name = "Japanese"
+ # field 1
+ fm = Field(deck)
+ fm.name = "Expression"
+ fm.conf['required'] = True
+ fm.conf['unique'] = True
+ m1.addField(fm)
+ # field2
+ fm = Field(deck)
+ fm.name = "Meaning"
+ m1.addField(fm)
+ # field3
+ fm = Field(deck)
+ fm.name = "Reading"
+ m1.addField(fm)
+ # template1
+ t = Template(deck)
+ t.name = "Recognition"
+ t.qfmt = "{{Expression}}"
+ t.afmt = "{{Reading}}
{{Meaning}}"
+ m1.addTemplate(t)
+ # template2
+ t = Template(deck)
+ t.name = "Recall"
+ t.qfmt = "{{Meaning}}"
+ t.afmt = "{{Expression}}
{{Reading}}"
+ #t.active = False
+ m1.addTemplate(t)
+ deck.addModel(m1)
+
+ # add some facts
+ f = deck.newFact()
+ f['Expression'] = u'e'
+ f['Meaning'] = u'm'
+ f['Reading'] = u'r'
+ deck.addFact(f)
+ f2 = deck.newFact()
+ f2['Expression'] = u'e2'
+ f2['Meaning'] = u'm2'
+ f2['Reading'] = u'r2'
+ deck.addFact(f2)
+
+ # convert to basic
+ assert deck.modelUseCount(m1) == 2
+ assert deck.modelUseCount(m2) == 0
+ assert deck.cardCount() == 4
+ assert deck.factCount() == 2
+ fmap = {m1.fields[0]: m2.fields[0],
+ m1.fields[1]: None,
+ m1.fields[2]: m2.fields[1]}
+ cmap = {m1.templates[0]: m2.templates[0],
+ m1.templates[1]: None}
+ deck.changeModel([f.id], m2, fmap, cmap)
+ assert deck.modelUseCount(m1) == 1
+ assert deck.modelUseCount(m2) == 1
+ assert deck.cardCount() == 3
+ assert deck.factCount() == 2
+ (q, a) = deck.db.first("""
+select q, a from cards where fid = :id""",
+ id=f.id)
+ assert stripHTML(q) == u"e"
+ assert stripHTML(a) == u"r"