Recently Written · git

sbm-android

sbm bookmarks for Android: fuzzy search, share to add, sync with bm and the browser add-on

git clone https://github.com/equwal/sbm-android

Log | Files | Refs


app/src/test/java/com/equwal/sbm/TsvTest.kt (5178 bytes)

1 package com.equwal.sbm
2 
3 import net.jqwik.api.Arbitraries
4 import net.jqwik.api.Arbitrary
5 import net.jqwik.api.Combinators
6 import net.jqwik.api.Example
7 import net.jqwik.api.ForAll
8 import net.jqwik.api.Property
9 import net.jqwik.api.Provide
10 
11 class TsvTest {
12     @Provide
13     fun bookmarks(): Arbitrary<Bookmark> {
14         val url = Arbitraries.strings().withCharRange('!', '~').ofMinLength(1).ofMaxLength(40)
15             .filter { !it.startsWith("#") }
16         val desc = Arbitraries.strings().ofMaxLength(40)
17             .filter { d -> d.none { it == '\t' || it == '\r' || it == '\n' } }
18         val tag = Arbitraries.strings().withCharRange('a', 'z').withChars('-', '0', '9')
19             .ofMinLength(1).ofMaxLength(10)
20         return Combinators.combine(url, desc, tag.list().ofMaxSize(5)).`as` { u, d, t -> Bookmark(u, d, t) }
21     }
22 
23     @Provide
24     fun files(): Arbitrary<List<Bookmark>> = bookmarks().list().ofMaxSize(20)
25 
26     @Property
27     fun `a written bookmark reads back the same`(@ForAll("bookmarks") b: Bookmark): Boolean =
28         Tsv.parse(Tsv.format(b) + "\n") == listOf(b)
29 
30     @Property
31     fun `a written file reads back the same`(@ForAll("files") bs: List<Bookmark>): Boolean =
32         Tsv.parse(bs.joinToString("") { Tsv.format(it) + "\n" }) == bs
33 
34     @Property
35     fun `a file with CRLF line ends reads the same`(@ForAll("files") bs: List<Bookmark>): Boolean {
36         val text = bs.joinToString("") { Tsv.format(it) + "\n" }
37         return Tsv.parse(text.replace("\n", "\r\n")) == Tsv.parse(text)
38     }
39 
40     // Web addresses as people bookmark them: scheme, host, path.
41     @Provide
42     fun hosts(): Arbitrary<String> {
43         val label = Arbitraries.strings().withCharRange('a', 'z').withCharRange('0', '9')
44             .ofMinLength(1).ofMaxLength(10)
45         val tld = Arbitraries.strings().withCharRange('a', 'z').ofMinLength(2).ofMaxLength(4)
46         return Combinators.combine(label.list().ofMinSize(1).ofMaxSize(3), tld)
47             .`as` { labels, t -> labels.joinToString(".") + "." + t }
48             .filter { !it.startsWith("www.") }
49     }
50 
51     @Provide
52     fun paths(): Arbitrary<String> =
53         Arbitraries.strings().withCharRange('a', 'z').withCharRange('A', 'Z').ofMinLength(1).ofMaxLength(6)
54             .list().ofMaxSize(3).map { segments -> segments.joinToString("") { "/$it" } }
55 
56     @Property
57     fun `the scheme does not matter`(@ForAll("hosts") h: String, @ForAll("paths") p: String): Boolean =
58         Tsv.norm("http://$h$p") == Tsv.norm("https://$h$p")
59 
60     @Property
61     fun `a leading www does not matter`(@ForAll("hosts") h: String, @ForAll("paths") p: String): Boolean =
62         Tsv.norm("https://www.$h$p") == Tsv.norm("https://$h$p")
63 
64     @Property
65     fun `trailing slashes do not matter`(@ForAll("hosts") h: String, @ForAll("paths") p: String): Boolean =
66         Tsv.norm("https://$h$p/") == Tsv.norm("https://$h$p") &&
67             Tsv.norm("https://$h$p//") == Tsv.norm("https://$h$p")
68 
69     @Property
70     fun `the case of the host does not matter`(@ForAll("hosts") h: String, @ForAll("paths") p: String): Boolean =
71         Tsv.norm("https://${h.uppercase()}$p") == Tsv.norm("https://$h$p")
72 
73     @Property
74     fun `the path counts`(@ForAll("hosts") h: String, @ForAll("paths") p: String): Boolean =
75         Tsv.norm("https://$h$p") == h + p
76 
77     @Example
78     fun `comments, blank lines and old lines`() {
79         val text = "# notes\n\nhttps://a.example\tA\tcode sec\nhttps://b.example B site | tag\n"
80         check(Tsv.parse(text) == listOf(
81             Bookmark("https://a.example", "A", listOf("code", "sec")),
82             Bookmark("https://b.example", "B site | tag", emptyList())))
83     }
84 
85     @Example
86     fun `a line without tags`() {
87         check(Tsv.parse("https://a.example\tA\t\n") == listOf(Bookmark("https://a.example", "A", emptyList())))
88     }
89 
90     @Example
91     fun `writing cleans each field`() {
92         val b = Bookmark(" https://a.example/x \n", "one\ttwo\nthree", listOf("a b", "", "c"))
93         check(Tsv.format(b) == "https://a.example/x\tone two three\ta b c")
94     }
95 
96     @Example
97     fun `the same page, as bm finds it`() {
98         val known = listOf(Bookmark("https://www.Example.com/a/", "A", emptyList()))
99         check(Tsv.find(known, "http://example.com/a") == known[0])
100         check(Tsv.find(known, "https://example.com/b") == null)
101     }
102 
103     @Example
104     fun `a new line always starts on a line of its own`() {
105         check(Tsv.appendix("", "x") == "x\n")
106         check(Tsv.appendix("a\n", "x") == "x\n")
107         check(Tsv.appendix("a", "x") == "\nx\n")
108     }
109 
110     @Example
111     fun `text that is no bookmark goes where bm sends it`() {
112         check(Tsv.target("suckless.org/dwm") == "https://suckless.org/dwm")
113         check(Tsv.target("gopher://x") == "gopher://x")
114         check(Tsv.target("posix sh printf") == "https://duckduckgo.com/?q=posix+sh+printf")
115     }
116 
117     @Example
118     fun `host and first address`() {
119         check(Tsv.host("https://www.a.example/x?y#z") == "www.a.example")
120         check(Tsv.host("a.example") == "a.example")
121         check(Tsv.firstUrl("Look at https://a.example/x now") == "https://a.example/x")
122         check(Tsv.firstUrl("no address") == null)
123     }
124 }