Test.hs
Centralized test suite validating configuration files, metadata databases, and module unit tests
Read this when Use this page when tracing the Haskell build pipeline, generators, metadata code, or backend utility behavior around Test.
Overview
Test.hs is the unified test harness for the gwern.net Haskell backend. Rather than scattering tests across individual modules (which would incur per-call overhead and risk quadratic behavior when validating lists), this module consolidates all validation into a single testAll entry point that runs during the build process.
The test suite validates three categories of concerns: (1) configuration file integrity—checking for duplicates, valid URLs, proper formats, and absence of cycles in rewrite databases; (2) regex pattern validity—ensuring all regex strings compile without errors; and (3) module-level unit tests—verifying that individual functions like cleanAbstractsHTML, titleCase, and tooltipToMetadata produce expected outputs. The design philosophy prioritizes catching configuration errors early, since many configs are hand-edited and prone to typos, duplicates, or format mistakes.
Test.hs imports from virtually every other module in the codebase, making it both a validation layer and an implicit documentation of cross-module dependencies. When tests fail, they print red error messages identifying exactly which config or test case failed; when they pass, they print green status messages with counts of verified items.
Public API
testAll :: IO ()
Main entry point that runs all tests sequentially. Prints colored output indicating pass/fail status.
Called by: hakyll.hs (build system), manual invocation
Calls: Every test function in the module, plus functions from 20+ other modules
testConfigs :: Int
Pure function that forces centralized configuration sanity checks and returns an accounting value used by testAll output. The source comments warn not to interpret this Int as a pass/fail error count; failed checks abort or report through their own validation paths.
Called by: testAll
Calls: isUniqueList, isUniqueKeys, isUniqueAll, isCycleLess, ensure, isDomainT, isURL* family
testXOTD :: IO Int
Validates the "X of the Day" quote and site databases for format correctness and uniqueness.
Called by: testAll
Calls: XOTD.readTTDB, isUniqueKeys3, ensure
testRegexPatterns :: [String] -> IO ()
Attempts to compile each regex pattern, printing errors for any that fail.
Called by: testAll
Calls: makeRegexM
Internal Architecture
Test Organization
testAll
├── Link Icon Tests (linkIconTest)
├── Interwiki Tests (interwikiTestSuite, interwikiCycleTestSuite)
├── Config Validation (testConfigs)
│ ├── Uniqueness checks (isUniqueList, isUniqueKeys, isUniqueAll)
│ ├── Format validation (ensure with predicates)
│ └── Cycle detection (isCycleLess)
├── Regex Validation (testRegexPatterns)
├── Metadata Database Tests
│ ├── readLinkMetadata
│ ├── readArchiveMetadataAndCheck
│ └── URL hash uniqueness
├── Unit Test Suites
│ ├── cleanAbstractsHTML
│ ├── titleCase
│ ├── authorCollapse
│ ├── tooltipToMetadata
│ └── many more...
└── Live Tests (Wikipedia API calls)
Validation Helpers from Unique Module
isUniqueList: Checks a list has no duplicate elementsisUniqueKeys: Checks a list of tuples has unique first elementsisUniqueAll: Checks both keys and values are uniqueisUniqueKeys3/isUniqueKeys4: For 3-tuples and 4-tuples
The ensure Pattern
ensure :: (Show a, NFData a) => String -> String -> (a -> Bool) -> [a] -> [a]
Checks every item of a list against a predicate (defined in Utils.hs). Used extensively:
ensure "Test.linkIDOverrides" "isURLAny" isURLAny Config.LinkID.linkIDOverrides
Returns the list unchanged if all items pass; if any item fails, evaluation aborts with an error naming the location, the property, and the offending item.
Key Patterns
Centralized Config Testing
The design deliberately avoids testing configs at point-of-use:
-- Run pure config sanity checks in one place rather than in the hot paths
-- that consume each config. Many checks scan whole lists or maps. Embedding
-- them in link conversion, filtering, or cleanup functions would repeat that
-- scan for every input element, quietly turning an otherwise-linear pass
-- into an accidental quadratic one.
This means adding a new config requires manually adding its test to testConfigs.
Colored Output Protocol
printGreen ("Testing link icon matches…" :: String)
unless (null linkIconTest) $ printRed ("Link icon rules have errors in: " ++ show linkIconTest)
Green messages indicate test categories being run; red indicates failures. Success is silent beyond the category announcement.
Cycle Detection in Rewrite Databases
Several configs (author canonicalization, tag rewrites, interwiki redirects) could create infinite loops if entries reference each other cyclically:
length $ isCycleLess Config.Metadata.Author.canonicals
length $ isCycleLess Config.Tags.tagsShort2LongRewrites
Live API Tests
The Wikipedia tests actually hit the Wikipedia API:
a <- Interwiki.isWPArticle False "https://en.wikipedia.org/wiki/George_Washington_XYZ"
b <- Interwiki.isWPArticle False "https://en.wikipedia.org/wiki/George_Washington"
c <- Interwiki.isWPDisambig "Mercury"
These verify that article-existence and disambiguation checking works correctly.
Configuration
Test.hs itself has no configuration. It tests configs from these modules:
| Config Module | What's Tested |
|---|---|
Config.GenerateSimilar | URL blacklists are valid URLs |
Config.Interwiki | Rewrites, redirects, quote overrides |
Config.LinkArchive | Whitelist matches, localization test cases |
Config.LinkIcon | Icon rules, blacklists |
Config.LinkLive | Domain whitelists/blacklists |
Config.LinkSuggester | Bad anchor strings, whitelists |
Config.Tags | Tag mappings, rewrites |
Config.Typography | Title case rules, date ranges |
Config.XOfTheDay | Site/quote databases |
Config.Inflation | Bitcoin exchange rates, test cases |
Config.LinkID | ID overrides, affiliation anchors |
Config.LinkMetadata | URI validation exemptions and previewable extension lists |
Config.Utext | Raw Markdown-to-Utext rendering cases |
Config.Metadata.* | Author, Format, Title configs |
Config.Misc | Tooltips, cycles, arxiv abstracts |
Config.Paragraph | Paragraph whitelist |
Integration Points
Imports (Tests From)
Test.hs imports test functions from these modules:
Annotation.tooltipToMetadata,testGuessAuthorDateCycle.testCycleDetectionInflation.inflationDollarTestSuiteInterwiki.interwikiTestSuite,interwikiCycleTestSuiteLinkArchive.testLinkRewritesGenerateSimilar.generateSimilarTestSuiteLinkIcon.linkIconTestLinkLive.linkLiveTest,linkLivePrioritizeTags.testTagsTypography.titleCaseTestConfig.LinkMetadata.fileTranscludesTest(run againstLinkMetadata.generateFileTransclusionBlock)Utext.rawMarkdown2Utext,rawMarkdown2UtextStyledMetadata.Author.authorCollapseTest,cleanAuthorsTestMetadata.Format.printDoubleTestSuite,cleanAbstractsHTMLTestMetadata.Date.dateRangeDurationTestCasesTestsuite
Working Directory
testAll = do Config.Misc.cd -- Changes to project root
Tests assume they run from the project root directory.
Exit Behavior
Unit-test failures don't exit—they print red errors and continue, so the build system must check output separately. However, the pure config sanity checks (ensure, the Unique helpers, isCycleLess) abort with error when forced, so a bad config entry does crash the test run.
What Is Tested
Configuration Integrity
- Uniqueness: No duplicate entries in lists, no duplicate keys in maps
- URL Validity: URLs are actually URLs (
isURL,isURLT,isURLAny) - Domain Validity: Domain strings are valid domains (
isDomainT) - Cycle Freedom: Rewrite chains don't loop infinitely
- Format Correctness: HTML identifiers start with letters, dates are valid dates
Regex Validity
All regex patterns from configs are compiled to catch syntax errors:
footnoteRegex,sectionAnonymousRegex,badUrlRegex- Tag rewrite regexes
- Author cleanup regexes
- HTML rewrite regexes
- Arxiv abstract regexes
Unit Test Suites
| Function | Config Source |
|---|---|
balanced | balancedBracketTestCases |
cleanAbstractsHTML | htmlRewriteTestCases |
extractTwitterUsername | extractTwitterUsernameTestSuite |
tooltipToMetadata | tooltipToMetadataTestcases |
titleCase | titleCaseTestCases |
authorCollapse | authorCollapseTestCases |
fileTranscludes | Generated from metadata |
Database Integrity
- Link Metadata: URL hashes are unique, IDs are valid
- Archive Metadata: Checked via
readArchiveMetadataAndCheck - X-of-the-Day: Quote/site DBs have valid format
See Also
- hakyll.hs - Build system that invokes tests during SLOW builds
- Utils.hs - Utility functions with unit tests
- sync.sh - Build orchestrator controlling test execution
- LinkMetadata.hs - Metadata database being validated
- Typography.hs - Typography functions with unit tests
- Cycle.hs - Cycle detection tested here