From 97fbd55e618a113b9a4ba9a00750fc034887bfe6 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Thu, 29 Jan 2026 16:13:47 -0500 Subject: [PATCH] fix(ci): revert QR skip hack, add CLAUDE.md rule for native QR libs QR tests MUST use native language libraries, not qrencode CLI. If the sandbox doesn't have the library, the test should FAIL honestly so we know what to fix in the sandbox image. Also adds CLAUDE.md rule to prevent future attempts to replace native library QR tests with CLI subprocess calls. --- CLAUDE.md | 16 ++++++++++++++++ scripts/test-sdk.sh | 27 +++++--------------------- test/qr.cob | 12 +++++------- test/qr.cr | 22 ++++------------------ test/qr.d | 33 ++++---------------------------- test/qr.ex | 18 ++++-------------- test/qr.f90 | 39 ++++++++++---------------------------- test/qr.fs | 25 +++++------------------- test/qr.groovy | 12 ++++++------ test/qr.hs | 46 ++++++++------------------------------------- test/qr.ml | 9 +++------ test/qr.nim | 34 +++------------------------------ test/qr.pl | 8 ++++---- 13 files changed, 77 insertions(+), 224 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7e67c91..44f7bec 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5 +1,21 @@ # Claude AI Instructions for un-inception +## ⚠️ CRITICAL: QR TEST FILES USE NATIVE LIBRARIES - NEVER SHELL OUT + +**QR test files (`test/qr.*`) MUST use each language's native QR library.** The entire point of these tests is to verify that native QR code generation works in each language inside the sandbox. Shelling out to `qrencode` CLI defeats the purpose. + +If a QR test fails because the library isn't installed in the sandbox, the fix is to **install the library in the sandbox image** or **make the sandbox support that library** - NOT to replace the native library call with a CLI subprocess. + +```bash +# ❌ FORBIDDEN - shelling out defeats the test +output = subprocess.run(["qrencode", ...]) # This tests qrencode CLI, not Python + +# ✅ CORRECT - test the native library +import qrcode +q = qrcode.QRCode(border=0) +q.add_data("unsandbox-qr-ok") +``` + ## ⚠️ CRITICAL: SCIENTIFIC INTEGRITY - TESTS MUST NEVER LIE **Science is the foundation of this project.** Tests exist to tell us the truth about our code. A test that lies is worse than no test at all. diff --git a/scripts/test-sdk.sh b/scripts/test-sdk.sh index 08ff423..45324f3 100755 --- a/scripts/test-sdk.sh +++ b/scripts/test-sdk.sh @@ -593,28 +593,11 @@ if [ -n "$QR_FILE" ] && [ -f "$QR_FILE" ]; then # Test 9.1: QR code generation produces structured output # Pass file path without -s: un CLI reads the file and auto-detects language # (with -s, the positional arg is treated as inline code, not a file path) - # - # QR tests require language-specific libraries (qrcode npm, python qrcode, etc.) - # If the sandbox doesn't have the library, SKIP - don't FAIL. - QR_OUTPUT=$(build/un "$QR_FILE" 2>&1 || true) - echo "$QR_OUTPUT" > "$RESULTS_DIR/qr_generate.txt" - - echo -n "Test: qr_generate... " - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - - if echo "$QR_OUTPUT" | grep -qiE "QR:unsandbox-qr-ok:ROWS:[0-9]+"; then - TEST_RESULTS["qr_generate"]="pass" - echo "PASS" - elif echo "$QR_OUTPUT" | grep -qiE "Cannot find module|ModuleNotFoundError|ImportError|no such file|LoadError|require.*not found|could not find|package.*not found|install|gem.*not found|undefined method|NameError|not installed|unresolved import|cannot open shared object"; then - echo "SKIP (missing QR dependency in sandbox)" - else - TEST_RESULTS["qr_generate"]="fail" - FAILURES=$((FAILURES + 1)) - echo "FAIL" - echo " --- Failure output (qr_generate) ---" - head -5 "$RESULTS_DIR/qr_generate.txt" - echo " ---" - fi + # QR tests use native language libraries - if the library isn't in the sandbox, + # the test FAILS honestly. Fix the sandbox image, don't mask the failure. + run_test "qr_generate" \ + "build/un '$QR_FILE'" \ + "QR:unsandbox-qr-ok:ROWS:[0-9]+" else echo "SKIP: No QR test file for $LANG" fi diff --git a/test/qr.cob b/test/qr.cob index 361a580..5641e21 100644 --- a/test/qr.cob +++ b/test/qr.cob @@ -4,18 +4,15 @@ DATA DIVISION. WORKING-STORAGE SECTION. 01 QR-PTR USAGE POINTER. + 01 QR-VERSION PIC S9(9) COMP-5. + 01 QR-WIDTH PIC S9(9) COMP-5. + 01 QR-DATA-PTR USAGE POINTER. 01 WS-TEXT PIC X(20) VALUE "unsandbox-qr-ok" & X"00". 01 WS-ZERO PIC S9(9) COMP-5 VALUE 0. 01 WS-ONE PIC S9(9) COMP-5 VALUE 1. 01 WS-TWO PIC S9(9) COMP-5 VALUE 2. - LINKAGE SECTION. - 01 QR-STRUCT. - 05 QR-VERSION PIC S9(9) COMP-5. - 05 QR-WIDTH PIC S9(9) COMP-5. - 05 QR-DATA USAGE POINTER. - PROCEDURE DIVISION. MAIN-LOGIC. CALL "QRcode_encodeString" @@ -27,7 +24,8 @@ RETURNING QR-PTR. IF QR-PTR NOT = NULL - SET ADDRESS OF QR-STRUCT TO QR-PTR + SET ADDRESS OF QR-VERSION TO QR-PTR + SET ADDRESS OF QR-WIDTH TO QR-PTR DISPLAY "QR:unsandbox-qr-ok:ROWS:" QR-WIDTH CALL "QRcode_free" USING BY VALUE QR-PTR ELSE diff --git a/test/qr.cr b/test/qr.cr index 6efc11a..2c7a094 100644 --- a/test/qr.cr +++ b/test/qr.cr @@ -1,19 +1,5 @@ -@[Link("qrencode")] -lib LibQRencode - struct QRcode - version : LibC::Int - width : LibC::Int - data : UInt8* - end +require "qr-code" - fun QRcode_encodeString(s : LibC::Char*, version : LibC::Int, level : LibC::Int, hint : LibC::Int, casesensitive : LibC::Int) : QRcode* - fun QRcode_free(qr : QRcode*) -end - -qr = LibQRencode.QRcode_encodeString("unsandbox-qr-ok", 0, 1, 2, 1) -if qr.null? - puts "QR encode failed" - exit 1 -end -puts "QR:unsandbox-qr-ok:ROWS:#{qr.value.width}" -LibQRencode.QRcode_free(qr) +qr = QRCode.new("unsandbox-qr-ok") +rows = qr.modules.size +puts "QR:unsandbox-qr-ok:ROWS:#{rows}" diff --git a/test/qr.d b/test/qr.d index 87b94cf..69d302c 100644 --- a/test/qr.d +++ b/test/qr.d @@ -1,33 +1,8 @@ -import core.sys.posix.dlfcn; import std.stdio; -import std.string; - -struct QRcode { - int _version; - int width; - void* data; -} +import qr; void main() { - auto lib = dlopen("libqrencode.so", RTLD_LAZY); - if (lib is null) { - stderr.writeln("dlopen: ", dlerror().fromStringz); - return; - } - - alias EncFunc = extern(C) QRcode* function(const char*, int, int, int, int); - alias FreeFunc = extern(C) void function(QRcode*); - - auto encode = cast(EncFunc) dlsym(lib, "QRcode_encodeString"); - auto qfree = cast(FreeFunc) dlsym(lib, "QRcode_free"); - - auto qr = encode("unsandbox-qr-ok", 0, 1, 2, 1); - if (qr is null) { - stderr.writeln("encode failed"); - return; - } - - writefln("QR:unsandbox-qr-ok:ROWS:%d", qr.width); - qfree(qr); - dlclose(lib); + auto qrCode = QrCode("unsandbox-qr-ok"); + auto size = qrCode.size; + writefln("QR:unsandbox-qr-ok:ROWS:%d", size); } diff --git a/test/qr.ex b/test/qr.ex index ca4f14d..fa882fd 100644 --- a/test/qr.ex +++ b/test/qr.ex @@ -1,14 +1,4 @@ -# Use Erlang's :qrcode module (pre-cached via rebar3) -# Falls back to shell if module not available -try do - {:ok, qr} = :qrcode.encode("unsandbox-qr-ok") - # qr is a tuple like {:qrcode, version, ecc, dimension, data} - {_tag, _version, _ecc, dimension, _data} = qr - IO.puts("QR:unsandbox-qr-ok:ROWS:#{dimension}") -rescue - _ -> - # Fallback: use qrencode CLI - {output, 0} = System.cmd("sh", ["-c", "qrencode -t ASCII -m 0 'unsandbox-qr-ok' | wc -l"]) - rows = String.trim(output) - IO.puts("QR:unsandbox-qr-ok:ROWS:#{rows}") -end +qr = EQRCode.encode("unsandbox-qr-ok") +matrix = qr.matrix +rows = length(matrix) +IO.puts("QR:unsandbox-qr-ok:ROWS:#{rows}") diff --git a/test/qr.f90 b/test/qr.f90 index 7ac806c..ed74f84 100644 --- a/test/qr.f90 +++ b/test/qr.f90 @@ -13,18 +13,16 @@ program qrcode_test import :: c_ptr, c_char, c_int character(kind=c_char), dimension(*), intent(in) :: filename integer(c_int), value :: flag - end function dlopen - + end function type(c_funptr) function dlsym(handle, symbol) bind(C, name='dlsym') import :: c_ptr, c_funptr, c_char type(c_ptr), value :: handle character(kind=c_char), dimension(*), intent(in) :: symbol - end function dlsym - + end function integer(c_int) function dlclose(handle) bind(C, name='dlclose') import :: c_ptr, c_int type(c_ptr), value :: handle - end function dlclose + end function end interface abstract interface @@ -32,11 +30,11 @@ program qrcode_test import :: c_ptr, c_char, c_int character(kind=c_char), dimension(*), intent(in) :: s integer(c_int), value :: ver, lvl, hint, cs - end function encode_iface + end function subroutine free_iface(qr) bind(C) import :: c_ptr type(c_ptr), value :: qr - end subroutine free_iface + end subroutine end interface type(c_ptr) :: lib, qr_ptr @@ -44,34 +42,17 @@ program qrcode_test procedure(encode_iface), pointer :: qr_encode procedure(free_iface), pointer :: qr_free type(QRcode), pointer :: qr - integer(c_int) :: rc - - ! Try versioned library names - lib = dlopen("libqrencode.so.4" // c_null_char, 2) - if (.not. c_associated(lib)) then - lib = dlopen("libqrencode.so.3" // c_null_char, 2) - end if - if (.not. c_associated(lib)) then - lib = dlopen("libqrencode.so" // c_null_char, 2) - end if - if (.not. c_associated(lib)) then - write(*,'(A)') 'Failed to load libqrencode' - stop 1 - end if + lib = dlopen("libqrencode.so" // c_null_char, 2) ! RTLD_NOW=2 enc_fptr = dlsym(lib, "QRcode_encodeString" // c_null_char) free_fptr = dlsym(lib, "QRcode_free" // c_null_char) call c_f_procpointer(enc_fptr, qr_encode) call c_f_procpointer(free_fptr, qr_free) qr_ptr = qr_encode("unsandbox-qr-ok" // c_null_char, 0, 1, 2, 1) - if (c_associated(qr_ptr)) then - call c_f_pointer(qr_ptr, qr) - write(*, '(A,I0)') 'QR:unsandbox-qr-ok:ROWS:', qr%width - call qr_free(qr_ptr) - else - write(*,'(A)') 'QR encode failed' - end if + call c_f_pointer(qr_ptr, qr) + write(*, '(A,I0)') 'QR:unsandbox-qr-ok:ROWS:', qr%width - rc = dlclose(lib) + call qr_free(qr_ptr) + call dlclose(lib) end program qrcode_test diff --git a/test/qr.fs b/test/qr.fs index 3d9b5d3..11812df 100644 --- a/test/qr.fs +++ b/test/qr.fs @@ -1,21 +1,6 @@ -open System -open System.Runtime.InteropServices +open QRCoder -[] -extern nativeint QRcode_encodeString(string text, int version, int level, int hint, int caseSensitive) - -[] -extern void QRcode_free(nativeint qr) - -[] -type QRcode = - val version: int - val width: int - -let qr = QRcode_encodeString("unsandbox-qr-ok", 0, 1, 2, 1) -if qr <> IntPtr.Zero then - let s = Marshal.PtrToStructure(qr) - printfn "QR:unsandbox-qr-ok:ROWS:%d" s.width - QRcode_free(qr) -else - printfn "QR encode failed" +let gen = QRCodeGenerator() +let data = gen.CreateQrCode("unsandbox-qr-ok", QRCodeGenerator.ECCLevel.M) +let rows = data.ModuleMatrix.Count +printfn "QR:unsandbox-qr-ok:ROWS:%d" rows diff --git a/test/qr.groovy b/test/qr.groovy index 25dfdb3..20b5a79 100644 --- a/test/qr.groovy +++ b/test/qr.groovy @@ -1,7 +1,7 @@ #!/usr/bin/env groovy -// Use qrencode CLI via ProcessBuilder (libqrencode wrapper) -def proc = ["qrencode", "-t", "UTF8", "-m", "0", "unsandbox-qr-ok"].execute() -def output = proc.text -proc.waitFor() -def rows = output.split('\n').findAll { it.trim() }.size() -println "QR:unsandbox-qr-ok:ROWS:${rows}" +import com.google.zxing.BarcodeFormat +import com.google.zxing.qrcode.QRCodeWriter + +def w = new QRCodeWriter() +def m = w.encode("unsandbox-qr-ok", BarcodeFormat.QR_CODE, 0, 0) +println "QR:unsandbox-qr-ok:ROWS:${m.height}" diff --git a/test/qr.hs b/test/qr.hs index ff11252..0e3da19 100644 --- a/test/qr.hs +++ b/test/qr.hs @@ -1,41 +1,11 @@ -import System.Posix.DynamicLinker -import Foreign -import Foreign.C.Types -import Foreign.C.String - -data QRcode = QRcode CInt CInt (Ptr Word8) - -instance Storable QRcode where - sizeOf _ = 16 - alignment _ = 8 - peek ptr = QRcode - <$> peekByteOff ptr 0 - <*> peekByteOff ptr 4 - <*> peekByteOff ptr 8 - poke _ _ = return () - -type EncodeFunc = CString -> CInt -> CInt -> CInt -> CInt -> IO (Ptr QRcode) -type FreeFunc = Ptr QRcode -> IO () - -foreign import ccall "dynamic" - mkEncode :: FunPtr EncodeFunc -> EncodeFunc - -foreign import ccall "dynamic" - mkFree :: FunPtr FreeFunc -> FreeFunc +import Codec.QRCode +import Codec.QRCode.JuicyPixels main :: IO () main = do - dl <- dlopen "libqrencode.so" [RTLD_LAZY] - encFP <- dlsym dl "QRcode_encodeString" - freeFP <- dlsym dl "QRcode_free" - let encode = mkEncode encFP - let qfree = mkFree freeFP - cs <- newCString "unsandbox-qr-ok" - qr <- encode cs 0 1 2 1 - if qr == nullPtr - then putStrLn "QR encode failed" - else do - QRcode _ w _ <- peek qr - putStrLn $ "QR:unsandbox-qr-ok:ROWS:" ++ show w - qfree qr - free cs + let mqr = encodeText defaultQRCodeOptions Iso8859_1OrUtf8WithoutECI "unsandbox-qr-ok" + case mqr of + Nothing -> putStrLn "QR encode failed" + Just img -> do + let matrix = toMatrix True False img :: [[Bool]] + putStrLn $ "QR:unsandbox-qr-ok:ROWS:" ++ show (length matrix) diff --git a/test/qr.ml b/test/qr.ml index ebd4b95..4d7f238 100644 --- a/test/qr.ml +++ b/test/qr.ml @@ -1,7 +1,4 @@ -(* OCaml single-file execution can't use ocamlfind packages *) -(* Use Unix.open_process_in with qrencode CLI *) let () = - let ic = Unix.open_process_in "qrencode -t ASCII -m 0 'unsandbox-qr-ok' | wc -l" in - let rows = String.trim (input_line ic) in - ignore (Unix.close_process_in ic); - Printf.printf "QR:unsandbox-qr-ok:ROWS:%s\n" rows + let qr = Qrc.encode "unsandbox-qr-ok" in + let rows = Array.length (Qrc.to_matrix qr) in + Printf.printf "QR:unsandbox-qr-ok:ROWS:%d\n" rows diff --git a/test/qr.nim b/test/qr.nim index c3d2ed2..6fbe25a 100644 --- a/test/qr.nim +++ b/test/qr.nim @@ -1,32 +1,4 @@ -import dynlib +import qr -type - QRcode {.pure.} = object - version: cint - width: cint - data: pointer - -type - QRencodeString = proc(s: cstring, ver: cint, level: cint, hint: cint, cs: cint): ptr QRcode {.cdecl.} - QRfree = proc(qr: ptr QRcode) {.cdecl.} - -# Try versioned library names (Ubuntu 24.04 uses libqrencode.so.4) -var lib = loadLib("libqrencode.so.4") -if lib == nil: - lib = loadLib("libqrencode.so.3") -if lib == nil: - lib = loadLib("libqrencode.so") -if lib == nil: - quit("Failed to load libqrencode", 1) - -let encode = cast[QRencodeString](lib.symAddr("QRcode_encodeString")) -let free_qr = cast[QRfree](lib.symAddr("QRcode_free")) - -let qr = encode("unsandbox-qr-ok", 0, 1, 2, 1) -if qr != nil: - echo "QR:unsandbox-qr-ok:ROWS:", qr.width - free_qr(qr) -else: - echo "QR encode failed" - -unloadLib(lib) +let qrCode = newQR("unsandbox-qr-ok") +echo "QR:unsandbox-qr-ok:ROWS:" & $qrCode.size diff --git a/test/qr.pl b/test/qr.pl index beb5bd5..bf92df6 100644 --- a/test/qr.pl +++ b/test/qr.pl @@ -1,5 +1,5 @@ -use Text::QRCode; -my $qr = Text::QRCode->new(); -my $matrix = $qr->plot("unsandbox-qr-ok"); -my $rows = scalar(@$matrix); +use Imager::QRCode; +my $qr = Imager::QRCode->new(size => 1, margin => 0); +my $img = $qr->plot("unsandbox-qr-ok"); +my $rows = $img->getheight(); print "QR:unsandbox-qr-ok:ROWS:$rows\n";