1#
2# filescan.test
3#
4# Tests for the scancontext and scanfile commands.
5#---------------------------------------------------------------------------
6# Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
7#
8# Permission to use, copy, modify, and distribute this software and its
9# documentation for any purpose and without fee is hereby granted, provided
10# that the above copyright notice appear in all copies.  Karl Lehenbauer and
11# Mark Diekhans make no representations about the suitability of this
12# software for any purpose.  It is provided "as is" without express or
13# implied warranty.
14#------------------------------------------------------------------------------
15# $Id: filescan.test,v 1.2 2002/04/02 02:29:43 hobbs Exp $
16#------------------------------------------------------------------------------
17#
18
19if {[cequal [info procs Test] {}]} {
20    source [file join [file dirname [info script]] testlib.tcl]
21}
22
23# Increment a name.  This takes a name and "adds one" to it, that is advancing
24# each digit lexically through "0"..."9" -> "A"-"Z" -> "a"..."z".  When one
25# digit wraps, the next one is advanced.  Optional arg forces upper case only
26# if true and start with all upper case or digits.
27
28proc IncrName {Name args} {
29    set Upper [expr {([llength $args] == 1) && [lindex $args 0]}]
30    set Last  [expr [clength $Name]-1]
31    set Begin [csubstr $Name 0 $Last]
32    set Digit [cindex $Name $Last]
33    set Recurse 0
34    case $Digit in {
35        {9}     {set Digit A}
36        {Z}     {if {$Upper} {set Recurse 1} else {set Digit a}}
37        {z}     {set Recurse 1}
38        default {set Digit [ctype char [expr [ctype ord $Digit]+1]]}
39    }
40    if {$Recurse} {
41        if {$Last == 0} then {
42            return 0 ;# Wrap around
43        } else {
44            return "[IncrName $Begin]0"
45        }
46    }
47    return "$Begin$Digit"
48}
49
50# Proc to generate record that can be validated.  The record has 
51# grows quite large to test the dynamic buffering in the file I/O.
52
53proc GenScanRec {key lineNum} {
54  set extra [replicate :@@@@@@@@: $lineNum]
55  return  "$key This is a test record ($extra) index is $key"
56}
57
58# Proc to validate a matched record.
59
60proc ValMatch {scanInfo id} {
61    global testFH matchInfo
62
63    Test filescan-${id}.1 {filescan tests} {
64        list line $matchInfo(line)
65    } 0 [list line [GenScanRec [keylget scanInfo key] \
66                               [keylget scanInfo linenum]]]
67
68    Test filescan-${id}.2 {filescan tests} {
69         list offset $matchInfo(offset)
70    } 0 [list offset [keylget scanInfo offset]]
71
72    if 0 {
73    Test filescan-${id}.2.1 {filescan tests} {
74         list bytesread $matchInfo(bytesread)
75    } 0 [list bytesread [keylget scanInfo bytesread]]
76    }
77
78    Test filescan-${id}.3 {filescan tests} {
79         list linenum $matchInfo(linenum)
80    } 0 [list linenum [keylget scanInfo linenum]]
81
82    Test filescan-${id}.4 {filescan tests} {
83         list handle $matchInfo(handle)
84    } 0 [list handle $testFH]
85
86    global matchCnt
87    incr matchCnt([keylget scanInfo matchType])
88}
89
90global matchInfo matchCnt chkMatchCnt testFH
91
92foreach i {0 1 2 3 df} {
93    set chkMatchCnt($i)  0
94}
95set scanList       {}
96set maxRec        200
97
98# Build a test file and a list of records to scan for.  Each element in the 
99# list will have the following info:
100#   {key fileOffset fileLineNumber matchType}
101# Also build a file to diff against for the -copyfile option.
102
103TestRemove TEST.TMP TEST2.TMP TESTCHK.TMP
104
105set testFH [open TEST.TMP w]
106set testChkFH [open TESTCHK.TMP w]
107
108set key FatHeadAAAA
109set bytesRead 0
110for {set cnt 0} {$cnt < $maxRec} {incr cnt} {
111    set rec [GenScanRec $key [expr $cnt+1]]
112    incr bytesRead [expr [clength $rec] + 1]
113    if {($cnt % 10) == 0} {
114        set matchType [random 4]
115        incr chkMatchCnt($matchType)
116        set scanInfo {}
117        keylset scanInfo key $key
118        keylset scanInfo offset [tell $testFH]
119        keylset scanInfo bytesread $bytesRead
120        keylset scanInfo linenum [expr $cnt+1]
121        keylset scanInfo matchType $matchType
122        if {[random 2]} {
123            set scanList [concat $scanList [list $scanInfo]]
124        } else {
125            set scanList [concat [list $scanInfo] $scanList]
126        }
127    } else {
128        incr chkMatchCnt(df)
129        puts $testChkFH $rec
130    }
131    if {$cnt == [expr $maxRec/2]} {
132        set midKey $key
133    }
134    puts $testFH $rec
135    set key [IncrName $key 1]  ;# Upper case only
136}
137
138close $testFH
139close $testChkFH
140
141# Build up the scan context.
142
143set testCH [scancontext create]
144
145foreach scanInfo $scanList {
146    set key [keylget scanInfo key]
147    set matchType [keylget scanInfo matchType]
148    set cmd "global matchInfo; ValMatch [list $scanInfo] 1.1" 
149    case $matchType in {
150      {0} {scanmatch -nocase $testCH [string toupper $key] $cmd}
151      {1} {scanmatch $testCH ^$key  $cmd}
152      {2} {scanmatch $testCH $key\$ $cmd}
153      {3} {scanmatch $testCH $key   $cmd}
154    }
155}
156
157scanmatch $testCH {
158    global matchCnt testFH matchInfo
159
160    incr matchCnt(df)
161
162    Test filescan-1.2 {filescan tests} {
163        set matchInfo(handle)
164    } 0 $testFH
165}
166
167proc ValScan id {
168    global matchInfo matchCnt chkMatchCnt testFH
169
170    Test filescan-${id}.1 {filescan tests} {
171        set matchCnt(0)
172    } 0 [set chkMatchCnt(0)]
173
174    Test filescan-${id}.2 {filescan tests} {
175        set matchCnt(1)
176    } 0 [set chkMatchCnt(1)]
177
178    Test filescan-${id}.3 {filescan tests} {
179        set matchCnt(2)
180    } 0 [set chkMatchCnt(2)]
181
182    Test filescan-${id}.4 {filescan tests} {
183        set matchCnt(3)
184    } 0 [set chkMatchCnt(3)]
185
186    Test filescan-${id}.5 {filescan tests} {
187        set matchCnt(df)
188    } 0 [set chkMatchCnt(df)]
189}
190
191foreach i {0 1 2 3 df} {
192    set matchCnt($i)  0
193}
194set testFH [open TEST.TMP r]
195scanfile $testCH $testFH
196close $testFH
197ValScan 1.3
198
199foreach i {0 1 2 3 df} {
200    set matchCnt($i)  0
201}
202set testFH [open TEST.TMP r]
203set test2FH [open TEST2.TMP w]
204scanfile -copyfile $test2FH $testCH $testFH
205close $testFH
206close $test2FH
207ValScan 1.4
208
209Test filescan-1.5 {filescan tests} {
210    set fh [open TESTCHK.TMP]
211    set TESTCHK [read $fh]
212    close $fh
213    set fh [open TEST2.TMP]
214    set TEST2 [read $fh]
215    close $fh
216    cequal $TESTCHK $TEST2
217} 0 1
218catch {unset TESTCHK}
219catch {unset TEST2}
220
221scancontext delete $testCH
222
223# Test return and continue from within match commands
224
225set testFH [open TEST.TMP r]
226
227set testCH [scancontext create]
228seek $testFH 0
229global matchCnt
230set matchCnt(0) 0
231
232scanmatch $testCH $midKey {
233    global matchCnt
234    incr matchCnt(0)
235    continue;
236}
237
238scanmatch $testCH ^$midKey {
239    error "This should not ever get executed  2.1"
240}
241
242scanmatch $testCH [IncrName $midKey] {
243    return "FudPucker"
244}
245
246Test filescan-2.2 {filescan tests} {
247    scanfile $testCH $testFH
248} 0 "FudPucker"
249
250scancontext delete $testCH
251
252# Test argument checking and error handling.
253
254Test filescan-3.1 {filescan tests} {
255    scancontext foomuch
256} 1 {invalid argument, expected one of: "create", "delete", or "copyfile"}
257
258Test filescan-3.2 {filescan tests} {
259    scanmatch $testCH
260} 1 {wrong # args: scanmatch ?-nocase? contexthandle ?regexp? command}
261
262Test filescan-3.3 {filescan tests} {
263    scanmatch
264} 1 {wrong # args: scanmatch ?-nocase? contexthandle ?regexp? command}
265
266Test filescan-3.4 {filescan tests} {
267    scanfile
268} 1 {wrong # args: scanfile ?-copyfile filehandle? contexthandle filehandle}
269
270Test filescan-3.5 {filescan tests} {
271    set testCH [scancontext create]
272    scanfile $testCH $testFH
273} 1 {no patterns in current scan context}
274
275Test filescan-3.6 {filescan tests} {
276    scancontext copyfile
277} 1 {wrong # args: scancontext copyfile contexthandle ?filehandle?}
278
279Test filescan-3.7 {filescan tests} {
280    scancontext copyfile $testCH x y
281} 1 {wrong # args: scancontext copyfile contexthandle ?filehandle?}
282
283
284catch {scancontext delete $testCH}
285
286close $testFH
287
288# 
289# Test subMatch handling.
290#
291
292set testFH [open TEST.TMP w]
293loop idx 0 10 {
294    puts $testFH "AAx[replicate xx $idx]xBBc[replicate cc $idx]cDD"
295}
296close $testFH
297
298# Procedure to verify submatches.  Works for upper or lower case.
299
300proc ChkSubMatch {id matchInfoVar} {
301    upvar $matchInfoVar matchInfo
302
303    set idx [expr $matchInfo(linenum) - 1]
304
305    set end0 [expr 3+($idx * 2)]
306    Test filescan-$id.0.$idx {filescan tests} {
307        set matchInfo(submatch0)
308    } 0 "x[replicate xx $idx]x"
309    Test filescan-$id.1.$idx {filescan tests} {
310        set matchInfo(subindex0)
311    } 0 "2 $end0"
312
313    set start1 [expr $end0+3]
314    set end1 [expr $start1+($idx*2)+1]
315    Test filescan-$id.2.$idx {filescan tests} {
316        set matchInfo(submatch1)
317    } 0 "c[replicate cc $idx]c"
318    Test filescan-$id.3.$idx {filescan tests} {
319        set matchInfo(subindex1)
320    } 0 "$start1 $end1"
321
322    Test filescan-$id.4.$idx {filescan tests} {
323        list [info exists matchInfo(submatch2)] \
324             [info exists matchInfo(subindex2)]
325    } 0 {0 0}
326}
327
328set testFH [open TEST.TMP r]
329
330set testCH [scancontext create]
331scanmatch $testCH {A*(x*)B*(c*)DD} {
332    ChkSubMatch 4 matchInfo
333}
334
335scanmatch -nocase $testCH {Aa(x*)B(C*)Dd} {
336    ChkSubMatch 5 matchInfo
337}
338
339scanfile $testCH $testFH
340
341scancontext delete $testCH
342close $testFH
343
344#
345# Test optional match patterns.
346#
347set testFH [open TEST.TMP w]
348puts $testFH {ABCD : efgh 123435}
349puts $testFH {HIJK : efgh 123435}
350puts $testFH {ABCD : efgh X123435}
351puts $testFH {HIJK : efgh X123435}
352close $testFH
353
354set testCH [scancontext create]
355scanmatch $testCH {([H-Z]+)? : ([a-z]+) (X)?([0-9]+)} {
356    set thisLine {}
357    for {set idx 0} {$idx < 50} {incr idx} {
358        if {!([info exists matchInfo(submatch$idx)] ||
359              [info exists matchInfo(subindex$idx)])} continue
360
361        set this [list $idx]
362        lappend this [info exists matchInfo(submatch$idx)]
363        lappend this [info exists matchInfo(subindex$idx)]
364        if [info exists matchInfo(submatch$idx)] {
365            lappend this $matchInfo(submatch$idx)
366        } 
367        if [info exists matchInfo(subindex$idx)] {
368            lappend this $matchInfo(subindex$idx)
369        } 
370        lappend thisLine $this
371    }
372    lappend matches $thisLine
373    catch {unset this}
374    unset thisLine
375}
376
377set testFH [open TEST.TMP r]
378Test filescan-6.1 {filescan tests} {
379    set matches {}
380    scanfile $testCH $testFH
381    set matches
382} 0  [list \
383        [list {0 1 1 {} {-1 -1}} \
384              {1 1 1 efgh {7 10}} \
385              {2 1 1 {} {-1 -1}} \
386              {3 1 1 123435 {12 17}}] \
387        [list {0 1 1 HIJK {0 3}} \
388              {1 1 1 efgh {7 10}} \
389              {2 1 1 {} {-1 -1}} \
390              {3 1 1 123435 {12 17}}] \
391        [list {0 1 1 {} {-1 -1}} \
392              {1 1 1 efgh {7 10}} \
393              {2 1 1 X {12 12}} \
394              {3 1 1 123435 {13 18}}] \
395        [list {0 1 1 HIJK {0 3}} \
396              {1 1 1 efgh {7 10}} \
397              {2 1 1 X {12 12}} \
398              {3 1 1 123435 {13 18}}]]
399close $testFH
400scancontext delete $testCH
401
402set testCH [scancontext create]
403
404Test filescan-7.1 {filescan tests} {
405    scanmatch $testCH {a[} {}
406} 1 {couldn't compile regular expression pattern: brackets [] not balanced}
407
408#
409# Test the copy file manipulation by sorting a file of numbered lines into two
410# files of odd and even.
411#
412
413set testFH [open TEST.TMP w]
414loop cnt 0 101 {
415    puts $testFH "Line $cnt"
416    puts $testFH "Match me"
417}
418close $testFH
419set testFH [open TEST.TMP r]
420
421set testChkFH [open TESTCHK.TMP w]
422set testChk2FH [open TESTCHK2.TMP w]
423
424set testCH [scancontext create]
425scancontext copyfile $testCH $testChkFH
426
427Test filescan-8.1 {filescan tests} {
428    scancontext copyfile $testCH
429} 0 $testChkFH
430
431scanmatch $testCH {^Match me$} {
432    if [cequal [scancontext copyfile $matchInfo(context)] $testChkFH] {
433        scancontext copyfile $matchInfo(context) $testChk2FH
434    } elseif [cequal [scancontext copyfile $matchInfo(context)] $testChk2FH] {
435        scancontext copyfile $matchInfo(context) $testChkFH
436    } else {
437        Test filescan-8.2 {filescan tests} {
438            scancontext copyfile $matchInfo(context)
439        } 0 "should have been $testChkFH or $testChk2FH"
440    }
441}
442
443scanfile $testCH $testFH
444
445close $testFH
446close $testChkFH
447close $testChk2FH
448scancontext delete $testCH
449
450set testChkFH [open TESTCHK.TMP r]
451set testChk2FH [open TESTCHK2.TMP r]
452loop cnt 0 101 2 {
453    Test filescan-8.3 {filescan tests} {
454        gets $testChkFH
455    } 0 "Line $cnt"
456}
457
458loop cnt 1 101 2 {
459    Test filescan-8.4 {filescan tests} {
460        gets $testChk2FH
461    } 0 "Line $cnt"
462}
463
464close $testChkFH
465close $testChk2FH
466
467#
468# Test for the problem De Clarke found with Boyer-Moore.
469#
470
471Test 9.1 {filescan tests} {
472    set testFH [open TEST.TMP w]
473    puts $testFH \
474            {Sun Dec 01 14:56:08 1996 mask1a.ps kelson bigdog.ucolick.org 1}
475    close $testFH
476
477    set linesMatched {}
478    set testCH [scancontext create]
479    scanmatch $testCH {[A-z] Dec [0-9]* [0123456789:]* 1996 } {
480        lappend linesMatched $matchInfo(line)
481    }
482    set testFH [open TEST.TMP]
483    scanfile $testCH $testFH
484    close $testFH
485    set linesMatched
486} 0 {{Sun Dec 01 14:56:08 1996 mask1a.ps kelson bigdog.ucolick.org 1}}
487
488#
489# Test some regexps that have caused probelms in the past.
490#
491Test 9.2 {filescan tests} {
492    set ch [scancontext create]
493    scanmatch -nocase $ch {^[a-z]} {echo foo}
494    scancontext delete $ch
495} 0 {}
496
497Test 9.3 {filescan tests} {
498    # Tuende Kriegl <tuende.kriegl@mch.sni.de> says this
499    # panics on NT but not unix! 
500    set ch [scancontext create]
501    scanmatch $ch {([^(]*).'([^']*).*%OpText %(.*)} {echo foo}
502    scancontext delete $ch
503} 0 {}
504
505Test 9.1 {filescan tests} {
506    set testFH [open TEST.TMP w]
507    puts $testFH "foo\nbar"
508    close $testFH
509
510    set linesMatched {}
511    set testCH [scancontext create]
512    scanmatch $testCH {foo|bar} {
513        lappend linesMatched $matchInfo(line)
514    }
515    set testFH [open TEST.TMP]
516    scanfile $testCH $testFH
517    close $testFH
518    set linesMatched
519} 0 {foo bar}
520
521TestRemove TEST.TMP TEST2.TMP TESTCHK.TMP TESTCHK2.TMP
522
523rename GenScanRec {}
524rename ValMatch {}
525rename ValScan {}
526rename ChkSubMatch {}
527
528unset matchCnt chkMatchCnt matchInfo testFH test2FH testChkFH testChk2FH
529
530
531