
VaultTest.pudu
Pudu126 lines4.8 KB
1/** @Test.Package.VaultTest.Suite — the wiki vault mirrors the source tree */2module Package.VaultTest34import Std.Io as Io5import Std.List as List6import Std.Result as Result7import Std.Test as Test8import Std.Text as Text91011fn files(directory: Str, ending: Str) -> Array[Str] {12 var found: Array[Str] = []13 for path in List.sortBy(&Result.unwrapOr(Io.listPaths(directory), []), fn(left: Str, right: Str) -> Bool { left < right }) {14 if path.endsWith(ending) {15 found = found.push(path)16 } else if !path.contains(".") {17 found = found.concat(files(path, ending))18 }19 }20 found21}222324fn read(path: Str) -> Str { Result.unwrapOr(Io.read(path), "") }252627fn pageOf(source: Str) -> Str { "wiki/" + source.replace(".pudu", ".md") }282930fn sourceOf(page: Str) -> Str { Text.stripPrefix(page, "wiki/").replace(".md", ".pudu") }313233fn nameOf(page: Str) -> Str { Text.stripPrefix(page, "wiki/").replace(".md", "") }343536fn bodyOf(text: Str) -> Str {37 match List.first(&text.split("\n## Referenced by")) {38 case Some(body) => body39 case None => text40 }41}424344fn backlinksOf(text: Str) -> Str {45 match List.get(&text.split("\n## Referenced by"), 1) {46 case Some(section) => section47 case None => ""48 }49}505152fn links(text: Str) -> Array[Str] {53 var found: Array[Str] = []54 for piece in List.drop(&text.split("[["), 1) {55 if let Some(inside) = List.first(&piece.split("]]")) {56 if let Some(target) = List.first(&inside.split("|")) {57 if let Some(page) = List.first(&target.split("#")) { found = found.push(page) }58 }59 }60 }61 found62}636465fn exportedFunctions(source: Str) -> Array[Str] {66 var names: Array[Str] = []67 for line in read(source).split("\n") {68 if line.startsWith("export fn ") {69 let rest = Text.stripPrefix(line, "export fn ")70 if let Some(head) = List.first(&rest.split("(")) {71 if let Some(name) = List.first(&head.split("[")) { names = names.push(name) }72 }73 }74 }75 names76}777879const ROOT: Str = "00-INDEX"808182fn main() -> Int {83 let sources = files("src", ".pudu")84 let pages = files("wiki", ".md")85 let mirrored = files("wiki/src", ".md").filter(|page: Str| !page.endsWith("_MOC.md"))86 let names = pages.map(|page: Str| nameOf(page))87 let unmirrored = sources.filter(|source: Str| !List.contains(&mirrored, pageOf(source)))88 let unbacked = mirrored.filter(|page: Str| !List.contains(&sources, sourceOf(page)))89 let ungrilled = mirrored.filter(|page: Str| !read(page).contains("## Grill Log\n\n- **Q:**"))90 var unsigned: Array[Str] = []91 for source in sources {92 let page = read(pageOf(source))93 for name in exportedFunctions(source) {94 if !page.contains("export fn " + name + "(") && !page.contains("export fn " + name + "[") { unsigned = unsigned.push(source + ": " + name) }95 }96 }97 var broken: Array[Str] = []98 var missingBacklinks: Array[Str] = []99 for page in pages {100 let text = read(page)101 for target in links(bodyOf(text)) {102 if !List.contains(&names, target) {103 broken = broken.push(nameOf(page) + " -> " + target)104 } else if target != nameOf(page) && target != ROOT && !backlinksOf(read("wiki/" + target + ".md")).contains("[[" + nameOf(page) + "]]") {105 missingBacklinks = missingBacklinks.push(target + " <- " + nameOf(page))106 }107 }108 }109 let checks = Test.suite("Package.Vault", &[110 Test.that("the vault and the source tree are found", sources.length() > 1 && mirrored.length() > 1),111 Test.equals("every source file has its mirrored page", &unmirrored, &[]),112 Test.equals("every mirrored page has its source file", &unbacked, &[]),113 Test.equals("every mirrored page has a resolved Grill Log", &ungrilled, &[]),114 Test.equals("every exported function appears in its page's signatures", &unsigned, &[]),115 Test.equals("every link names a page", &broken, &[]),116 Test.equals("every page lists the pages linking to it", &missingBacklinks, &[]),117 Test.equals("a link keeps its target and drops its alias and heading", &links("see [[a/B|b]] and [[c#Grill Log]]"), &["a/B", "c"]),118 Test.equals("a function's name stops at its parameters", &exportedFunctions("src/PuduLangResilience/Utils/Template.pudu"), &["fill"])119 ])120 let ran = Test.run(&checks)121 for failure in Test.failuresOf(&ran) {122 let _reported = Io.writeErrorLine(failure)123 }124 Test.report(&ran)125}126