1 # -*- coding: cp1251 -*-
2 # Установка номера тестового ПРА
5 set regnumPRA 0000000000000001
10 # Собственно тестовый фреймворк
15 # Уровень логгинга по умолчанию. Может быть переопределен явным
16 # присваиванием перед созданием контекста. Действует на контексты
17 # созданные makeCtx, makeCtx2 и threecontexts.
18 # Задание -logminpriority в test::ctxParams имеет приоритет.
20 # Переменная хранящая имя динамической библиотеки для userlib
22 # Чтобы timestamp была определена всегда
23 variable timestamp [clock seconds]
26 if {$::tcl_platform(platform)!="dos"} {
27 set dirlist [list [file dirname [info script]]\
28 [file dirname [info nameofexecutable]]]
29 if {$::tcl_platform(platform) == "windows"} {
31 [file normalize [file join [file dirname [info script]] .. obj_mid.w32]]\
32 [file normalize [file join [file dirname [info script]] .. obj_mid.w32]]
33 } elseif {$::tcl_platform(os) == "Linux"} {
35 [file normalize [file join [file dirname [info script]] .. obj_sid.lnx]]
36 } elseif {$::tcl_platform(os) == "SunOS"} {
37 if {$::tcl_platform(wordSize) == 8} {
39 } elseif {$::tcl_platform(byteOrder) == "littleEndian"} {
45 [file normalize [file join [file dirname [info script]] .. obj_sid.$for]]
47 foreach dir $dirlist {
48 set userlib_file [file join $dir usermci[info sharedlibextension]]
49 if {[file exists $userlib_file]} {
53 if {![file exists $userlib_file]} {
54 error "No usable userlib found in $dirlist"
56 set userlib [list -userlib $userlib_file]
64 # Вызывается в начале тестового скрипта. Инициализирует необходимые
65 # переменные пакета, открывает лог и пишет в него заголовок
66 # Параметры name - заголовок тестового скрипта.
68 # Побочные эффекты - создается <имя-скрипта>.log
70 proc start_tests {name} {
72 if {![info exists suffix]} {
73 set binary [file rootname [file tail [info nameofexecutable]]]
74 if {$binary != "tclsh"} {
75 set suffix "_[string range [file tail [info nameofexecutable]] 0 2]"
80 variable logname [file rootname [file tail [info script]]]$suffix.log
81 variable no 0 ok 0 failed 0 p_skip 0 c_skip 0 t_name $name logchannel [open $logname w] tempfiles {}
82 if {![catch {package present Vizir}]} {
85 puts [format [rus "=========== Группа тестов: %s ================="] [rus $name]]
86 puts $::test::logchannel [format [rus "Группа тестов \"%s\""] $name]
89 # Завершает выполнение теста и выводит отчет
102 puts "==================================================="
103 puts [format [rus "Всего %d тестов. Выполнено %d успешно, %d неуспешно"] $no $ok $failed]
104 if {$p_skip || $c_skip} {
105 puts [format [rus "Пропущено: %d на данной платформе %d из-за невыполнения других тестов"] $p_skip $c_skip]
108 puts [format [rus "Смотри более подробную информацию в %s"] $logname]
110 set test_id [file rootname [file tail [info script]]]$suffix
111 set stat [open "stats" a]
112 fconfigure $stat -encoding cp1251
113 puts $stat [list $test_id [rus $t_name] $no $ok $failed $p_skip $c_skip]
116 foreach file $tempfiles {
118 if [info exists $file] {puts [test_log] "Deleting $file"
124 # Вовзращает идентификатор канала, куда пишется лог тестов.
125 # Рекомендуется назначать его в качестве -logchannel создаваемым
126 # контекстам чтобы вся выдача была в одном месте
136 # 2. Код (рекомендуется писать {
139 # 3. Ожидаемый результат выполнения - 0 успешно 1 - ошибка. Варианты
140 # больше 1 (TCL_BREAK, TCL_CONTINUE и TCL_RETURN) возможны, но вряд
142 # 4. Ожидаемый возвращаемый результат
143 # Если предыдущий параметр 0, результат сравнивается на точное
144 # совпадение, если 1 - результат - регексп, которому должно
145 # удовлетворять сообщение об ошибке.
150 while {[string match -* [lindex $args 0]]} {
151 set key [lindex $args 0]
152 set val [lindex $args 1]
153 set args [lrange $args 2 end]
156 foreach {message code exitStatus expectedResult} $args break
158 if {[info exists opts(-platform)] && [lsearch -exact $opts(-platform) $::tcl_platform(platform)]==-1} {
159 logskip $message "platform"
162 if {[info exists opts(-platformex)] && ![uplevel expr $opts(-platformex)]} {
163 logskip $message "platform"
166 if {[info exists opts(-skip)] && [uplevel expr $opts(-skip)]} {
167 logskip $message "prereq"
170 if {[info exists opts(-fixme)] && [uplevel expr $opts(-fixme)]} {
171 logmiss $message "FIXME"
174 if {[info exists opts(-createsfiles)]} {
175 foreach file $opts(-createsfiles) {
176 lappend tempfiles $file
177 if {[file exists $file]} {file delete $file}
180 if {[info exists opts(-createsvars)]} {
181 foreach var $opts(-createsvars) {
182 uplevel "if {\[info exists $var\]} {unset $var}"
186 set teststart [clock seconds]
187 set status [catch {uplevel $code} result]
188 set testend [clock seconds]
189 if {$teststart == $testend} {
190 set timestamp $teststart
192 # Handle negative intervals correctly
193 if {$teststart > $testend} {
194 set timestamp "$testend+[expr $teststart-$testend]"
196 set timestamp "$teststart+[expr $testend-$teststart]"
199 if {$status!=$exitStatus || ($status==1?![regexp --\
200 [rus $expectedResult] $result]:([info exists opts(-time)]?\
201 ![listcompare $result $expectedResult $opts(-time)]:\
202 [string compare "$result" "$expectedResult"]))} {
205 set expectedResult [rus $expectedResult]
207 log "Code:----$code---------------"
208 log "Expected status $exitStatus got $status"
209 log "Expected result: [list $expectedResult]"
210 log " Got result: [list $result]"
212 log "errorCode = $::errorCode"
219 # Внутренние (неэкспортируемые)процедуры
224 # Сравнение списков с учетом того что некоторые элементы могут быть
225 # метками времени, которые проверяются с точностью +-секунда
226 # Параметр time - список, каждый элемент которого является индексом
227 # элемента в списке, либо списком индексов во вложенных списках
229 proc listcompare {list1 list2 time} {
231 if {[llength $e]>1} {
232 lappend a([lindex $e 0]) [lrange $e 1 end]
237 if {[llength $list1] !=[llength $list2]} {
241 foreach e1 $list1 e2 $list2 {
242 if {![info exists a($i)]} {
243 if {[string compare $e1 $e2]!=0} {
246 } elseif {[llength $a($i)]} {
247 if {![listcompare $e1 $e2 $a($i)]} {
251 if {$e2 == "::test::timestamp"} {
252 set e2 $::test::timestamp
254 if {[regexp {^([[:digit:]]+)\+([[:digit:]]+)$} $e2 m start delta]} {
255 if {$e1<$start || $e1 >$start+$delta} {
258 } elseif {abs($e1-$e2)>1} {
266 # Перекодирует строку из кодировки скрипта (assumed 1251)
267 # в текущую системную
269 if {[encoding system] == "utf-8" } {
272 return [encoding convertfrom cp1251 $string]
278 return \[encoding convertfrom cp1251 \[encoding convertto [encoding system] \$string\]\]
287 puts $logchannel $message
290 # Вызывается при начале теста
292 proc logbegin {testname} {
296 puts -nonewline [rus [format "Тест%5d: %-60s:" $no [string range $testname 0 59]]]
298 set curtest $testname
299 log [rus "Тест $no: $testname start"]
302 # Вызывается при пропуске теста
304 proc logskip {testname reason} {
308 puts "[rus [format "Тест%5d: %-60s:" $no [string rang $testname 0 59]]]skipped "
309 log "[rus "Тест $no: skipped "][expr {$reason=="platform"?"on
310 the platform $::tcl_platform(platform)":"due to failed prerequisites"}]:[rus $testname]"
312 if {$reason == "platform"} {
320 # Вызывается при игнорировании теста
322 proc logmiss {testname reason} {
325 puts "[rus [format "Тест%5d: %-60s:" $no [string rang $testname 0 59]]]missed "
326 log "[rus "Тест $no: missed "][expr {$reason=="platform"?"on
327 the platform $::tcl_platform(platform)":"by reason: $reason"}]:[rus $testname]"
333 # Вызывается конце теста и с параметром ok или failed
335 proc logend {status} {
341 log [rus "Тест $no: $curtest ends $status"]
344 #####################################################################
345 # Вспомогательные процедуры, не специфичные для тестируемого
347 #####################################################################
350 # Записывает данные из data в файл name. По умолчанию пишет в
351 # текущей системной кодировке. Можно указать кодировку явно третьим
354 proc makeFile {name data {encoding {}}} {
356 setFileEncoding $f $encoding
357 puts -nonewline $f $data
360 proc setFileEncoding {f encoding} {
361 if {[string length $encoding]} {
362 if {"$encoding" == "binary"} {
363 fconfigure $f -translation binary
365 fconfigure $f -encoding $encoding
370 # Возвращает содeржимое файла
373 proc getFile {filename {encoding {}}} {
374 set f [open $filename]
375 setFileEncoding $f $encoding
381 # Возвращает содержимое бинарного файла. Для совместимости со старыми
384 proc getfile {filename} {
385 return [getFile $filename binary]
388 # Зачитывает указанный файл, удаляет его и возвращает содержимое.
389 # По умолчанию читает файл в текущей системной кодировке. Можно
390 # указать кодировку явно вторым аргументом.
393 proc readAndDel {name {encoding {}}} {
395 setFileEncoding $f $encoding
404 # Защищает файл от записи средствами операционной системы
405 # denywrite filename ?boolean?
406 # Если boolean не указан, или он true, файл становится read-only
407 # Если указан - readwrite (для владельца. Впрочем для не-владельца все
408 # равно не сработает)
410 proc denyWrite {filename {deny 1}} {
412 if {$tcl_platform(platform) == "unix"} {
413 set cur_attr [file attributes $filename -permissions]
415 set new_attr [expr {$cur_attr &~ 0200}]
417 set new_attr [expr {$cur_attr | 0200}]
419 file attributes $filename -permissions $new_attr
421 file attributes $filename -readonly $deny
425 # Записывает в лог 16-ричный дамп указанной переменной
428 proc hexdump {data } {
429 while {[string length $data]} {
430 set block [string range $data 0 15]
431 set data [string replace $data 0 15]
432 binary scan [encoding convertto $block] c* list
436 append line [format "%02x " [expr $code>=0?$code:$code +256]]
441 append line [string repeat " " [expr 56-[string length $line]]]
442 regsub -all "\[\0-\37\]" $block . printable
443 append line [rus $printable]
447 namespace export test start_tests end_tests test_log rus log\
448 makeFile readAndDel hexdump denyWrite getFile getfile
450 namespace import ::test::*
452 package provide test 0.2