feat(fsharp): m01-m06 util, model, rules, random, spawn, game, view with Expecto tests
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
183821737e
commit
9cbefeb2be
@@ -0,0 +1,54 @@
|
||||
module Snake.Tests.Purity
|
||||
|
||||
open System.IO
|
||||
open System.Text.RegularExpressions
|
||||
open Expecto
|
||||
|
||||
let private srcDir = Path.Combine(Directory.GetCurrentDirectory(), "src")
|
||||
|
||||
// The one file allowed to touch the outside world (the browser glue lives in web/, outside src/).
|
||||
let private excluded (file: string) = Path.GetFileName(file) = "Runtime.fs"
|
||||
|
||||
let private banned: (string * Regex * string) list =
|
||||
[ "mutable", Regex(@"\bmutable\b"), "let mutable n = 0"
|
||||
"<-", Regex(@"<-"), "n <- n + 1"
|
||||
"System.Random", Regex(@"\bSystem\.Random\b"), "let r = System.Random()"
|
||||
"DateTime", Regex(@"\bDateTime\b"), "let t = System.DateTime.Now"
|
||||
"printf", Regex(@"\bprintf"), "printfn \"hi\""
|
||||
"ref ", Regex(@"\bref "), "let cell = ref 0" ]
|
||||
|
||||
// Block comments `(* ... *)` and line comments (`//`, `///`) are removed before scanning.
|
||||
let private stripComments (code: string) =
|
||||
Regex.Replace(Regex.Replace(code, @"(?s)\(\*.*?\*\)", ""), @"(?m)//.*$", "")
|
||||
|
||||
let private violations (code: string) =
|
||||
let clean = stripComments code
|
||||
[ for name, re, _ in banned do
|
||||
if re.IsMatch clean then
|
||||
yield name ]
|
||||
|
||||
let private fsFiles () =
|
||||
if Directory.Exists srcDir then
|
||||
Directory.GetFiles(srcDir, "*.fs", SearchOption.AllDirectories) |> Array.sort |> List.ofArray
|
||||
else
|
||||
[]
|
||||
|
||||
[<Tests>]
|
||||
let tests =
|
||||
testList "Purity" [
|
||||
for name, _, sample in banned do
|
||||
yield testCase $"purity scanner flags a known-bad sample using {name}" (fun () ->
|
||||
Expect.contains (violations sample) name "flagged")
|
||||
|
||||
yield testCase "purity scanner ignores banned words inside comments" (fun () ->
|
||||
let code = "// let mutable x\n(* x <- 1; printfn \"hi\" *)\nlet a = 1 // System.Random DateTime ref x\n"
|
||||
Expect.equal (violations code) [] "no violations")
|
||||
|
||||
yield testCase "purity scanner accepts ordinary pure code" (fun () ->
|
||||
Expect.equal (violations "let f xs = List.map (fun x -> x + 1) xs") [] "no violations")
|
||||
|
||||
for f in fsFiles () do
|
||||
if not (excluded f) then
|
||||
yield testCase $"purity: {Path.GetRelativePath(srcDir, f)} has no banned constructs" (fun () ->
|
||||
Expect.equal (violations (File.ReadAllText f)) [] "violations")
|
||||
]
|
||||
Reference in New Issue
Block a user