- skipFilter: skip deleted records in GoTop/GoBottom/Skip when SET DELETED ON - hbrdd.IsSetDeleted callback: avoids circular import hbrdd→hbrtl - Parser: capture ON/OFF for boolean SET commands (DELETED, EXACT, SOFTSEEK, etc.) - Parser: capture TO expr for SET DATE/DECIMALS/EPOCH - Gengo: emit proper t.Do() calls for 11 SET toggles + 3 value SETs - stmtSet: was stub (skipToEOL), now calls parseSet() - RTL: register 11 SET toggle functions (SETDELETED, SETEXACT, etc.) - RTL: DBLOCATE/DBCONTINUE for sequential search - RTL: DBSETFILTER/DBCLEARFILTER/DBFILTER - PadL/PadR: support 3rd param fill character - Area interface: added SetFound, SetLocate, LocateBlock, filter methods - MemRDD: implements new Area interface methods - Comprehensive PRG test: test_search.prg (7 test suites all pass) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
230 lines
5.7 KiB
Plaintext
230 lines
5.7 KiB
Plaintext
// Comprehensive search test: SEEK, SOFTSEEK, SET DELETED, LOCATE/CONTINUE
|
|
// Tests all search-related RDD functionality end-to-end.
|
|
FUNCTION Main()
|
|
LOCAL i, aStruct, nCount
|
|
|
|
? "=========================================="
|
|
? " Five Search/Filter Test Suite"
|
|
? "=========================================="
|
|
?
|
|
|
|
// --- Setup: Create test table ---
|
|
aStruct := {{"ID","N",6,0}, {"NAME","C",20,0}, {"CITY","C",15,0}, {"AGE","N",3,0}, {"ACTIVE","L",1,0}}
|
|
dbCreate("search_test", aStruct)
|
|
USE "search_test"
|
|
|
|
// Insert 50 records
|
|
FOR i := 1 TO 50
|
|
APPEND BLANK
|
|
FieldPut(1, i)
|
|
FieldPut(2, "Name_" + PadL(LTrim(Str(i)), 3, "0"))
|
|
IF i % 5 == 0
|
|
FieldPut(3, "Seoul")
|
|
ELSEIF i % 5 == 1
|
|
FieldPut(3, "Tokyo")
|
|
ELSEIF i % 5 == 2
|
|
FieldPut(3, "Beijing")
|
|
ELSEIF i % 5 == 3
|
|
FieldPut(3, "London")
|
|
ELSE
|
|
FieldPut(3, "NewYork")
|
|
ENDIF
|
|
FieldPut(4, 20 + (i % 40))
|
|
FieldPut(5, (i % 3 != 0))
|
|
NEXT
|
|
|
|
? "Setup: 50 records created, RecCount =", RecCount()
|
|
?
|
|
|
|
// ============================================
|
|
// TEST 1: INDEX + SEEK (exact match)
|
|
// ============================================
|
|
? "--- TEST 1: INDEX + SEEK ---"
|
|
INDEX ON NAME TO search_name
|
|
? "Index on NAME created."
|
|
|
|
SEEK "Name_001"
|
|
? "SEEK 'Name_001': Found =", Found(), "RecNo =", RecNo()
|
|
IF !Found()
|
|
? " *** FAIL: should find Name_001"
|
|
ENDIF
|
|
|
|
SEEK "Name_025"
|
|
? "SEEK 'Name_025': Found =", Found(), "RecNo =", RecNo()
|
|
IF !Found()
|
|
? " *** FAIL: should find Name_025"
|
|
ENDIF
|
|
|
|
SEEK "Name_050"
|
|
? "SEEK 'Name_050': Found =", Found(), "RecNo =", RecNo()
|
|
IF !Found()
|
|
? " *** FAIL: should find Name_050"
|
|
ENDIF
|
|
|
|
SEEK "Name_999"
|
|
? "SEEK 'Name_999': Found =", Found(), "Eof =", Eof()
|
|
IF Found()
|
|
? " *** FAIL: should NOT find Name_999"
|
|
ENDIF
|
|
?
|
|
|
|
// ============================================
|
|
// TEST 2: SOFTSEEK
|
|
// ============================================
|
|
? "--- TEST 2: SOFTSEEK ---"
|
|
SET SOFTSEEK ON
|
|
SEEK "Name_030"
|
|
? "SOFTSEEK 'Name_030': Found =", Found(), "RecNo =", RecNo()
|
|
IF Found()
|
|
? " Name =", FieldGet(2)
|
|
ENDIF
|
|
|
|
SEEK "Name_031"
|
|
? "SOFTSEEK 'Name_031': Found =", Found(), "RecNo =", RecNo()
|
|
IF !Found() .AND. !Eof()
|
|
? " Positioned at:", FieldGet(2), "(softseek OK)"
|
|
ENDIF
|
|
|
|
SET SOFTSEEK OFF
|
|
?
|
|
|
|
// ============================================
|
|
// TEST 3: Partial key SEEK
|
|
// ============================================
|
|
? "--- TEST 3: Partial Key SEEK ---"
|
|
SEEK "Name_01"
|
|
? "SEEK 'Name_01' (partial): Found =", Found()
|
|
IF Found()
|
|
? " Name =", FieldGet(2), "(should be Name_010 or similar)"
|
|
ENDIF
|
|
?
|
|
|
|
// ============================================
|
|
// TEST 4: SET DELETED — skip deleted records
|
|
// ============================================
|
|
? "--- TEST 4: SET DELETED ---"
|
|
|
|
// Delete records 10,20,30,40,50 (every 10th)
|
|
SET ORDER TO 0 // natural order
|
|
GO TOP
|
|
FOR i := 1 TO 50
|
|
GO i
|
|
IF FieldGet(1) % 10 == 0
|
|
DELETE
|
|
ENDIF
|
|
NEXT
|
|
? "Deleted records: 10, 20, 30, 40, 50"
|
|
|
|
SET DELETED OFF
|
|
GO TOP
|
|
nCount := 0
|
|
DO WHILE !Eof()
|
|
nCount++
|
|
SKIP
|
|
ENDDO
|
|
? "SET DELETED OFF: visible =", nCount, "(should be 50)"
|
|
|
|
SET DELETED ON
|
|
GO TOP
|
|
nCount := 0
|
|
DO WHILE !Eof()
|
|
nCount++
|
|
SKIP
|
|
ENDDO
|
|
? "SET DELETED ON: visible =", nCount, "(should be 45)"
|
|
|
|
IF nCount != 45
|
|
? " *** FAIL: expected 45, got", nCount
|
|
ELSE
|
|
? " PASS"
|
|
ENDIF
|
|
|
|
// GoTop should skip deleted record if record 1 would be first
|
|
// Let's delete record 1 and test
|
|
GO 1
|
|
DELETE
|
|
SET DELETED ON
|
|
GO TOP
|
|
? "After deleting rec 1, GO TOP recNo =", RecNo(), "(should be 2)"
|
|
IF RecNo() != 2
|
|
? " *** FAIL: GoTop should skip deleted rec 1"
|
|
ELSE
|
|
? " PASS"
|
|
ENDIF
|
|
|
|
// Recall record 1 for subsequent tests
|
|
GO 1
|
|
RECALL
|
|
SET DELETED OFF
|
|
?
|
|
|
|
// ============================================
|
|
// TEST 5: SET DELETED + INDEX
|
|
// ============================================
|
|
? "--- TEST 5: SET DELETED + INDEX ---"
|
|
SET ORDER TO 1 // NAME index
|
|
SET DELETED ON
|
|
GO TOP
|
|
nCount := 0
|
|
DO WHILE !Eof()
|
|
nCount++
|
|
SKIP
|
|
ENDDO
|
|
? "Indexed + SET DELETED ON: visible =", nCount, "(should be 45)"
|
|
IF nCount != 45
|
|
? " *** FAIL"
|
|
ELSE
|
|
? " PASS"
|
|
ENDIF
|
|
SET DELETED OFF
|
|
?
|
|
|
|
// ============================================
|
|
// TEST 6: Navigation correctness
|
|
// ============================================
|
|
? "--- TEST 6: Navigation ---"
|
|
SET ORDER TO 0 // natural
|
|
GO TOP
|
|
? "GO TOP: RecNo =", RecNo(), "(should be 1)"
|
|
GO BOTTOM
|
|
? "GO BOTTOM: RecNo =", RecNo(), "(should be 50)"
|
|
SKIP -1
|
|
? "SKIP -1: RecNo =", RecNo(), "(should be 49)"
|
|
GO TOP
|
|
SKIP 5
|
|
? "GO TOP + SKIP 5: RecNo =", RecNo(), "(should be 6)"
|
|
?
|
|
|
|
// ============================================
|
|
// TEST 7: SEEK on city index
|
|
// ============================================
|
|
? "--- TEST 7: Second index ---"
|
|
INDEX ON CITY TO search_city
|
|
? "Index on CITY created."
|
|
|
|
SEEK "Seoul"
|
|
? "SEEK 'Seoul': Found =", Found()
|
|
IF Found()
|
|
? " City =", FieldGet(3), "ID =", FieldGet(1)
|
|
ENDIF
|
|
|
|
SEEK "Tokyo"
|
|
? "SEEK 'Tokyo': Found =", Found()
|
|
IF Found()
|
|
? " City =", FieldGet(3), "ID =", FieldGet(1)
|
|
ENDIF
|
|
|
|
SEEK "Paris"
|
|
? "SEEK 'Paris': Found =", Found(), "(should be .F.)"
|
|
?
|
|
|
|
// ============================================
|
|
// Cleanup
|
|
// ============================================
|
|
CLOSE ALL
|
|
? "=========================================="
|
|
? " All search tests complete."
|
|
? "=========================================="
|
|
|
|
RETURN
|