1 # Установка номера тестового ПРА
4 set regnumPRA 0000000000000001
9 # Собственно тестовый фреймворк
14 # Уровень логгинга по умолчанию. Может быть переопределен явным
15 # присваиванием перед созданием контекста. Действует на контексты
16 # созданные makeCtx, makeCtx2 и threecontexts.
17 # Задание -logminpriority в test::ctxParams имеет приоритет.
19 # Переменная хранящая имя динамической библиотеки для userlib
21 # Чтобы timestamp была определена всегда
22 variable timestamp [clock seconds]
25 if {$::tcl_platform(platform)!="dos"} {
26 set dirlist [list [file dirname [info script]]\
27 [file dirname [info nameofexecutable]]]
28 if {$::tcl_platform(platform) == "windows"} {
30 [file normalize [file join [file dirname [info script]] .. obj_mid.w32]]\
31 [file normalize [file join [file dirname [info script]] .. obj_mid.w32]]
32 } elseif {$::tcl_platform(os) == "Linux"} {
34 [file normalize [file join [file dirname [info script]] .. obj_sid.lnx]]
35 } elseif {$::tcl_platform(os) == "SunOS"} {
36 if {$::tcl_platform(wordSize) == 8} {
38 } elseif {$::tcl_platform(byteOrder) == "littleEndian"} {
44 [file normalize [file join [file dirname [info script]] .. obj_sid.$for]]
46 foreach dir $dirlist {
47 set userlib_file [file join $dir usermci[info sharedlibextension]]
48 if {[file exists $userlib_file]} {
52 if {![file exists $userlib_file]} {
53 error "No usable userlib found in $dirlist"
55 set userlib [list -userlib $userlib_file]
63 # Вызывается в начале тестового скрипта. Инициализирует необходимые
64 # переменные пакета, открывает лог и пишет в него заголовок
65 # Параметры name - заголовок тестового скрипта.
67 # Побочные эффекты - создается <имя-скрипта>.log
69 proc start_tests {name} {
71 if {![info exists suffix]} {
72 set binary [file rootname [file tail [info nameofexecutable]]]
73 if {$binary != "tclsh"} {
74 set suffix "_[string range [file tail [info nameofexecutable]] 0 2]"
79 variable logname [file rootname [file tail [info script]]]$suffix.log
80 variable no 0 ok 0 failed 0 p_skip 0 c_skip 0 t_name $name logchannel [open $logname w] tempfiles {}
81 if {![catch {package present Vizir}]} {
84 puts [format [rus "=========== Группа тестов: %s ================="] [rus $name]]
85 puts $::test::logchannel [format [rus "Группа тестов \"%s\""] $name]
88 # Завершает выполнение теста и выводит отчет
101 puts "==================================================="
102 puts [format [rus "Всего %d тестов. Выполнено %d успешно, %d неуспешно"] $no $ok $failed]
103 if {$p_skip || $c_skip} {
104 puts [format [rus "Пропущено: %d на данной платформе %d из-за невыполнения других тестов"] $p_skip $c_skip]
107 puts [format [rus "Смотри более подробную информацию в %s"] $logname]
109 set test_id [file rootname [file tail [info script]]]$suffix
110 set stat [open "stats" a]
111 fconfigure $stat -encoding utf-8
112 puts $stat [list $test_id [rus $t_name] $no $ok $failed $p_skip $c_skip]
115 foreach file $tempfiles {
117 if [info exists $file] {puts [test_log] "Deleting $file"
123 # Вовзращает идентификатор канала, куда пишется лог тестов.
124 # Рекомендуется назначать его в качестве -logchannel создаваемым
125 # контекстам чтобы вся выдача была в одном месте
135 # 2. Код (рекомендуется писать {
138 # 3. Ожидаемый результат выполнения - 0 успешно 1 - ошибка. Варианты
139 # больше 1 (TCL_BREAK, TCL_CONTINUE и TCL_RETURN) возможны, но вряд
141 # 4. Ожидаемый возвращаемый результат
142 # Если предыдущий параметр 0, результат сравнивается на точное
143 # совпадение, если 1 - результат - регексп, которому должно
144 # удовлетворять сообщение об ошибке.
149 while {[string match -* [lindex $args 0]]} {
150 set key [lindex $args 0]
151 set val [lindex $args 1]
152 set args [lrange $args 2 end]
155 foreach {message code exitStatus expectedResult} $args break
157 if {[info exists opts(-platform)] && [lsearch -exact $opts(-platform) $::tcl_platform(platform)]==-1} {
158 logskip $message "platform"
161 if {[info exists opts(-platformex)] && ![uplevel expr $opts(-platformex)]} {
162 logskip $message "platform"
165 if {[info exists opts(-skip)] && [uplevel expr $opts(-skip)]} {
166 logskip $message "prereq"
169 if {[info exists opts(-fixme)] && [uplevel expr $opts(-fixme)]} {
170 logmiss $message "FIXME"
173 if {[info exists opts(-createsfiles)]} {
174 foreach file $opts(-createsfiles) {
175 lappend tempfiles $file
176 if {[file exists $file]} {file delete $file}
179 if {[info exists opts(-createsvars)]} {
180 foreach var $opts(-createsvars) {
181 uplevel "if {\[info exists $var\]} {unset $var}"
185 set teststart [clock seconds]
186 set status [catch {uplevel $code} result]
187 set testend [clock seconds]
188 if {$teststart == $testend} {
189 set timestamp $teststart
191 # Handle negative intervals correctly
192 if {$teststart > $testend} {
193 set timestamp "$testend+[expr $teststart-$testend]"
195 set timestamp "$teststart+[expr $testend-$teststart]"
198 if {$status!=$exitStatus || ($status==1?![regexp --\
199 [rus $expectedResult] $result]:([info exists opts(-time)]?\
200 ![listcompare $result $expectedResult $opts(-time)]:\
201 [string compare "$result" "$expectedResult"]))} {
204 set expectedResult [rus $expectedResult]
206 log "Code:----$code---------------"
207 log "Expected status $exitStatus got $status"
208 log "Expected result: [list $expectedResult]"
209 log " Got result: [list $result]"
211 log "errorCode = $::errorCode"
218 # Внутренние (неэкспортируемые)процедуры
223 # Сравнение списков с учетом того что некоторые элементы могут быть
224 # метками времени, которые проверяются с точностью +-секунда
225 # Параметр time - список, каждый элемент которого является индексом
226 # элемента в списке, либо списком индексов во вложенных списках
228 proc listcompare {list1 list2 time} {
230 if {[llength $e]>1} {
231 lappend a([lindex $e 0]) [lrange $e 1 end]
236 if {[llength $list1] !=[llength $list2]} {
240 foreach e1 $list1 e2 $list2 {
241 if {![info exists a($i)]} {
242 if {[string compare $e1 $e2]!=0} {
245 } elseif {[llength $a($i)]} {
246 if {![listcompare $e1 $e2 $a($i)]} {
250 if {$e2 == "::test::timestamp"} {
251 set e2 $::test::timestamp
253 if {[regexp {^([[:digit:]]+)\+([[:digit:]]+)$} $e2 m start delta]} {
254 if {$e1<$start || $e1 >$start+$delta} {
257 } elseif {abs($e1-$e2)>1} {
273 puts $logchannel $message
276 # Вызывается при начале теста
278 proc logbegin {testname} {
282 puts -nonewline [rus [format "Тест%5d: %-60s:" $no [string range $testname 0 59]]]
284 set curtest $testname
285 log [rus "Тест $no: $testname start"]
288 # Вызывается при пропуске теста
290 proc logskip {testname reason} {
294 puts "[rus [format "Тест%5d: %-60s:" $no [string rang $testname 0 59]]]skipped "
295 log "[rus "Тест $no: skipped "][expr {$reason=="platform"?"on
296 the platform $::tcl_platform(platform)":"due to failed prerequisites"}]:[rus $testname]"
298 if {$reason == "platform"} {
306 # Вызывается при игнорировании теста
308 proc logmiss {testname reason} {
311 puts "[rus [format "Тест%5d: %-60s:" $no [string rang $testname 0 59]]]missed "
312 log "[rus "Тест $no: missed "][expr {$reason=="platform"?"on
313 the platform $::tcl_platform(platform)":"by reason: $reason"}]:[rus $testname]"
319 # Вызывается конце теста и с параметром ok или failed
321 proc logend {status} {
327 log [rus "Тест $no: $curtest ends $status"]
330 #####################################################################
331 # Вспомогательные процедуры, не специфичные для тестируемого
333 #####################################################################
336 # Записывает данные из data в файл name. По умолчанию пишет в
337 # текущей системной кодировке. Можно указать кодировку явно третьим
340 proc makeFile {name data {encoding {}}} {
342 setFileEncoding $f $encoding
343 puts -nonewline $f $data
346 proc setFileEncoding {f encoding} {
347 if {[string length $encoding]} {
348 if {"$encoding" == "binary"} {
349 fconfigure $f -translation binary
351 fconfigure $f -encoding $encoding
356 # Возвращает содeржимое файла
359 proc getFile {filename {encoding {}}} {
360 set f [open $filename]
361 setFileEncoding $f $encoding
367 # Возвращает содержимое бинарного файла. Для совместимости со старыми
370 proc getfile {filename} {
371 return [getFile $filename binary]
374 # Зачитывает указанный файл, удаляет его и возвращает содержимое.
375 # По умолчанию читает файл в текущей системной кодировке. Можно
376 # указать кодировку явно вторым аргументом.
379 proc readAndDel {name {encoding {}}} {
381 setFileEncoding $f $encoding
390 # Защищает файл от записи средствами операционной системы
391 # denywrite filename ?boolean?
392 # Если boolean не указан, или он true, файл становится read-only
393 # Если указан - readwrite (для владельца. Впрочем для не-владельца все
394 # равно не сработает)
396 proc denyWrite {filename {deny 1}} {
398 if {$tcl_platform(platform) == "unix"} {
399 set cur_attr [file attributes $filename -permissions]
401 set new_attr [expr {$cur_attr &~ 0200}]
403 set new_attr [expr {$cur_attr | 0200}]
405 file attributes $filename -permissions $new_attr
407 file attributes $filename -readonly $deny
411 # Записывает в лог 16-ричный дамп указанной переменной
414 proc hexdump {data } {
415 while {[string length $data]} {
416 set block [string range $data 0 15]
417 set data [string replace $data 0 15]
418 binary scan [encoding convertto $block] c* list
422 append line [format "%02x " [expr $code>=0?$code:$code +256]]
427 append line [string repeat " " [expr 56-[string length $line]]]
428 regsub -all "\[\0-\37\]" $block . printable
429 append line [rus $printable]
433 namespace export test start_tests end_tests test_log rus log\
434 makeFile readAndDel hexdump denyWrite getFile getfile
436 namespace import ::test::*
438 package provide test 0.2