test.fnl (8160B)
1 (local { : database : event-loop : parse-event : parse-terms } (require :devout)) 2 (local { : view } (require :fennel)) 3 (local ll (require :lualinux)) 4 (import-macros { : expect : expect= } :anoia.assert) 5 6 (var failed false) 7 (fn fail [d msg] (set failed true) (print :FAIL d (.. "\n" msg))) 8 9 (macro example [description & body] 10 (if (. body 1) 11 `(let [(ok?# err#) (xpcall (fn [] ,body) debug.traceback)] 12 (if ok?# 13 (print :PASS ,description) 14 (fail ,description err#))) 15 `(print :PENDING ,description))) 16 17 (local sda-uevent 18 "add@/devices/pci0000:00/0000:00:13.0/usb1/1-1/1-1:1.0/host0/target0:0:0/0:0:0:0/block/sda\0ACTION=add 19 DEVPATH=/devices/pci0000:00/0000:00:13.0/usb1/1-1/1-1:1.0/host0/target0:0:0/0:0:0:0/block/sda 20 SUBSYSTEM=block 21 MAJOR=8 22 MINOR=0 23 DEVNAME=sda 24 DEVTYPE=disk 25 DISKSEQ=2 26 SEQNUM=1527") 27 28 (local sdb1-insert 29 "add@/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/host1/target1:0:0/1:0:0:0/block/sdb/sdb1\0ACTION=add 30 DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/host1/target1:0:0/1:0:0:0/block/sdb/sdb1 31 SUBSYSTEM=block 32 DEVNAME=/dev/sdb1 33 DEVTYPE=partition 34 DISKSEQ=33 35 PARTN=1 36 SEQNUM=82381 37 MAJOR=8 38 MINOR=17") 39 40 (local sdb1-remove 41 "remove@/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/host1/target1:0:0/1:0:0:0/block/sdb/sdb1\0ACTION=remove 42 DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/host1/target1:0:0/1:0:0:0/block/sdb/sdb1 43 SUBSYSTEM=block 44 DEVNAME=/dev/sdb1 45 DEVTYPE=partition 46 DISKSEQ=33 47 PARTN=1 48 SEQNUM=82386 49 MAJOR=8 50 MINOR=17") 51 52 (example 53 "I can parse an event" 54 (let [e (parse-event sdb1-insert)] 55 (expect= e.properties.seqnum "82381") 56 (expect= e.properties.devname "/dev/sdb1") 57 (expect= e.path "/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/host1/target1:0:0/1:0:0:0/block/sdb/sdb1") 58 (expect= e.action :add) 59 (expect= e (parse-event (e:format))))) 60 61 (example 62 "An event can match against terms" 63 (let [terms {:devname "foo" :partname "my-usbstick"}] 64 (expect= (: (parse-event "add@/\0SEQNUM=1") :matches? terms) false) 65 (expect= (: (parse-event "add@/\0DEVNAME=bill") :matches? terms) false) 66 (expect= (: (parse-event "add@/\0DEVNAME=foo\nPARTNAME=my-usbstick") :matches? terms) true) 67 (expect= (: (parse-event "add@/\0DEVNAME=foo\nPARTNAME=my-usbstick\nOTHERTHING=bar") :matches? terms) true) 68 )) 69 70 (example 71 "given an empty database, searching it finds no entries" 72 (let [db (database)] 73 (expect= (db:find {:partname "boot"}) []))) 74 75 (example 76 "when I add a device, I can find it" 77 (let [db (database)] 78 (db:add sda-uevent) 79 (let [[m & more] (db:find {:devname "sda"})] 80 (expect= m.properties.devname "sda") 81 (expect= m.properties.major "8") 82 (expect= more [])))) 83 84 (example 85 "when I add a device, I cannot find it with wrong terms" 86 (let [db (database)] 87 (db:add sda-uevent) 88 (expect= (db:find {:devname "sdb"}) []))) 89 90 (example 91 "when I add a device, I can retrieve it by path" 92 (let [db (database)] 93 (db:add sda-uevent) 94 (let [m (db:at-path "/devices/pci0000:00/0000:00:13.0/usb1/1-1/1-1:1.0/host0/target0:0:0/0:0:0:0/block/sda")] 95 (expect= m.properties.devname "sda") 96 (expect= m.properties.major "8")))) 97 98 (example 99 "when I add and then remove a device, I cannot retrieve it by path" 100 (let [db (database)] 101 (db:add sdb1-insert) 102 (db:add sdb1-remove) 103 (expect= (db:at-path "/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/host1/target1:0:0/1:0:0:0/block/sdb/sdb1") nil))) 104 105 (example 106 "when I add and then remove a device, I cannot find it" 107 (let [db (database)] 108 (db:add sdb1-insert) 109 (db:add sda-uevent) 110 (db:add sdb1-remove) 111 (expect= (db:find {:devname "/dev/sdb1"}) []))) 112 113 (example 114 "when I search on multiple terms it uses all of them" 115 (let [db (database)] 116 (db:add sda-uevent) 117 (expect= (# (db:find {:devname "sda" :devtype "disk"})) 1) 118 (expect= (# (db:find {:devname "sda" :devtype "dosk"})) 0))) 119 120 (example 121 "I can find a device with sysfs attributes" 122 (let [db (database {:sys-path "./fixtures/sys"}) 123 path "/devices/pci0000:00/0000:00:14.0/usb1" 124 e (.. "add@" path "\0ACTION=add\n" 125 (with-open [f (io.open (.. "fixtures/sys" path "/uevent"))] 126 (f:read "*a")))] 127 (db:add e) 128 (let [[m & more] (db:find {:devtype "usb_device" :attr {:idVendor "1d6b" }})] 129 (expect= m.properties.driver "usb") 130 (expect= m.properties.major "189") 131 (expect= more [])) 132 (let [[m & more] (db:find {:devtype "usb_device" :attr {:idVendor "0000" }})] 133 (expect (not m))) 134 )) 135 136 (example 137 "I can find a device matching ancestor sysfs attributes" 138 (let [db (database {:sys-path "./fixtures/sys"}) 139 path "/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.1" 140 e (.. "add@" path "\0ACTION=add\n" 141 (with-open [f (io.open (.. "fixtures/sys" path "/uevent"))] 142 (f:read "*a")))] 143 (db:add e) 144 (let [[m & more] (db:find {:devtype "usb_interface" 145 :driver "uvcvideo" 146 :attrs {:idVendor "13d3" 147 :manufacturer "Azurewave" 148 }})] 149 (expect= m.properties.driver "uvcvideo") 150 (expect= m.properties.interface "14/2/0") 151 (expect= more [])) 152 153 (let [[m & more] (db:find {:devtype "usb_interface" 154 :driver "uvcvideo" 155 :attrs {:idVendor "23d3" 156 :manufacturer "Azurewave" 157 }})] 158 (expect (not m))))) 159 160 ;;; tests for indices 161 162 (example "when I add a device with $attributes major minor foo bar baz, 163 it is added to indices for foo bar baz but not major minor") 164 165 (example "a removed device can no longer be found by looking in any index") 166 167 (example "when I query with multiple attributes, the search is performed using the most specific attribute" 168 ;; (= the attribute whose 169 ;; value at this key has fewest elements) 170 ) 171 172 ;;; tests for subscriptions 173 174 (example 175 "I can subscribe to some search terms and be notified of matching events" 176 (var received []) 177 (let [db (database) 178 subscriber (fn [e] (table.insert received e))] 179 (db:subscribe :me subscriber {:devname "/dev/sdb1"}) 180 (db:add sdb1-insert) 181 (db:add sda-uevent) 182 (db:add sdb1-remove) 183 (expect= (# received) 2))) 184 185 (example 186 "Subscribers get notifications of prior events for present devices" 187 (var received []) 188 (let [db (database) 189 subscriber (fn [e] (table.insert received e))] 190 (db:add sdb1-insert) 191 (db:add sda-uevent) 192 (db:subscribe :me subscriber {:devname "/dev/sdb1"}) 193 (expect= (# received) 1))) 194 195 (example 196 "I can unsubscribe after subscribing" 197 (var received []) 198 (let [db (database) 199 subscriber (fn [e] (table.insert received e))] 200 (db:subscribe :me subscriber {:devname "/dev/sdb1"}) 201 (db:unsubscribe :me) 202 (db:add sdb1-insert) 203 (db:add sda-uevent) 204 (db:add sdb1-remove) 205 (expect= (# received) 0))) 206 207 208 ;;; test for event loop 209 210 (example 211 "I can register a fd with a callback" 212 (let [loop (event-loop) 213 cb #(print $1)] 214 (loop:register 3 cb) 215 (expect= (. (loop:_tbl) 3) cb))) 216 217 (example 218 "when the fd is ready, my callback is called" 219 (let [loop (event-loop)] 220 (var ran? false) 221 (loop:register 3 #(set ran? true)) 222 (loop:feed {3 1}) 223 (expect= ran? true) 224 )) 225 226 (example 227 "when the callback returns true it remains registered" 228 (let [loop (event-loop)] 229 (loop:register 3 #true) 230 (loop:feed {3 1}) 231 (expect (. (loop:_tbl) 3)) 232 )) 233 234 (fn new-fd [] 235 (ll.open "/dev/zero" 0 0x1ff)) 236 237 (example 238 "when the callback returns false it is unregistered and the fd is closed" 239 (let [loop (event-loop) 240 fd (new-fd)] 241 (expect (> fd 2)) 242 (loop:register 3 #false) 243 (loop:feed {3 1}) 244 (expect (not (. (loop:_tbl) 3))) 245 (assert (not (os.execute (string.format "test -e /dev/fd/%d" fd)))) 246 )) 247 248 249 (example 250 "parse terms from string" 251 (expect= (parse-terms "foo=bar baz=quuz") 252 {:foo "bar" :baz "quuz" :attr {} :attrs {}}) 253 (expect= (parse-terms "foo=bar attr.womble=0x1234") 254 {:foo "bar" :attr {:womble "0x1234"} :attrs {}})) 255 256 (if failed (os.exit 1) (print "OK"))