This PR sets up the new integrated test/bench suite. It then migrates all benchmarks and some related tests to the new suite. There's also some documentation and some linting. For now, a lot of the old tests are left alone so this PR doesn't become even larger than it already is. Eventually, all tests should be migrated to the new suite though so there isn't a confusing mix of two systems.
88 lines
2.8 KiB
Text
88 lines
2.8 KiB
Text
import Lean.Data.Trie
|
||
|
||
/-!
|
||
|
||
# Tests for the trie data structure
|
||
|
||
This test tests the `Lean.Parser.Trie` data structure by bisimulation with a simple `Array String`:
|
||
It performs a sequence of trie creation steps, and after each steps checks
|
||
whether the trie is operationally equivalent to the array of strings.
|
||
|
||
This test does not bother with values that are different than they `String` they are stored under.
|
||
This test does not test `upsert`; since `Trie.insert` goes through it, it should be sufficient
|
||
(and it would make this test approach more complicated.)
|
||
-/
|
||
|
||
open Lean.Data
|
||
|
||
/-- These keys used in `T.check` below. Also include keys for negative lookup tests here! -/
|
||
def keys : Array String := #[
|
||
"",
|
||
"h",
|
||
"hello",
|
||
"helloo",
|
||
"hellooo",
|
||
"helloooooo",
|
||
"hella",
|
||
"hellx",
|
||
"hö",
|
||
"hü",
|
||
"hä",
|
||
"💩"
|
||
]
|
||
|
||
/-- A trie together with a reference value as an array of values -/
|
||
def T := Trie String × Array String
|
||
|
||
def T.empty : T := (.empty, .empty)
|
||
|
||
def T.insert : T → String → T := fun (t,a) s =>
|
||
(t.insert s s, if a.contains s then a else a.push s)
|
||
|
||
/-- A convenience function for use in this test case -/
|
||
def Array.sorted : Array String → Array String := fun a =>
|
||
a.qsort (fun s1 s2 => s1 < s2)
|
||
|
||
/-- The intended semanics of `Trie.findPrefix` -/
|
||
def Array.findPrefix : Array String → String → Array String := fun a s =>
|
||
a.filter (fun s' => s.isPrefixOf s')
|
||
|
||
/-- The intended semanics of `Trie.matchPrefix`: Longest prefix found in trie -/
|
||
def Array.matchPrefix : Array String → String → Option String := fun a s => Id.run do
|
||
for i in List.reverse (List.range (s.length + 1)) do
|
||
let pfix := s.take i |>.copy
|
||
if let some _ := a.find? (· == pfix) then
|
||
return some pfix
|
||
return none
|
||
|
||
|
||
def T.check : T → IO Unit := fun (t,a) => do
|
||
-- Check lookup equivalence
|
||
keys.forM fun s => do
|
||
unless t.find? s = a.find? (· == s) do
|
||
IO.println s!"find? differs: key = {s}"
|
||
-- Check findPrefix equivalence
|
||
keys.forM fun s => do
|
||
unless (t.findPrefix s).sorted = (a.findPrefix s).sorted do
|
||
IO.println s!"findPrefix differs: key = {s}"
|
||
-- Check matchPrefix equivalence
|
||
keys.forM fun s => do
|
||
unless t.matchPrefix s 0 = a.matchPrefix s do
|
||
IO.println s!"matchPrefix differs: key = {s}, got: {t.matchPrefix s 0} exp: {a.matchPrefix s} "
|
||
let s' := "somePrefix" ++ s
|
||
unless t.matchPrefix s' ((0 : String.Pos.Raw) + "somePrefix") = a.matchPrefix s do
|
||
IO.println s!"matchPrefix differs (with prefix): key = {s}"
|
||
|
||
def main : IO Unit := do
|
||
-- Add tricky insert sequences here:
|
||
for seq in #[
|
||
#["hello", "hella", "hellooo", "h", "hö", "hü", "💩", "", "hü"],
|
||
#["", "helooooo"]
|
||
] do
|
||
IO.println "Resetting trie"
|
||
let mut t : T := T.empty
|
||
t.check
|
||
for s in seq do
|
||
IO.println s!"Inserting {s}"
|
||
t := t.insert s
|
||
t.check
|