spb/metrika Public
Stata-class statistics, GPU-accelerated by Apple Silicon. Native Swift — no Electron, no Python runtime, no compromises.
Swift 92.4%
HTML 3.3%
R 3%
Shell 1.3%
1//2// EngineTests.swift3// Metrika4//5// Author: Simon-Pierre Boucher6// Contact: contact@spboucher.ai7// Copyright © 2026 Simon-Pierre Boucher. All rights reserved.8//910import Testing11import ZQEngine1213/// End-to-end tests: full command lines through the session, checking the14/// returned scalar results against the R fixtures.15@Suite("ZQEngine end-to-end", .serialized)16struct EngineTests {17 let fixtures: Fixtures18 let session: ZQSession1920 init() async throws {21 self.fixtures = try Fixtures()22 self.session = try ZQSession(discoverUserCommands: false)23 _ = try await session.execute("use \(fixtures.datasetURL.path)")24 _ = try await session.execute("gen log_rev = ln(revenue)")25 }2627 @Test("use reports dataset dimensions")28 func loadedDimensions() async throws {29 let result = try await session.execute("count")30 #expect(result.scalars["N"] == 60)31 }3233 @Test("sysuse loads bundled samples and lists them")34 func sysuse() async throws {35 let fresh = try ZQSession(discoverUserCommands: false)36 let listing = try await fresh.execute("sysuse")37 #expect(listing.text.contains("sales"))38 let sales = try await fresh.execute("sysuse sales")39 #expect(sales.scalars["N"] == 1000)40 let reg = try await fresh.execute("reg revenue price")41 #expect(reg.scalars["N"] == 1000)42 let cars = try await fresh.execute("sysuse mtcars")43 #expect(cars.scalars["N"] == 32)44 await #expect(throws: ZQEngineError.self) {45 _ = try await fresh.execute("sysuse nosuchsample")46 }47 }4849 @Test("regress with listwise deletion matches R")50 func regress() async throws {51 let result = try await session.execute("reg log_rev price")52 #expect(result.scalars["N"] == fixtures["ols_n"])53 expectClose(try #require(result.scalars["b_price"]), fixtures["ols_b_price"], "b[price]")54 expectClose(try #require(result.scalars["b__cons"]), fixtures["ols_b_cons"], "b[_cons]")55 expectClose(56 try #require(result.scalars["se_price"]),57 fixtures["ols_se_classical_price"], "se[price]"58 )59 expectClose(try #require(result.scalars["r2"]), fixtures["ols_r2"], "R²")60 #expect(result.text.contains("observations dropped due to missing values"))61 }6263 @Test("regress, robust matches R HC1")64 func regressRobust() async throws {65 let result = try await session.execute("reg log_rev price, robust")66 expectClose(67 try #require(result.scalars["se_price"]),68 fixtures["ols_se_hc1_price"], "robust se[price]"69 )70 }7172 @Test("regress, cluster matches R with Stata small-sample factor")73 func regressCluster() async throws {74 let result = try await session.execute("reg log_rev price, cluster(firm_id)")75 #expect(result.scalars["N_clust"] == fixtures["ols_G"])76 expectClose(77 try #require(result.scalars["se_price"]),78 fixtures["ols_se_cluster_price"], "cluster se[price]"79 )80 }8182 @Test("factor variable expansion i.region matches R factor()")83 func regressFactor() async throws {84 let result = try await session.execute("reg log_rev price i.region")85 expectClose(try #require(result.scalars["b_price"]), fixtures["ols2_b_price"], "b[price]")86 expectClose(87 try #require(result.scalars["b_2.region"]),88 fixtures["ols2_b_region2"], "b[2.region]"89 )90 expectClose(91 try #require(result.scalars["b_3.region"]),92 fixtures["ols2_b_region3"], "b[3.region]"93 )94 expectClose(try #require(result.scalars["b__cons"]), fixtures["ols2_b_cons"], "b[_cons]")95 expectClose(try #require(result.scalars["r2"]), fixtures["ols2_r2"], "R²")96 }9798 @Test("summarize scalars match R")99 func summarize() async throws {100 let result = try await session.execute("summarize revenue")101 expectClose(102 try #require(result.scalars["mean"]),103 fixtures["sum_revenue_mean"], "mean"104 )105 expectClose(try #require(result.scalars["sd"]), fixtures["sum_revenue_sd"], "sd")106 }107108 @Test("if qualifier and count")109 func conditionalCount() async throws {110 let all = try await session.execute("count")111 let some = try await session.execute("count if missing(price)")112 #expect(all.scalars["N"] == 60)113 #expect(some.scalars["N"] == fixtures["ols_dropped"])114 }115116 @Test("bootstrap is reproducible for a fixed seed")117 func bootstrapReproducible() async throws {118 let first = try await session.execute(119 "bootstrap, reps(200) seed(42): reg log_rev price"120 )121 let second = try await session.execute(122 "bootstrap, reps(200) seed(42): reg log_rev price"123 )124 #expect(first.scalars["se_price"] == second.scalars["se_price"])125 // Bootstrap SE should be in the neighborhood of the analytic one.126 let analytic = fixtures["ols_se_classical_price"]127 let bootstrap = try #require(first.scalars["se_price"])128 #expect(bootstrap > analytic / 3 && bootstrap < analytic * 3)129 }130131 @Test("generate honors if, replace counts changes")132 func generateReplace() async throws {133 _ = try await session.execute("gen flag = 1 if price > 10")134 let count = try await session.execute("count if flag == 1")135 let expected = try await session.execute("count if price > 10")136 #expect(count.scalars["N"] == expected.scalars["N"])137 let replaced = try await session.execute("replace flag = 0 if missing(flag)")138 #expect(replaced.text.contains("real changes"))139 _ = try await session.execute("drop flag")140 }141142 @Test("graph scatter produces a plot spec")143 func graphScatter() async throws {144 _ = try await session.execute("graph scatter log_rev price, by(region)")145 let plot = try #require(await session.lastPlot)146 #expect(plot.kind == .scatter)147 #expect(plot.series.count == 3) // three regions148 #expect(plot.xLabel == "price")149 }150151 @Test("logit through the console matches R")152 func logitCommand() async throws {153 let result = try await session.execute("logit purchase price")154 expectClose(155 try #require(result.scalars["b_price"]), fixtures["logit_b_price"], "b[price]"156 )157 expectClose(try #require(result.scalars["ll"]), fixtures["logit_ll"], "ll")158 #expect(result.text.contains("Logistic regression"))159 #expect(result.text.contains("Pseudo R2"))160 }161162 @Test("poisson with robust SE through the console")163 func poissonCommand() async throws {164 let result = try await session.execute("poisson orders price, robust")165 expectClose(166 try #require(result.scalars["b_price"]), fixtures["pois_b_price"], "b[price]"167 )168 expectClose(169 try #require(result.scalars["se_price"]),170 fixtures["pois_se_hc0_price"], "robust se[price]"171 )172 }173174 @Test("tabulate one-way counts region levels")175 func tabulateOneWay() async throws {176 let result = try await session.execute("tab region")177 #expect(result.scalars["N"] == 60)178 #expect(result.scalars["r"] == 3)179 #expect(result.text.contains("Total"))180 // region cycles 1,2,3 over 60 rows → 20 each.181 #expect(result.text.contains("20"))182 }183184 @Test("tabulate two-way region by purchase")185 func tabulateTwoWay() async throws {186 let result = try await session.execute("tabulate region purchase")187 #expect(result.scalars["r"] == 3)188 #expect(result.scalars["c"] == 2)189 #expect(result.scalars["N"] == 60)190 }191192 @Test("correlate matches R and reports listwise obs")193 func correlateCommand() async throws {194 let result = try await session.execute("correlate revenue price")195 expectClose(196 try #require(result.scalars["rho"]),197 fixtures["corr_rev_price"], "cor(revenue, price)"198 )199 #expect(result.scalars["N"] == fixtures["ols_n"])200 }201202 @Test("help lists every category; help <verb> shows the entry")203 func help() async throws {204 let index = try await session.execute("help")205 for category in ZQCommandReference.categories {206 #expect(index.text.contains(category), Comment(rawValue: category))207 }208 let entry = try await session.execute("help regress")209 #expect(entry.text.contains("cluster(var)"))210 #expect(entry.text.contains("reg log_rev price, robust"))211 let byAbbreviation = try await session.execute("help reg")212 #expect(byAbbreviation.text.contains("Ordinary least squares"))213 let unknown = try await session.execute("help nosuchthing")214 #expect(unknown.text.contains("no entry"))215 }216217 @Test("every dispatched estimation and data verb has a manual entry")218 func manualCoverage() {219 let documented = Set(ZQCommandReference.all.map(\.verb))220 for verb in [221 "use", "sysuse", "save", "clear", "describe", "list", "count", "generate",222 "replace", "drop", "keep", "summarize", "tabulate", "correlate",223 "regress", "logit", "probit", "poisson", "ivregress", "xtreg",224 "xtset", "lasso", "elasticnet", "boost", "bootstrap", "permute", "predict",225 "margins", "bayes", "scatter", "histogram", "kdensity", "graph", "display",226 "set", "log", "help", "zscore",227 ] {228 #expect(documented.contains(verb), Comment(rawValue: "missing manual entry: \(verb)"))229 }230 }231232 @Test("display evaluates scalar expressions")233 func display() async throws {234 let result = try await session.execute("display 2 + 2 * 3")235 #expect(result.text == "8")236 }237238 @Test("unknown variable yields a helpful error")239 func unknownVariable() async throws {240 await #expect(throws: (any Error).self) {241 _ = try await session.execute("summarize nonexistent_var")242 }243 }244}245