occt-gltf-addon-linux-x64 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/LICENSE_LGPL_21.txt +502 -0
  2. package/OCCT_LGPL_EXCEPTION.txt +10 -0
  3. package/README.md +2 -2
  4. package/index.js +63 -1
  5. package/lib/libTKBO.so.8.0.0 +0 -0
  6. package/lib/libTKBRep.so.8.0.0 +0 -0
  7. package/lib/libTKBin.so.8.0.0 +0 -0
  8. package/lib/libTKBinL.so.8.0.0 +0 -0
  9. package/lib/libTKBinTObj.so.8.0.0 +0 -0
  10. package/lib/libTKBinXCAF.so.8.0.0 +0 -0
  11. package/lib/libTKBool.so.8.0.0 +0 -0
  12. package/lib/libTKCAF.so.8.0.0 +0 -0
  13. package/lib/libTKCDF.so.8.0.0 +0 -0
  14. package/lib/libTKDE.so.8.0.0 +0 -0
  15. package/lib/libTKDECascade.so.8.0.0 +0 -0
  16. package/lib/libTKDEIGES.so.8.0.0 +0 -0
  17. package/lib/libTKDEOBJ.so.8.0.0 +0 -0
  18. package/lib/libTKDEPLY.so.8.0.0 +0 -0
  19. package/lib/libTKDESTEP.so.8.0.0 +0 -0
  20. package/lib/libTKDESTL.so.8.0.0 +0 -0
  21. package/lib/libTKDEVRML.so.8.0.0 +0 -0
  22. package/lib/libTKExpress.so.8.0.0 +0 -0
  23. package/lib/libTKFeat.so.8.0.0 +0 -0
  24. package/lib/libTKFillet.so.8.0.0 +0 -0
  25. package/lib/libTKG2d.so.8.0.0 +0 -0
  26. package/lib/libTKG3d.so.8.0.0 +0 -0
  27. package/lib/libTKGeomAlgo.so.8.0.0 +0 -0
  28. package/lib/libTKGeomBase.so.8.0.0 +0 -0
  29. package/lib/libTKHLR.so.8.0.0 +0 -0
  30. package/lib/libTKHelix.so.8.0.0 +0 -0
  31. package/lib/libTKLCAF.so.8.0.0 +0 -0
  32. package/lib/libTKMath.so.8.0.0 +0 -0
  33. package/lib/libTKMesh.so.8.0.0 +0 -0
  34. package/lib/libTKOffset.so.8.0.0 +0 -0
  35. package/lib/libTKPrim.so.8.0.0 +0 -0
  36. package/lib/libTKRWMesh.so.8.0.0 +0 -0
  37. package/lib/libTKService.so.8.0.0 +0 -0
  38. package/lib/libTKShHealing.so.8.0.0 +0 -0
  39. package/lib/libTKStd.so.8.0.0 +0 -0
  40. package/lib/libTKStdL.so.8.0.0 +0 -0
  41. package/lib/libTKTObj.so.8.0.0 +0 -0
  42. package/lib/libTKTopAlgo.so.8.0.0 +0 -0
  43. package/lib/libTKV3d.so.8.0.0 +0 -0
  44. package/lib/libTKVCAF.so.8.0.0 +0 -0
  45. package/lib/libTKXCAF.so.8.0.0 +0 -0
  46. package/lib/libTKXMesh.so.8.0.0 +0 -0
  47. package/lib/libTKXSBase.so.8.0.0 +0 -0
  48. package/lib/libTKXml.so.8.0.0 +0 -0
  49. package/lib/libTKXmlL.so.8.0.0 +0 -0
  50. package/lib/libTKXmlTObj.so.8.0.0 +0 -0
  51. package/lib/libTKXmlXCAF.so.8.0.0 +0 -0
  52. package/lib/libTKernel.so.8.0.0 +0 -0
  53. package/lib/libdraco.so.1 +0 -0
  54. package/lib/libtbb.so.2 +0 -0
  55. package/lib/libtbbmalloc.so.2 +0 -0
  56. package/lib/libtbbmalloc_proxy.so.2 +0 -0
  57. package/occt_gltf_addon.node +0 -0
  58. package/package.json +6 -2
  59. package/resources/BOPAlgo/BOPAlgo.msg +129 -0
  60. package/resources/BOPAlgo/FILES +1 -0
  61. package/resources/DrawResources/CURVES.tcl +38 -0
  62. package/resources/DrawResources/CheckCommands.tcl +1206 -0
  63. package/resources/DrawResources/DrawDefault +130 -0
  64. package/resources/DrawResources/DrawPlugin +64 -0
  65. package/resources/DrawResources/DrawTK.tcl +623 -0
  66. package/resources/DrawResources/FILES +16 -0
  67. package/resources/DrawResources/Geometry.tcl +96 -0
  68. package/resources/DrawResources/InitEnvironment.tcl +50 -0
  69. package/resources/DrawResources/Move.tcl +85 -0
  70. package/resources/DrawResources/OCC_logo.png +0 -0
  71. package/resources/DrawResources/PROFIL.tcl +726 -0
  72. package/resources/DrawResources/SCAN.tcl +192 -0
  73. package/resources/DrawResources/SURFACES.tcl +35 -0
  74. package/resources/DrawResources/StandardCommands.tcl +451 -0
  75. package/resources/DrawResources/StandardViews.tcl +284 -0
  76. package/resources/DrawResources/TKTopTest.tcl +27 -0
  77. package/resources/DrawResources/TestCommands.tcl +2969 -0
  78. package/resources/DrawResources/Vector.tcl +402 -0
  79. package/resources/DrawResources/dfb_attribns.gif +0 -0
  80. package/resources/DrawResources/dfb_attribute.gif +0 -0
  81. package/resources/DrawResources/dfb_folder.gif +0 -0
  82. package/resources/DrawResources/dftree.tcl +381 -0
  83. package/resources/DrawResources/lamp.ico +0 -0
  84. package/resources/SHMessage/FILES +2 -0
  85. package/resources/SHMessage/SHAPE.fr +267 -0
  86. package/resources/SHMessage/SHAPE.us +267 -0
  87. package/resources/Shaders/Declarations.glsl +276 -0
  88. package/resources/Shaders/DeclarationsImpl.glsl +121 -0
  89. package/resources/Shaders/Display.fs +157 -0
  90. package/resources/Shaders/FILES +27 -0
  91. package/resources/Shaders/LightShadow.glsl +48 -0
  92. package/resources/Shaders/PBRCookTorrance.glsl +20 -0
  93. package/resources/Shaders/PBRDirectionalLight.glsl +20 -0
  94. package/resources/Shaders/PBRDistribution.glsl +9 -0
  95. package/resources/Shaders/PBREnvBaking.fs +226 -0
  96. package/resources/Shaders/PBREnvBaking.vs +55 -0
  97. package/resources/Shaders/PBRFresnel.glsl +36 -0
  98. package/resources/Shaders/PBRGeometry.glsl +13 -0
  99. package/resources/Shaders/PBRIllumination.glsl +28 -0
  100. package/resources/Shaders/PBRPointLight.glsl +27 -0
  101. package/resources/Shaders/PBRSpotLight.glsl +45 -0
  102. package/resources/Shaders/PathtraceBase.fs +993 -0
  103. package/resources/Shaders/PhongDirectionalLight.glsl +29 -0
  104. package/resources/Shaders/PhongPointLight.glsl +36 -0
  105. package/resources/Shaders/PhongShading.fs +191 -0
  106. package/resources/Shaders/PhongShading.vs +43 -0
  107. package/resources/Shaders/PhongSpotLight.glsl +52 -0
  108. package/resources/Shaders/PointLightAttenuation.glsl +35 -0
  109. package/resources/Shaders/RaytraceBase.fs +1236 -0
  110. package/resources/Shaders/RaytraceBase.vs +12 -0
  111. package/resources/Shaders/RaytraceRender.fs +134 -0
  112. package/resources/Shaders/RaytraceSmooth.fs +80 -0
  113. package/resources/Shaders/SkydomBackground.fs +300 -0
  114. package/resources/Shaders/TangentSpaceNormal.glsl +17 -0
  115. package/resources/StdResource/FILES +6 -0
  116. package/resources/StdResource/MigrationSheet.txt +21 -0
  117. package/resources/StdResource/Plugin +52 -0
  118. package/resources/StdResource/Standard +25 -0
  119. package/resources/StdResource/StandardLite +22 -0
  120. package/resources/StdResource/TObj +17 -0
  121. package/resources/StdResource/XCAF +50 -0
  122. package/resources/TObj/FILES +1 -0
  123. package/resources/TObj/TObj.msg +85 -0
  124. package/resources/Textures/1d_elevation.rgb +0 -0
  125. package/resources/Textures/2d_MatraDatavision.rgb +0 -0
  126. package/resources/Textures/2d_alienskin.rgb +0 -0
  127. package/resources/Textures/2d_aluminum.rgb +0 -0
  128. package/resources/Textures/2d_blue_rock.rgb +0 -0
  129. package/resources/Textures/2d_bluewhite_paper.rgb +0 -0
  130. package/resources/Textures/2d_brushed.rgb +0 -0
  131. package/resources/Textures/2d_bubbles.rgb +0 -0
  132. package/resources/Textures/2d_bumps.rgb +0 -0
  133. package/resources/Textures/2d_cast.rgb +0 -0
  134. package/resources/Textures/2d_chess.rgba +0 -0
  135. package/resources/Textures/2d_chipbd.rgb +0 -0
  136. package/resources/Textures/2d_clouds.rgb +0 -0
  137. package/resources/Textures/2d_flesh.rgb +0 -0
  138. package/resources/Textures/2d_floor.rgb +0 -0
  139. package/resources/Textures/2d_galvnisd.rgb +0 -0
  140. package/resources/Textures/2d_grass.rgb +0 -0
  141. package/resources/Textures/2d_knurl.rgb +0 -0
  142. package/resources/Textures/2d_maple.rgb +0 -0
  143. package/resources/Textures/2d_marble.rgb +0 -0
  144. package/resources/Textures/2d_mottled.rgb +0 -0
  145. package/resources/Textures/2d_rain.rgb +0 -0
  146. package/resources/Textures/2d_rock.rgb +0 -0
  147. package/resources/Textures/FILES +31 -0
  148. package/resources/Textures/env_clouds.rgb +0 -0
  149. package/resources/Textures/env_cv.rgb +0 -0
  150. package/resources/Textures/env_lines.rgb +0 -0
  151. package/resources/Textures/env_medit.rgb +0 -0
  152. package/resources/Textures/env_pearl.rgb +0 -0
  153. package/resources/Textures/env_road.rgb +0 -0
  154. package/resources/Textures/env_sky1.rgb +0 -0
  155. package/resources/Textures/env_sky2.rgb +0 -0
  156. package/resources/UnitsAPI/CurrentUnits +74 -0
  157. package/resources/UnitsAPI/FILES +4 -0
  158. package/resources/UnitsAPI/MDTVBaseUnits +70 -0
  159. package/resources/UnitsAPI/MDTVCurrentUnits +74 -0
  160. package/resources/UnitsAPI/Units.dat +481 -0
  161. package/resources/XRResources/FILES +10 -0
  162. package/resources/XRResources/occtvr_actions.json +225 -0
  163. package/resources/XRResources/occtvr_bindings_generic.json +87 -0
  164. package/resources/XRResources/occtvr_bindings_holographic_hmd.json +18 -0
  165. package/resources/XRResources/occtvr_bindings_index_hmd.json +18 -0
  166. package/resources/XRResources/occtvr_bindings_rift.json +18 -0
  167. package/resources/XRResources/occtvr_bindings_touch.json +160 -0
  168. package/resources/XRResources/occtvr_bindings_vive.json +18 -0
  169. package/resources/XRResources/occtvr_bindings_vive_controller.json +139 -0
  170. package/resources/XRResources/occtvr_bindings_vive_cosmos.json +18 -0
  171. package/resources/XRResources/occtvr_bindings_vive_pro.json +18 -0
  172. package/resources/XSMessage/FILES +2 -0
  173. package/resources/XSMessage/XSTEP.fr +1026 -0
  174. package/resources/XSMessage/XSTEP.us +1023 -0
  175. package/resources/XSTEPResource/FILES +2 -0
  176. package/resources/XSTEPResource/IGES +65 -0
  177. package/resources/XSTEPResource/STEP +68 -0
  178. package/resources/XmlOcafResource/FILES +11 -0
  179. package/resources/XmlOcafResource/XmlOcaf.xsd +131 -0
  180. package/resources/XmlOcafResource/XmlOcaf_SmallTypes.xsd +217 -0
  181. package/resources/XmlOcafResource/XmlOcaf_TDF.xsd +33 -0
  182. package/resources/XmlOcafResource/XmlOcaf_TDataStd.xsd +230 -0
  183. package/resources/XmlOcafResource/XmlOcaf_TDataStd_Name.xsd +37 -0
  184. package/resources/XmlOcafResource/XmlOcaf_TDocStd.xsd +37 -0
  185. package/resources/XmlOcafResource/XmlOcaf_TFunction.xsd +38 -0
  186. package/resources/XmlOcafResource/XmlOcaf_TNaming.xsd +128 -0
  187. package/resources/XmlOcafResource/XmlOcaf_TNaming_NamedShape.xsd +97 -0
  188. package/resources/XmlOcafResource/XmlOcaf_TPrsStd.xsd +42 -0
  189. package/resources/XmlOcafResource/XmlXcaf.xsd +109 -0
  190. package/resources/samples/tcl/ANC101.tcl +282 -0
  191. package/resources/samples/tcl/DataExchangeDemo.tcl +85 -0
  192. package/resources/samples/tcl/MBBGehauseRohteil.tcl +268 -0
  193. package/resources/samples/tcl/ModelingDemo.tcl +120 -0
  194. package/resources/samples/tcl/Penrose.tcl +87 -0
  195. package/resources/samples/tcl/Readme.txt +4 -0
  196. package/resources/samples/tcl/VisualizationDemo.tcl +162 -0
  197. package/resources/samples/tcl/bottle.tcl +115 -0
  198. package/resources/samples/tcl/cad.tcl +63 -0
  199. package/resources/samples/tcl/cpu.tcl +327 -0
  200. package/resources/samples/tcl/cutter.tcl +91 -0
  201. package/resources/samples/tcl/dimensions.tcl +83 -0
  202. package/resources/samples/tcl/drill.tcl +147 -0
  203. package/resources/samples/tcl/logo2019.tcl +89 -0
  204. package/resources/samples/tcl/markers.tcl +76 -0
  205. package/resources/samples/tcl/materials.tcl +75 -0
  206. package/resources/samples/tcl/pathtrace_ball.tcl +79 -0
  207. package/resources/samples/tcl/pathtrace_cube.tcl +85 -0
  208. package/resources/samples/tcl/pathtrace_materials.tcl +229 -0
  209. package/resources/samples/tcl/pencil.tcl +64 -0
  210. package/resources/samples/tcl/raytrace.tcl +44 -0
  211. package/resources/samples/tcl/snowflake.tcl +161 -0
  212. package/resources/samples/tcl/spheres.tcl +148 -0
  213. package/resources/samples/tcl/vis_pbr_spheres.tcl +94 -0
  214. package/resources/samples/tcl/xde.tcl +47 -0
@@ -0,0 +1,2969 @@
1
+ # Copyright (c) 2013-2014 OPEN CASCADE SAS
2
+ #
3
+ # This file is part of Open CASCADE Technology software library.
4
+ #
5
+ # This library is free software; you can redistribute it and/or modify it under
6
+ # the terms of the GNU Lesser General Public License version 2.1 as published
7
+ # by the Free Software Foundation, with special exception defined in the file
8
+ # OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9
+ # distribution for complete text of the license and disclaimer of any warranty.
10
+ #
11
+ # Alternatively, this file may be used under the terms of Open CASCADE
12
+ # commercial license or contractual agreement.
13
+
14
+ ############################################################################
15
+ # This file defines scripts for execution of OCCT tests.
16
+ # It should be loaded automatically when DRAW is started, and provides
17
+ # top-level commands starting with 'test'. Type 'help test' to get their
18
+ # synopsis.
19
+ # See OCCT Tests User Guide for description of the test system.
20
+ #
21
+ # Note: procedures with names starting with underscore are for internal use
22
+ # inside the test system.
23
+ ############################################################################
24
+
25
+ # Default verbose level for command _run_test
26
+ set _tests_verbose 0
27
+
28
+ # regexp for parsing test case results in summary log
29
+ set _test_case_regexp {^CASE\s+([\w.-]+)\s+([\w.-]+)\s+([\w.-]+)\s*:\s*([\w]+)(.*)}
30
+
31
+ # Basic command to run indicated test case in DRAW
32
+ help test {
33
+ Run specified test case
34
+ Use: test group grid casename [options...]
35
+ Allowed options are:
36
+ -echo: all commands and results are echoed immediately,
37
+ but log is not saved and summary is not produced
38
+ It is also possible to use "1" instead of "-echo"
39
+ If echo is OFF, log is stored in memory and only summary
40
+ is output (the log can be obtained with command "dlog get")
41
+ -outfile filename: set log file (should be non-existing),
42
+ it is possible to save log file in text file or
43
+ in html file(with snapshot), for that "filename"
44
+ should have ".html" extension
45
+ -overwrite: force writing log in existing file
46
+ -beep: play sound signal at the end of the test
47
+ -errors: show all lines from the log report that are recognized as errors
48
+ This key will be ignored if the "-echo" key is already set.
49
+ }
50
+ proc test {group grid casename {args {}}} {
51
+ # set default values of arguments
52
+ set echo 0
53
+ set errors 0
54
+ set logfile ""
55
+ set overwrite 0
56
+ set signal 0
57
+
58
+ # get test case paths (will raise error if input is invalid)
59
+ _get_test $group $grid $casename dir gridname casefile
60
+
61
+ # check arguments
62
+ for {set narg 0} {$narg < [llength $args]} {incr narg} {
63
+ set arg [lindex $args $narg]
64
+ # if echo specified as "-echo", convert it to bool
65
+ if { $arg == "-echo" || $arg == "1" } {
66
+ set echo t
67
+ continue
68
+ }
69
+
70
+ # output log file
71
+ if { $arg == "-outfile" } {
72
+ incr narg
73
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
74
+ set logfile [lindex $args $narg]
75
+ } else {
76
+ error "Option -outfile requires argument"
77
+ }
78
+ continue
79
+ }
80
+
81
+ # allow overwrite existing log
82
+ if { $arg == "-overwrite" } {
83
+ set overwrite 1
84
+ continue
85
+ }
86
+
87
+ # sound signal at the end of the test
88
+ if { $arg == "-beep" } {
89
+ set signal t
90
+ continue
91
+ }
92
+
93
+ # if errors specified as "-errors", convert it to bool
94
+ if { $arg == "-errors" } {
95
+ set errors t
96
+ continue
97
+ }
98
+
99
+ # unsupported option
100
+ error "Error: unsupported option \"$arg\""
101
+ }
102
+ # run test
103
+ uplevel _run_test $dir $group $gridname $casefile $echo
104
+
105
+ # check log
106
+ if { !$echo } {
107
+ _check_log $dir $group $gridname $casename $errors [dlog get] summary html_log
108
+
109
+ # create log file
110
+ if { ! $overwrite && [file isfile $logfile] } {
111
+ error "Error: Specified log file \"$logfile\" exists; please remove it before running test or use -overwrite option"
112
+ }
113
+ if {$logfile != ""} {
114
+ if {[file extension $logfile] == ".html"} {
115
+ if {[regexp {vdump ([^\s\n]+)} $html_log dump snapshot]} {
116
+ catch {file copy -force $snapshot [file rootname $logfile][file extension $snapshot]}
117
+ }
118
+ _log_html $logfile $html_log "Test $group $grid $casename"
119
+ } else {
120
+ _log_save $logfile "[dlog get]\n$summary" "Test $group $grid $casename"
121
+ }
122
+ }
123
+ }
124
+
125
+ # play sound signal at the end of test
126
+ if {$signal} {
127
+ puts "\7\7\7\7"
128
+ }
129
+ return
130
+ }
131
+
132
+ # Basic command to run indicated test case in DRAW
133
+ help testgrid {
134
+ Run all tests, or specified group, or one grid
135
+ Use: testgrid [groupmask [gridmask [casemask]]] [options...]
136
+ Allowed options are:
137
+ -exclude N: exclude group, subgroup or single test case from executing, where
138
+ N is name of group, subgroup or case. Excluded items should be separated by comma.
139
+ Option should be used as the first argument after list of executed groups, grids, and test cases.
140
+ -parallel N: run N parallel processes (default is number of CPUs, 0 to disable)
141
+ -refresh N: save summary logs every N seconds (default 600, minimal 1, 0 to disable)
142
+ -outdir dirname: set log directory (should be empty or non-existing)
143
+ -overwrite: force writing logs in existing non-empty directory
144
+ -xml filename: write XML report for Jenkins (in JUnit-like format)
145
+ -beep: play sound signal at the end of the tests
146
+ -regress dirname: re-run only a set of tests that have been detected as regressions on some previous run.
147
+ -skipped dirname: re-run only a set of tests that have been skipped on some previous run.
148
+ Here "dirname" is path to directory containing results of previous run.
149
+ -skip N: skip first N tests (useful to restart after abort)
150
+ Groups, grids, and test cases to be executed can be specified by list of file
151
+ masks, separated by spaces or comma; default is all (*).
152
+ }
153
+ proc testgrid {args} {
154
+ global env tcl_platform _tests_verbose
155
+
156
+ ######################################################
157
+ # check arguments
158
+ ######################################################
159
+
160
+ # check that environment variable defining paths to test scripts is defined
161
+ if { ! [info exists env(CSF_TestScriptsPath)] ||
162
+ [llength $env(CSF_TestScriptsPath)] <= 0 } {
163
+ error "Error: Environment variable CSF_TestScriptsPath is not defined"
164
+ }
165
+
166
+ # treat options
167
+ set parallel [_get_nb_cpus]
168
+ set refresh 60
169
+ set logdir ""
170
+ set overwrite 0
171
+ set xmlfile ""
172
+ set signal 0
173
+ set exc_group 0
174
+ set exc_grid 0
175
+ set exc_case 0
176
+ set regress 0
177
+ set skipped 0
178
+ set logdir_regr ""
179
+ set logdir_skip ""
180
+ set nbskip 0
181
+ for {set narg 0} {$narg < [llength $args]} {incr narg} {
182
+ set arg [lindex $args $narg]
183
+
184
+ # parallel execution
185
+ if { $arg == "-parallel" } {
186
+ incr narg
187
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
188
+ set parallel [expr [lindex $args $narg]]
189
+ } else {
190
+ error "Option -parallel requires argument"
191
+ }
192
+ continue
193
+ }
194
+
195
+ # refresh logs time
196
+ if { $arg == "-refresh" } {
197
+ incr narg
198
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
199
+ set refresh [expr [lindex $args $narg]]
200
+ } else {
201
+ error "Option -refresh requires argument"
202
+ }
203
+ continue
204
+ }
205
+
206
+ # output directory
207
+ if { $arg == "-outdir" } {
208
+ incr narg
209
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
210
+ set logdir [lindex $args $narg]
211
+ } else {
212
+ error "Option -outdir requires argument"
213
+ }
214
+ continue
215
+ }
216
+
217
+ # allow overwrite logs
218
+ if { $arg == "-overwrite" } {
219
+ set overwrite 1
220
+ continue
221
+ }
222
+
223
+ # refresh logs time
224
+ if { $arg == "-xml" } {
225
+ incr narg
226
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
227
+ set xmlfile [lindex $args $narg]
228
+ }
229
+ if { $xmlfile == "" } {
230
+ set xmlfile TESTS-summary.xml
231
+ }
232
+ continue
233
+ }
234
+
235
+ # sound signal at the end of the test
236
+ if { $arg == "-beep" } {
237
+ set signal t
238
+ continue
239
+ }
240
+
241
+ # re-run only a set of tests that have been detected as regressions on some previous run
242
+ if { $arg == "-regress" || $arg == "-skipped" } {
243
+ incr narg
244
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
245
+ if { $arg == "-regress" } {
246
+ set logdir_regr [file normalize [string trim [lindex $args $narg]]]
247
+ set regress 1
248
+ } else {
249
+ set logdir_skip [file normalize [string trim [lindex $args $narg]]]
250
+ set skipped 1
251
+ }
252
+ } else {
253
+ error "Option $arg requires argument"
254
+ }
255
+ continue
256
+ }
257
+
258
+ # skip N first tests
259
+ if { $arg == "-skip" } {
260
+ incr narg
261
+ if { $narg < [llength $args] && [string is integer [lindex $args $narg]] } {
262
+ set nbskip [lindex $args $narg]
263
+ } else {
264
+ error "Option -skip requires integer argument"
265
+ }
266
+ continue
267
+ }
268
+
269
+ # exclude group, subgroup or single test case from executing
270
+ if { $arg == "-exclude" } {
271
+ incr narg
272
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
273
+ set argts $args
274
+ set idx_begin [string first " -ex" $argts]
275
+ if { ${idx_begin} != "-1" } {
276
+ set argts [string replace $argts 0 $idx_begin]
277
+ }
278
+ set idx_exclude [string first "exclude" $argts]
279
+ if { ${idx_exclude} != "-1" } {
280
+ set argts [string replace $argts 0 $idx_exclude+7]
281
+ }
282
+ set idx [string first " -" $argts]
283
+ if { ${idx} != "-1" } {
284
+ set argts [string replace $argts $idx end]
285
+ }
286
+ set argts [split $argts ,]
287
+ foreach argt $argts {
288
+ if { [llength $argt] == 1 } {
289
+ lappend exclude_group $argt
290
+ set exc_group 1
291
+ } elseif { [llength $argt] == 2 } {
292
+ lappend exclude_grid $argt
293
+ set exc_grid 1
294
+ incr narg
295
+ } elseif { [llength $argt] == 3 } {
296
+ lappend exclude_case $argt
297
+ set exc_case 1
298
+ incr narg
299
+ incr narg
300
+ }
301
+ }
302
+ } else {
303
+ error "Option -exclude requires argument"
304
+ }
305
+ continue
306
+ }
307
+
308
+ # unsupported option
309
+ if { [regexp {^-} $arg] } {
310
+ error "Error: unsupported option \"$arg\""
311
+ }
312
+
313
+ # treat arguments not recognized as options as group and grid names
314
+ if { ! [info exists groupmask] } {
315
+ set groupmask [split $arg ,]
316
+ } elseif { ! [info exists gridmask] } {
317
+ set gridmask [split $arg ,]
318
+ } elseif { ! [info exists casemask] } {
319
+ set casemask [split $arg ,]
320
+ } else {
321
+ error "Error: cannot interpret argument $narg ($arg)"
322
+ }
323
+ }
324
+
325
+ # check that target log directory is empty or does not exist
326
+ set logdir [file normalize [string trim $logdir]]
327
+ if { $logdir == "" } {
328
+ # if specified logdir is empty string, generate unique name like
329
+ # results/<branch>_<timestamp>
330
+ set prefix ""
331
+ if { ! [catch {exec git branch} gitout] &&
332
+ [regexp {[*] ([\w-]+)} $gitout res branch] } {
333
+ set prefix "${branch}_"
334
+ }
335
+ set logdir "results/${prefix}[clock format [clock seconds] -format {%Y-%m-%dT%H%M}]"
336
+
337
+ set logdir [file normalize $logdir]
338
+ }
339
+ if { [file isdirectory $logdir] && ! $overwrite && ! [catch {glob -directory $logdir *}] } {
340
+ error "Error: Specified log directory \"$logdir\" is not empty; please clean it before running tests"
341
+ }
342
+ if { [catch {file mkdir $logdir}] || ! [file writable $logdir] } {
343
+ error "Error: Cannot create directory \"$logdir\", or it is not writable"
344
+ }
345
+
346
+ # masks for search of test groups, grids, and cases
347
+ if { ! [info exists groupmask] } { set groupmask * }
348
+ if { ! [info exists gridmask ] } { set gridmask * }
349
+ if { ! [info exists casemask ] } { set casemask * }
350
+
351
+ # Find test cases with FAILED and IMPROVEMENT statuses in previous run
352
+ # if option "regress" is given
353
+ set rerun_group_grid_case {}
354
+
355
+ if { ${regress} > 0 || ${skipped} > 0 } {
356
+ if { "${groupmask}" != "*"} {
357
+ lappend rerun_group_grid_case [list $groupmask $gridmask $casemask]
358
+ }
359
+ } else {
360
+ lappend rerun_group_grid_case [list $groupmask $gridmask $casemask]
361
+ }
362
+
363
+ if { ${regress} > 0 } {
364
+ if { [file exists ${logdir_regr}/tests.log] } {
365
+ set fd [open ${logdir_regr}/tests.log]
366
+ while { [gets $fd line] >= 0 } {
367
+ if {[regexp {CASE ([^\s]+) ([^\s]+) ([^\s]+): FAILED} $line dump group grid casename] ||
368
+ [regexp {CASE ([^\s]+) ([^\s]+) ([^\s]+): IMPROVEMENT} $line dump group grid casename]} {
369
+ lappend rerun_group_grid_case [list $group $grid $casename]
370
+ }
371
+ }
372
+ close $fd
373
+ } else {
374
+ error "Error: file ${logdir_regr}/tests.log is not found, check your input arguments!"
375
+ }
376
+ }
377
+ if { ${skipped} > 0 } {
378
+ if { [file exists ${logdir_skip}/tests.log] } {
379
+ set fd [open ${logdir_skip}/tests.log]
380
+ while { [gets $fd line] >= 0 } {
381
+ if {[regexp {CASE ([^\s]+) ([^\s]+) ([^\s]+): SKIPPED} $line dump group grid casename] } {
382
+ lappend rerun_group_grid_case [list $group $grid $casename]
383
+ }
384
+ }
385
+ close $fd
386
+ } else {
387
+ error "Error: file ${logdir_skip}/tests.log is not found, check your input arguments!"
388
+ }
389
+ }
390
+
391
+ ######################################################
392
+ # prepare list of tests to be performed
393
+ ######################################################
394
+
395
+ # list of tests, each defined by a list of:
396
+ # test scripts directory
397
+ # group (subfolder) name
398
+ # grid (subfolder) name
399
+ # test case name
400
+ # path to test case file
401
+ set tests_list {}
402
+
403
+ foreach group_grid_case ${rerun_group_grid_case} {
404
+ set groupmask [lindex $group_grid_case 0]
405
+ set gridmask [lindex $group_grid_case 1]
406
+ set casemask [lindex $group_grid_case 2]
407
+
408
+ # iterate by all script paths
409
+ foreach dir [lsort -unique [_split_path $env(CSF_TestScriptsPath)]] {
410
+ # protection against empty paths
411
+ set dir [string trim $dir]
412
+ if { $dir == "" } { continue }
413
+
414
+ if { $_tests_verbose > 0 } { _log_and_puts log "Examining tests directory $dir" }
415
+
416
+ # check that directory exists
417
+ if { ! [file isdirectory $dir] } {
418
+ _log_and_puts log "Warning: directory $dir listed in CSF_TestScriptsPath does not exist, skipped"
419
+ continue
420
+ }
421
+
422
+ # search all directories in the current dir with specified mask
423
+ if [catch {glob -directory $dir -tail -types d {*}$groupmask} groups] { continue }
424
+
425
+ # exclude selected groups from all groups
426
+ if { ${exc_group} > 0 } {
427
+ foreach exclude_group_element ${exclude_group} {
428
+ set idx [lsearch $groups "${exclude_group_element}"]
429
+ if { ${idx} != "-1" } {
430
+ set groups [lreplace $groups $idx $idx]
431
+ } else {
432
+ continue
433
+ }
434
+ }
435
+ }
436
+
437
+ # iterate by groups
438
+ if { $_tests_verbose > 0 } { _log_and_puts log "Groups to be executed: $groups" }
439
+ foreach group [lsort -dictionary $groups] {
440
+ if { $_tests_verbose > 0 } { _log_and_puts log "Examining group directory $group" }
441
+
442
+ # file grids.list must exist: it defines sequence of grids in the group
443
+ if { ! [file exists $dir/$group/grids.list] } {
444
+ _log_and_puts log "Warning: directory $dir/$group does not contain file grids.list, skipped"
445
+ continue
446
+ }
447
+
448
+ # read grids.list file and make a list of grids to be executed
449
+ set gridlist {}
450
+ set fd [open $dir/$group/grids.list]
451
+ set nline 0
452
+ while { [gets $fd line] >= 0 } {
453
+ incr nline
454
+
455
+ # skip comments and empty lines
456
+ if { [regexp "\[ \t\]*\#.*" $line] } { continue }
457
+ if { [string trim $line] == "" } { continue }
458
+
459
+ # get grid id and name
460
+ if { ! [regexp "^\(\[0-9\]+\)\[ \t\]*\(\[A-Za-z0-9_.-\]+\)\$" $line res gridid grid] } {
461
+ _log_and_puts log "Warning: cannot recognize line $nline in file $dir/$group/grids.list as \"gridid gridname\"; ignored"
462
+ continue
463
+ }
464
+
465
+ # check that grid fits into the specified mask
466
+ foreach mask $gridmask {
467
+ if { $mask == $gridid || [string match $mask $grid] } {
468
+ lappend gridlist $grid
469
+ }
470
+ }
471
+ }
472
+ close $fd
473
+
474
+ # exclude selected grids from all grids
475
+ if { ${exc_grid} > 0 } {
476
+ foreach exclude_grid_element ${exclude_grid} {
477
+ set exclude_elem [lindex $exclude_grid_element end]
478
+ set idx [lsearch $gridlist "${exclude_elem}"]
479
+ if { ${idx} != "-1" } {
480
+ set gridlist [lreplace $gridlist $idx $idx]
481
+ } else {
482
+ continue
483
+ }
484
+ }
485
+ }
486
+
487
+ # iterate by all grids
488
+ foreach grid $gridlist {
489
+
490
+ # check if this grid is aliased to another one
491
+ set griddir $dir/$group/$grid
492
+ if { [file exists $griddir/cases.list] } {
493
+ set fd [open $griddir/cases.list]
494
+ if { [gets $fd line] >= 0 } {
495
+ set griddir [file normalize $dir/$group/$grid/[string trim $line]]
496
+ }
497
+ close $fd
498
+ }
499
+
500
+ # check if grid directory actually exists
501
+ if { ! [file isdirectory $griddir] } {
502
+ _log_and_puts log "Error: tests directory for grid $grid ($griddir) is missing; skipped"
503
+ continue
504
+ }
505
+
506
+ # create directory for logging test results
507
+ if { $logdir != "" } { file mkdir $logdir/$group/$grid }
508
+
509
+ # iterate by all tests in the grid directory
510
+ if { [catch {glob -directory $griddir -type f {*}$casemask} testfiles] } { continue }
511
+
512
+ # exclude selected test cases from all testfiles
513
+ if { ${exc_case} > 0 } {
514
+ foreach exclude_case_element ${exclude_case} {
515
+ set exclude_casegroup_elem [lindex $exclude_case_element end-2]
516
+ set exclude_casegrid_elem [lindex $exclude_case_element end-1]
517
+ set exclude_elem [lindex $exclude_case_element end]
518
+ if { ${exclude_casegrid_elem} == "${grid}" } {
519
+ set idx [lsearch $testfiles "${dir}/${exclude_casegroup_elem}/${exclude_casegrid_elem}/${exclude_elem}"]
520
+ if { ${idx} != "-1" } {
521
+ set testfiles [lreplace $testfiles $idx $idx]
522
+ } else {
523
+ continue
524
+ }
525
+ }
526
+ }
527
+ }
528
+
529
+ foreach casefile [lsort -dictionary $testfiles] {
530
+ # filter out files with reserved names
531
+ set casename [file tail $casefile]
532
+ if { $casename == "begin" || $casename == "end" ||
533
+ $casename == "parse.rules" } {
534
+ continue
535
+ }
536
+
537
+ if { $nbskip > 0 } {
538
+ incr nbskip -1
539
+ } else {
540
+ # Check if current test matches exclude pattern
541
+ set should_exclude 0
542
+ if { ${exc_case} > 0 } {
543
+ foreach excl $exclude_case {
544
+ if {[string match "$group" [lindex $excl 0]] &&
545
+ [string match "$grid" [lindex $excl 1]] &&
546
+ [string match "$casename" [lindex $excl 2]]} {
547
+ set should_exclude 1
548
+ break
549
+ }
550
+ }
551
+ }
552
+ if {!$should_exclude} {
553
+ lappend tests_list [list $dir $group $grid $casename $casefile]
554
+ }
555
+ }
556
+ }
557
+ }
558
+ }
559
+ }
560
+ }
561
+ if { [llength $tests_list] < 1 } {
562
+ error "Error: no tests are found, check your input arguments and variable CSF_TestScriptsPath!"
563
+ } else {
564
+ puts "Running tests (total [llength $tests_list] test cases)..."
565
+ }
566
+ ######################################################
567
+ # run tests
568
+ ######################################################
569
+
570
+ # log command arguments and environment
571
+ lappend log "Command: testgrid $args"
572
+ lappend log "Host: [info hostname]"
573
+ lappend log "Started on: [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S}]"
574
+ catch {lappend log "DRAW build:\n[dversion]" }
575
+ catch { pload VISUALIZATION; vinit g/v/info -virtual -w 2 -h 2 }
576
+ catch { lappend log "[vglinfo -complete -lineWidth 80]" }
577
+ catch { vclose g/v/info 0 }
578
+ lappend log "Environment:"
579
+ foreach envar [lsort [array names env]] {
580
+ lappend log "$envar=\"$env($envar)\""
581
+ }
582
+ lappend log ""
583
+
584
+ set refresh_timer [clock seconds]
585
+ uplevel dchrono _timer reset
586
+ uplevel dchrono _timer start
587
+
588
+ # if parallel execution is requested, allocate thread pool
589
+ if { $parallel > 0 } {
590
+ if { ! [info exists tcl_platform(threaded)] || [catch {package require Thread}] } {
591
+ _log_and_puts log "Warning: Tcl package Thread is not available, running in sequential mode"
592
+ set parallel 0
593
+ } else {
594
+ set worker [tpool::create -minworkers $parallel -maxworkers $parallel]
595
+ # suspend the pool until all jobs are posted, to prevent blocking of the process
596
+ # of starting / processing jobs by running threads
597
+ catch {tpool::suspend $worker}
598
+ if { $_tests_verbose > 0 } { _log_and_puts log "Executing tests in (up to) $parallel threads" }
599
+ # limit number of jobs in the queue by reasonable value
600
+ # to prevent slowdown due to unnecessary queue processing
601
+ set nbpooled 0
602
+ set nbpooled_max [expr 10 * $parallel]
603
+ set nbpooled_ok [expr 5 * $parallel]
604
+ }
605
+ }
606
+
607
+ # start test cases
608
+ set userbreak 0
609
+ foreach test_def $tests_list {
610
+ # check for user break
611
+ if { $userbreak || "[info commands dbreak]" == "dbreak" && [catch dbreak] } {
612
+ set userbreak 1
613
+ break
614
+ }
615
+
616
+ set dir [lindex $test_def 0]
617
+ set group [lindex $test_def 1]
618
+ set grid [lindex $test_def 2]
619
+ set casename [lindex $test_def 3]
620
+ set casefile [lindex $test_def 4]
621
+
622
+ # command to set tests for generation of image in results directory
623
+ set imgdir_cmd ""
624
+ if { $logdir != "" } { set imgdir_cmd "set imagedir $logdir/$group/$grid" }
625
+
626
+ # prepare command file for running test case in separate instance of DRAW
627
+ set file_cmd "$logdir/$group/$grid/${casename}.tcl"
628
+ set fd_cmd [open $file_cmd w]
629
+
630
+ # UTF-8 encoding is used by default on Linux everywhere, and "unicode" is set
631
+ # by default as encoding of stdin and stdout on Windows in interactive mode;
632
+ # however in batch mode on Windows default encoding is set to system one (e.g. 1252),
633
+ # so we need to set UTF-8 encoding explicitly to have Unicode symbols transmitted
634
+ # correctly between calling and caller processes
635
+ if { "$tcl_platform(platform)" == "windows" } {
636
+ puts $fd_cmd "fconfigure stdout -encoding utf-8"
637
+ puts $fd_cmd "fconfigure stdin -encoding utf-8"
638
+ }
639
+
640
+ # commands to set up and run test
641
+ puts $fd_cmd "$imgdir_cmd"
642
+ puts $fd_cmd "set test_image $casename"
643
+ puts $fd_cmd "_run_test $dir $group $grid $casefile t"
644
+
645
+ # use dlog command to obtain complete output of the test when it is absent (i.e. since OCCT 6.6.0)
646
+ # note: this is not needed if echo is set to 1 in call to _run_test above
647
+ if { ! [catch {dlog get}] } {
648
+ puts $fd_cmd "puts \[dlog get\]"
649
+ } else {
650
+ # else try to use old-style QA_ variables to get more output...
651
+ set env(QA_DUMP) 1
652
+ set env(QA_DUP) 1
653
+ set env(QA_print_command) 1
654
+ }
655
+
656
+ # final 'exit' is needed when running on Linux under VirtualGl
657
+ puts $fd_cmd "exit"
658
+ close $fd_cmd
659
+
660
+ # command to run DRAW with a command file;
661
+ # note that empty string is passed as standard input to avoid possible
662
+ # hang-ups due to waiting for stdin of the launching process
663
+ set command "exec <<{} DRAWEXE -f $file_cmd"
664
+
665
+ # alternative method to run without temporary file; disabled as it needs too many backslashes
666
+ # else {
667
+ # set command "exec <<\"\" DRAWEXE -c $imgdir_cmd\\\; set test_image $casename\\\; \
668
+ # _run_test $dir $group $grid $casefile\\\; \
669
+ # puts \\\[dlog get\\\]\\\; exit"
670
+ # }
671
+
672
+ # run test case, either in parallel or sequentially
673
+ if { $parallel > 0 } {
674
+ # parallel execution
675
+ set job [tpool::post -nowait $worker "catch \"$command\" output; return \$output"]
676
+ set job_def($job) [list $logdir $dir $group $grid $casename]
677
+ incr nbpooled
678
+ if { $nbpooled > $nbpooled_max } {
679
+ _testgrid_process_jobs $worker $nbpooled_ok
680
+ }
681
+ } else {
682
+ # sequential execution
683
+ catch {eval $command} output
684
+ _log_test_case $output $logdir $dir $group $grid $casename log
685
+
686
+ # update summary log with requested period
687
+ if { $logdir != "" && $refresh > 0 && [expr [clock seconds] - $refresh_timer > $refresh] } {
688
+ # update and dump summary
689
+ _log_summarize $logdir $log
690
+ set refresh_timer [clock seconds]
691
+ }
692
+ }
693
+ }
694
+
695
+ # get results of started threads
696
+ if { $parallel > 0 } {
697
+ _testgrid_process_jobs $worker
698
+ # release thread pool
699
+ if { $nbpooled > 0 } {
700
+ tpool::cancel $worker [array names job_def]
701
+ }
702
+ catch {tpool::resume $worker}
703
+ tpool::release $worker
704
+ }
705
+
706
+ uplevel dchrono _timer stop
707
+ set time [lindex [split [uplevel dchrono _timer show] "\n"] 0]
708
+
709
+ if { $userbreak } {
710
+ _log_and_puts log "*********** Stopped by user break ***********"
711
+ set time "${time} \nNote: the process is not finished, stopped by user break!"
712
+ }
713
+
714
+ ######################################################
715
+ # output summary logs and exit
716
+ ######################################################
717
+
718
+ _log_summarize $logdir $log $time
719
+ if { $logdir != "" } {
720
+ puts "Detailed logs are saved in $logdir"
721
+ }
722
+ if { $logdir != "" && $xmlfile != "" } {
723
+ # XML output file is assumed relative to log dir unless it is absolute
724
+ if { [ file pathtype $xmlfile] == "relative" } {
725
+ set xmlfile [file normalize $logdir/$xmlfile]
726
+ }
727
+ _log_xml_summary $logdir $xmlfile $log 0
728
+ puts "XML summary is saved to $xmlfile"
729
+ }
730
+ # play sound signal at the end of test
731
+ if {$signal} {
732
+ puts "\7\7\7\7"
733
+ }
734
+ return
735
+ }
736
+
737
+ # Procedure to regenerate summary log from logs of test cases
738
+ help testsummarize {
739
+ Regenerate summary log in the test directory from logs of test cases.
740
+ This can be necessary if test grids are executed separately (e.g. on
741
+ different stations) or some grids have been re-executed.
742
+ Use: testsummarize dir
743
+ }
744
+ proc testsummarize {dir} {
745
+ global _test_case_regexp
746
+
747
+ if { ! [file isdirectory $dir] } {
748
+ error "Error: \"$dir\" is not a directory"
749
+ }
750
+
751
+ # get summary statements from all test cases in one log
752
+ set log {}
753
+
754
+ # to avoid huge listing of logs, first find all subdirectories and iterate
755
+ # by them, parsing log files in each subdirectory independently
756
+ foreach grid [glob -directory $dir -types d -tails */*] {
757
+ foreach caselog [glob -nocomplain -directory [file join $dir $grid] -types f -tails *.log] {
758
+ set file [file join $dir $grid $caselog]
759
+ set nbfound 0
760
+ set fd [open $file r]
761
+ while { [gets $fd line] >= 0 } {
762
+ if { [regexp $_test_case_regexp $line res grp grd cas status message] } {
763
+ if { "[file join $grid $caselog]" != "[file join $grp $grd ${cas}.log]" } {
764
+ puts "Error: $file contains status line for another test case ($line)"
765
+ }
766
+ lappend log $line
767
+ incr nbfound
768
+ }
769
+ }
770
+ close $fd
771
+
772
+ if { $nbfound != 1 } {
773
+ puts "Error: $file contains $nbfound status lines, expected 1"
774
+ }
775
+ }
776
+ }
777
+
778
+ _log_summarize $dir $log "Summary regenerated from logs at [clock format [clock seconds]]"
779
+ return
780
+ }
781
+
782
+ # Procedure to compare results of two runs of test cases
783
+ help testdiff {
784
+ Compare results of two executions of tests (CPU times, ...)
785
+ Use: testdiff dir1 dir2 [groupname [gridname]] [options...]
786
+ Where dir1 and dir2 are directories containing logs of two test runs.
787
+ dir1 (A) should point to NEW tests results to be verified and dir2 (B) to REFERENCE results.
788
+ Allowed options are:
789
+ -image [filename]: compare only images and save its in specified file (default
790
+ name is <dir1>/diffimage-<dir2>.log)
791
+ -cpu [filename]: compare only CPU and save it in specified file (default
792
+ name is <dir1>/diffcpu-<dir2>.log)
793
+ -memory [filename]: compare only memory and save it in specified file (default
794
+ name is <dir1>/diffmemory-<dir2>.log)
795
+ -save filename: save resulting log in specified file (default name is
796
+ <dir1>/diff-<dir2>.log); HTML log is saved with same name
797
+ and extension .html
798
+ -status {same|ok|all}: filter cases for comparing by their status:
799
+ same - only cases with same status are compared (default)
800
+ ok - only cases with OK status in both logs are compared
801
+ all - results are compared regardless of status
802
+ -verbose level:
803
+ 1 - output only differences
804
+ 2 - output also list of logs and directories present in one of dirs only
805
+ 3 - (default) output also progress messages
806
+ -highlight_percent value: highlight considerable (>value in %) deviations
807
+ of CPU and memory (default value is 5%)
808
+ }
809
+ proc testdiff {dir1 dir2 args} {
810
+ if { "$dir1" == "$dir2" } {
811
+ error "Input directories are the same"
812
+ }
813
+
814
+ ######################################################
815
+ # check arguments
816
+ ######################################################
817
+
818
+ # treat options
819
+ set logfile [file join $dir1 "diff-[file tail $dir2].log"]
820
+ set logfile_image ""
821
+ set logfile_cpu ""
822
+ set logfile_memory ""
823
+ set image false
824
+ set cpu false
825
+ set memory false
826
+ set basename ""
827
+ set save false
828
+ set status "same"
829
+ set verbose 3
830
+ set highlight_percent 5
831
+ for {set narg 0} {$narg < [llength $args]} {incr narg} {
832
+ set arg [lindex $args $narg]
833
+ # log file name
834
+ if { $arg == "-save" } {
835
+ incr narg
836
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
837
+ set logfile [lindex $args $narg]
838
+ } else {
839
+ error "Error: Option -save must be followed by log file name"
840
+ }
841
+ set save true
842
+ continue
843
+ }
844
+
845
+ # image compared log
846
+ if { $arg == "-image" } {
847
+ incr narg
848
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
849
+ set logfile_image [lindex $args $narg]
850
+ } else {
851
+ set logfile_image [file join $dir1 "diffimage-[file tail $dir2].log"]
852
+ incr narg -1
853
+ }
854
+ set image true
855
+ continue
856
+ }
857
+
858
+ # CPU compared log
859
+ if { $arg == "-cpu" } {
860
+ incr narg
861
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
862
+ set logfile_cpu [lindex $args $narg]
863
+ } else {
864
+ set logfile_cpu [file join $dir1 "diffcpu-[file tail $dir2].log"]
865
+ incr narg -1
866
+ }
867
+ set cpu true
868
+ continue
869
+ }
870
+
871
+ # memory compared log
872
+ if { $arg == "-memory" } {
873
+ incr narg
874
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
875
+ set logfile_memory [lindex $args $narg]
876
+ } else {
877
+ set logfile_memory [file join $dir1 "diffmemory-[file tail $dir2].log"]
878
+ incr narg -1
879
+ }
880
+ set memory true
881
+ continue
882
+ }
883
+
884
+ # status filter
885
+ if { $arg == "-status" } {
886
+ incr narg
887
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
888
+ set status [lindex $args $narg]
889
+ } else {
890
+ set status ""
891
+ }
892
+ if { "$status" != "same" && "$status" != "all" && "$status" != "ok" } {
893
+ error "Error: Option -status must be followed by one of \"same\", \"all\", or \"ok\""
894
+ }
895
+ continue
896
+ }
897
+
898
+ # verbose level
899
+ if { $arg == "-verbose" } {
900
+ incr narg
901
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
902
+ set verbose [expr [lindex $args $narg]]
903
+ } else {
904
+ error "Error: Option -verbose must be followed by integer verbose level"
905
+ }
906
+ continue
907
+ }
908
+
909
+ # highlight_percent
910
+ if { $arg == "-highlight_percent" } {
911
+ incr narg
912
+ if { $narg < [llength $args] && ! [regexp {^-} [lindex $args $narg]] } {
913
+ set highlight_percent [expr [lindex $args $narg]]
914
+ } else {
915
+ error "Error: Option -highlight_percent must be followed by integer value"
916
+ }
917
+ continue
918
+ }
919
+
920
+ if { [regexp {^-} $arg] } {
921
+ error "Error: unsupported option \"$arg\""
922
+ }
923
+
924
+ # non-option arguments form a subdirectory path
925
+ set basename [file join $basename $arg]
926
+ }
927
+
928
+ if {$image != false || $cpu != false || $memory != false} {
929
+ if {$save != false} {
930
+ error "Error: Option -save can not be used with image/cpu/memory options"
931
+ }
932
+ }
933
+
934
+ # run diff procedure (recursive)
935
+ _test_diff $dir1 $dir2 $basename $image $cpu $memory $status $verbose log log_image log_cpu log_memory
936
+
937
+ # save result to log file
938
+ if {$image == false && $cpu == false && $memory == false} {
939
+ if { "$logfile" != "" } {
940
+ _log_save $logfile [join $log "\n"]
941
+ _log_html_diff "[file rootname $logfile].html" $log $dir1 $dir2 ${highlight_percent}
942
+ puts "Log is saved to $logfile (and .html)"
943
+ }
944
+ } else {
945
+ foreach mode {image cpu memory} {
946
+ if {"[set logfile_${mode}]" != ""} {
947
+ _log_save "[set logfile_${mode}]" [join "[set log_${mode}]" "\n"]
948
+ _log_html_diff "[file rootname [set logfile_${mode}]].html" "[set log_${mode}]" $dir1 $dir2 ${highlight_percent}
949
+ puts "Log (${mode}) is saved to [set logfile_${mode}] (and .html)"
950
+ }
951
+ }
952
+ }
953
+ return
954
+ }
955
+
956
+ # Procedure to check data file before adding it to repository
957
+ help testfile {
958
+ Checks specified data files for putting them into the test data files repository.
959
+
960
+ Use: testfile filelist
961
+
962
+ Will report if:
963
+ - data file (non-binary) is in DOS encoding (CR/LF)
964
+ - same data file (with same or another name) already exists in the repository
965
+ - another file with the same name already exists
966
+ Note that names are considered to be case-insensitive (for compatibility
967
+ with Windows).
968
+
969
+ Unless the file is already in the repository, tries to load it, reports
970
+ the recognized file format, file size, number of faces and edges in the
971
+ loaded shape (if any), information contained its triangulation, and makes
972
+ snapshot (in the temporary directory).
973
+
974
+ Finally it advises whether the file should be put to public section of the
975
+ repository.
976
+
977
+ Use: testfile -check
978
+
979
+ If "-check" is given as an argument, then procedure will check files already
980
+ located in the repository (for possible duplicates and for DOS encoding).
981
+ }
982
+ proc testfile {filelist} {
983
+ global env
984
+
985
+ # check that CSF_TestDataPath is defined
986
+ if { ! [info exists env(CSF_TestDataPath)] } {
987
+ error "Environment variable CSF_TestDataPath must be defined!"
988
+ }
989
+
990
+ set checkrepo f
991
+ if { "$filelist" == "-check" } { set checkrepo t }
992
+
993
+ # build registry of existing data files (name -> path) and (size -> path)
994
+ puts "Collecting info on test data files repository..."
995
+ foreach dir [_split_path $env(CSF_TestDataPath)] {
996
+ while {[llength $dir] != 0} {
997
+ set curr [lindex $dir 0]
998
+ set dir [lrange $dir 1 end]
999
+ eval lappend dir [glob -nocomplain -directory $curr -type d *]
1000
+ foreach file [glob -nocomplain -directory $curr -type f *] {
1001
+ set name [file tail $file]
1002
+ set name_lower [string tolower $name]
1003
+ set size [file size $file]
1004
+
1005
+ # check that the file is not in DOS encoding
1006
+ if { $checkrepo } {
1007
+ if { [_check_dos_encoding $file] } {
1008
+ puts "Warning: file $file is in DOS encoding; was this intended?"
1009
+ }
1010
+ _check_file_format $file
1011
+
1012
+ # check if file with the same name is present twice or more
1013
+ if { [info exists names($name_lower)] } {
1014
+ puts "Error: more than one file with name $name is present in the repository:"
1015
+ if { [_diff_files $file $names($name_lower)] } {
1016
+ puts "(files are different by content)"
1017
+ } else {
1018
+ puts "(files are same by content)"
1019
+ }
1020
+ puts "--> $file"
1021
+ puts "--> $names($name_lower)"
1022
+ continue
1023
+ }
1024
+
1025
+ # check if file with the same content exists
1026
+ if { [info exists sizes($size)] } {
1027
+ foreach other $sizes($size) {
1028
+ if { ! [_diff_files $file $other] } {
1029
+ puts "Warning: two files with the same content found:"
1030
+ puts "--> $file"
1031
+ puts "--> $other"
1032
+ }
1033
+ }
1034
+ }
1035
+ }
1036
+
1037
+ # add the file to the registry
1038
+ lappend names($name_lower) $file
1039
+ lappend sizes($size) $file
1040
+ }
1041
+ }
1042
+ }
1043
+ if { $checkrepo || [llength $filelist] <= 0 } { return }
1044
+
1045
+ # check the new files
1046
+ set has_images f
1047
+ puts "Checking new file(s)..."
1048
+ foreach file $filelist {
1049
+ set name [file tail $file]
1050
+ set name_lower [string tolower $name]
1051
+ set found f
1052
+
1053
+ # check for presence of the file with same name
1054
+ if { [info exists names($name_lower)] } {
1055
+ set found f
1056
+ foreach other $names($name_lower) {
1057
+ # avoid comparing the file with itself
1058
+ if { [file normalize $file] == [file normalize $other] } {
1059
+ continue
1060
+ }
1061
+ # compare content
1062
+ if { [_diff_files $file $other] } {
1063
+ puts "\n* $file: error\n name is already used by existing file\n --> $other"
1064
+ } else {
1065
+ puts "\n* $file: already present \n --> $other"
1066
+ }
1067
+ set found t
1068
+ break
1069
+ }
1070
+ if { $found } { continue }
1071
+ }
1072
+
1073
+ # get size of the file; if it is in DOS encoding and less than 1 MB,
1074
+ # estimate also its size in UNIX encoding to be able to find same
1075
+ # file if already present but in UNIX encoding
1076
+ set sizeact [file size $file]
1077
+ set sizeunx ""
1078
+ set isdos [_check_dos_encoding $file]
1079
+ if { $isdos && $sizeact < 10000000 } {
1080
+ set fd [open $file r]
1081
+ fconfigure $fd -translation crlf
1082
+ set sizeunx [string length [read $fd]]
1083
+ close $fd
1084
+ }
1085
+
1086
+ # check if file with the same content exists
1087
+ foreach size "$sizeact $sizeunx" {
1088
+ if { [info exists sizes($size)] } {
1089
+ foreach other $sizes($size) {
1090
+ # avoid comparing the file with itself
1091
+ if { [file normalize $file] == [file normalize $other] } {
1092
+ continue
1093
+ }
1094
+ # compare content
1095
+ if { ! [_diff_files $file $other] } {
1096
+ puts "\n* $file: duplicate \n already present under name [file tail $other]\n --> $other"
1097
+ set found t
1098
+ break
1099
+ }
1100
+ }
1101
+ if { $found } { break }
1102
+ }
1103
+ }
1104
+ if { $found } { continue }
1105
+
1106
+ # file is not present yet, so to be analyzed
1107
+ puts "\n* $file: new file"
1108
+
1109
+ # add the file to the registry as if it were added to the repository,
1110
+ # to report possible duplicates among the currently processed files
1111
+ lappend names($name_lower) $file
1112
+ if { "$sizeunx" != "" } {
1113
+ lappend sizes($sizeunx) $file
1114
+ } else {
1115
+ lappend sizes($sizeact) $file
1116
+ }
1117
+
1118
+ # first of all, complain if it is in DOS encoding
1119
+ if { $isdos } {
1120
+ puts " Warning: DOS encoding detected, consider converting to"
1121
+ puts " UNIX unless DOS line ends are needed for the test"
1122
+ }
1123
+
1124
+ # try to read the file
1125
+ set format [_check_file_format $file]
1126
+ if { [catch {uplevel load_data_file $file $format a}] } {
1127
+ puts " Warning: Cannot read as $format file"
1128
+ continue
1129
+ }
1130
+
1131
+ # warn if shape contains triangulation
1132
+ pload MODELING
1133
+ if { "$format" != "STL" &&
1134
+ [regexp {([0-9]+)\s+triangles} [uplevel trinfo a] res nbtriangles] &&
1135
+ $nbtriangles != 0 } {
1136
+ puts " Warning: shape contains triangulation ($nbtriangles triangles),"
1137
+ puts " consider removing them unless they are needed for the test!"
1138
+ }
1139
+
1140
+ # get number of faces and edges
1141
+ set edges 0
1142
+ set faces 0
1143
+ set nbs [uplevel nbshapes a]
1144
+ regexp {EDGE[ \t:]*([0-9]+)} $nbs res edges
1145
+ regexp {FACE[ \t:]*([0-9]+)} $nbs res faces
1146
+
1147
+ # classify; first check file size and number of faces and edges
1148
+ if { $size < 95000 && $faces < 20 && $edges < 100 } {
1149
+ set dir public
1150
+ } else {
1151
+ set dir private
1152
+ }
1153
+
1154
+ # add stats
1155
+ puts " $format size=[expr $size / 1024] KiB, nbfaces=$faces, nbedges=$edges -> $dir"
1156
+
1157
+ set tmpdir [_get_temp_dir]
1158
+ file mkdir $tmpdir/$dir
1159
+
1160
+ # make snapshot
1161
+ pload VISUALIZATION
1162
+ uplevel vdisplay a
1163
+ uplevel vsetdispmode 1
1164
+ uplevel vfit
1165
+ uplevel vzfit
1166
+ uplevel vdump $tmpdir/$dir/[file rootname [file tail $file]].png
1167
+ set has_images t
1168
+ }
1169
+ if { $has_images } {
1170
+ puts "Snapshots are saved in subdirectory [_get_temp_dir]"
1171
+ }
1172
+ }
1173
+
1174
+ # Procedure to locate data file for test given its name.
1175
+ # The search is performed assuming that the function is called
1176
+ # from the test case script; the search order is:
1177
+ # - subdirectory "data" of the test script (grid) folder
1178
+ # - subdirectories in environment variable CSF_TestDataPath
1179
+ # - subdirectory set by datadir command
1180
+ # If file is not found, raises Tcl error.
1181
+ proc locate_data_file {filename} {
1182
+ global env groupname gridname casename
1183
+
1184
+ # check if the file is located in the subdirectory data of the script dir
1185
+ set scriptfile [info script]
1186
+ if { "$scriptfile" != "" } {
1187
+ set path [file join [file dirname "$scriptfile"] data "$filename"]
1188
+ if { [file exists "$path"] } {
1189
+ return [file normalize "$path"]
1190
+ }
1191
+ }
1192
+
1193
+ # check sub-directories in paths indicated by CSF_TestDataPath
1194
+ if { [info exists env(CSF_TestDataPath)] } {
1195
+ foreach dir [_split_path $env(CSF_TestDataPath)] {
1196
+ set dir [list "$dir"]
1197
+ while {[llength "$dir"] != 0} {
1198
+ set name [lindex "$dir" 0]
1199
+ set dir [lrange "$dir" 1 end]
1200
+
1201
+ # skip directories starting with dot
1202
+ set aTail [file tail "$name"]
1203
+ if { [regexp {^[.]} "$aTail"] } { continue }
1204
+ if { [file exists "$name/$filename"] } {
1205
+ return [file normalize "$name/$filename"]
1206
+ }
1207
+ eval lappend dir [glob -nocomplain -directory "$name" -type d *]
1208
+ }
1209
+ }
1210
+ }
1211
+
1212
+ # check current datadir
1213
+ if { [file exists "[uplevel datadir]/$filename"] } {
1214
+ return [file normalize "[uplevel datadir]/$filename"]
1215
+ }
1216
+
1217
+ # raise error
1218
+ error [join [list "File $filename could not be found" \
1219
+ "(should be in paths indicated by CSF_TestDataPath environment variable, " \
1220
+ "or in subfolder data in the script directory)"] "\n"]
1221
+ }
1222
+
1223
+ # Internal procedure to find test case indicated by group, grid, and test case names;
1224
+ # returns:
1225
+ # - dir: path to the base directory of the tests group
1226
+ # - gridname: actual name of the grid
1227
+ # - casefile: path to the test case script
1228
+ # if no such test is found, raises error with appropriate message
1229
+ proc _get_test {group grid casename _dir _gridname _casefile} {
1230
+ upvar $_dir dir
1231
+ upvar $_gridname gridname
1232
+ upvar $_casefile casefile
1233
+
1234
+ global env
1235
+
1236
+ # check that environment variable defining paths to test scripts is defined
1237
+ if { ! [info exists env(CSF_TestScriptsPath)] ||
1238
+ [llength $env(CSF_TestScriptsPath)] <= 0 } {
1239
+ error "Error: Environment variable CSF_TestScriptsPath is not defined"
1240
+ }
1241
+
1242
+ # iterate by all script paths
1243
+ foreach dir [_split_path $env(CSF_TestScriptsPath)] {
1244
+ # protection against empty paths
1245
+ set dir [string trim $dir]
1246
+ if { $dir == "" } { continue }
1247
+
1248
+ # check that directory exists
1249
+ if { ! [file isdirectory $dir] } {
1250
+ puts "Warning: directory $dir listed in CSF_TestScriptsPath does not exist, skipped"
1251
+ continue
1252
+ }
1253
+
1254
+ # check if test group with given name exists in this dir
1255
+ # if not, continue to the next test dir
1256
+ if { ! [file isdirectory $dir/$group] } { continue }
1257
+
1258
+ # check that grid with given name (possibly alias) exists; stop otherwise
1259
+ set gridname $grid
1260
+ if { ! [file isdirectory $dir/$group/$gridname] } {
1261
+ # check if grid is named by alias rather than by actual name
1262
+ if { [file exists $dir/$group/grids.list] } {
1263
+ set fd [open $dir/$group/grids.list]
1264
+ while { [gets $fd line] >= 0 } {
1265
+ if { [regexp "\[ \t\]*\#.*" $line] } { continue }
1266
+ if { [regexp "^$grid\[ \t\]*\(\[A-Za-z0-9_.-\]+\)\$" $line res gridname] } {
1267
+ break
1268
+ }
1269
+ }
1270
+ close $fd
1271
+ }
1272
+ }
1273
+ if { ! [file isdirectory $dir/$group/$gridname] } { continue }
1274
+
1275
+ # get actual file name of the script; stop if it cannot be found
1276
+ set casefile $dir/$group/$gridname/$casename
1277
+ if { ! [file exists $casefile] } {
1278
+ # check if this grid is aliased to another one
1279
+ if { [file exists $dir/$group/$gridname/cases.list] } {
1280
+ set fd [open $dir/$group/$gridname/cases.list]
1281
+ if { [gets $fd line] >= 0 } {
1282
+ set casefile [file normalize $dir/$group/$gridname/[string trim $line]/$casename]
1283
+ }
1284
+ close $fd
1285
+ }
1286
+ }
1287
+ if { [file exists $casefile] } {
1288
+ # normal return
1289
+ return
1290
+ }
1291
+ }
1292
+
1293
+ # coming here means specified test is not found; report error
1294
+ error [join [list "Error: test case $group / $grid / $casename is not found in paths listed in variable" \
1295
+ "CSF_TestScriptsPath (current value is \"$env(CSF_TestScriptsPath)\")"] "\n"]
1296
+ }
1297
+
1298
+ # Internal procedure to run test case indicated by base directory,
1299
+ # grid and grid names, and test case file path.
1300
+ # The log can be obtained by command "dlog get".
1301
+ proc _run_test {scriptsdir group gridname casefile echo} {
1302
+ global env
1303
+
1304
+ # start timer
1305
+ uplevel dchrono _timer reset
1306
+ uplevel dchrono _timer start
1307
+ catch {uplevel meminfo h} membase
1308
+
1309
+ # enable commands logging; switch to old-style mode if dlog command is not present
1310
+ set dlog_exists 1
1311
+ if { [catch {dlog reset}] } {
1312
+ set dlog_exists 0
1313
+ } elseif { $echo } {
1314
+ decho on
1315
+ } else {
1316
+ dlog reset
1317
+ dlog on
1318
+ rename puts puts-saved
1319
+ proc puts args {
1320
+ global _tests_verbose
1321
+
1322
+ # log only output to stdout and stderr, not to file!
1323
+ if {[llength $args] > 1} {
1324
+ set optarg [lindex $args end-1]
1325
+ if { $optarg == "stdout" || $optarg == "stderr" || $optarg == "-newline" } {
1326
+ dlog add [lindex $args end]
1327
+ } else {
1328
+ eval puts-saved $args
1329
+ }
1330
+ } else {
1331
+ dlog add [lindex $args end]
1332
+ }
1333
+ }
1334
+ }
1335
+
1336
+ # evaluate test case
1337
+ set tmp_imagedir 0
1338
+ if [catch {
1339
+ # set variables identifying test case
1340
+ uplevel set casename [file tail $casefile]
1341
+ uplevel set groupname $group
1342
+ uplevel set gridname $gridname
1343
+ uplevel set dirname $scriptsdir
1344
+
1345
+ # set path for saving of log and images (if not yet set) to temp dir
1346
+ if { ! [uplevel info exists imagedir] } {
1347
+ uplevel set test_image \$casename
1348
+
1349
+ # create subdirectory in temp named after group and grid with timestamp
1350
+ set rootlogdir [_get_temp_dir]
1351
+
1352
+ set imagedir "${group}-${gridname}-${::casename}-[clock format [clock seconds] -format {%Y-%m-%dT%Hh%Mm%Ss}]"
1353
+ set imagedir [file normalize ${rootlogdir}/$imagedir]
1354
+
1355
+ if { [catch {file mkdir $imagedir}] || ! [file writable $imagedir] ||
1356
+ ! [catch {glob -directory $imagedir *}] } {
1357
+ # puts "Warning: Cannot create directory \"$imagedir\", or it is not empty; \"${rootlogdir}\" is used"
1358
+ set imagedir $rootlogdir
1359
+ }
1360
+
1361
+ uplevel set imagedir \"$imagedir\"
1362
+ set tmp_imagedir 1
1363
+ }
1364
+
1365
+ # execute test scripts
1366
+ if { [file exists $scriptsdir/$group/begin] } {
1367
+ puts "Executing $scriptsdir/$group/begin..."; flush stdout
1368
+ uplevel source -encoding utf-8 $scriptsdir/$group/begin
1369
+ }
1370
+ if { [file exists $scriptsdir/$group/$gridname/begin] } {
1371
+ puts "Executing $scriptsdir/$group/$gridname/begin..."; flush stdout
1372
+ uplevel source -encoding utf-8 $scriptsdir/$group/$gridname/begin
1373
+ }
1374
+
1375
+ puts "Executing $casefile..."; flush stdout
1376
+ uplevel source -encoding utf-8 $casefile
1377
+
1378
+ if { [file exists $scriptsdir/$group/$gridname/end] } {
1379
+ puts "Executing $scriptsdir/$group/$gridname/end..."; flush stdout
1380
+ uplevel source -encoding utf-8 $scriptsdir/$group/$gridname/end
1381
+ }
1382
+ if { [file exists $scriptsdir/$group/end] } {
1383
+ puts "Executing $scriptsdir/$group/end..."; flush stdout
1384
+ uplevel source -encoding utf-8 $scriptsdir/$group/end
1385
+ }
1386
+ } res] {
1387
+ if { "$res" == "" } { set res "EMPTY" }
1388
+ # in echo mode, output error message using dputs command to have it colored,
1389
+ # note that doing the same in logged mode would duplicate the message
1390
+ if { ! $dlog_exists || ! $echo } {
1391
+ puts "Tcl Exception: $res"
1392
+ } else {
1393
+ decho off
1394
+ dputs -red -intense "Tcl Exception: $res"
1395
+ }
1396
+ }
1397
+
1398
+ # stop logging
1399
+ if { $dlog_exists } {
1400
+ if { $echo } {
1401
+ decho off
1402
+ } else {
1403
+ rename puts {}
1404
+ rename puts-saved puts
1405
+ dlog off
1406
+ }
1407
+ }
1408
+
1409
+ # stop cpulimit killer if armed by the test
1410
+ cpulimit
1411
+
1412
+ # add memory and timing info
1413
+ set stats ""
1414
+ if { ! [catch {uplevel meminfo h} memuse] } {
1415
+ append stats "MEMORY DELTA: [expr ($memuse - $membase) / 1024] KiB\n"
1416
+ }
1417
+ uplevel dchrono _timer stop
1418
+ set cpu_usr [uplevel dchrono _timer -userCPU]
1419
+ set elps [uplevel dchrono _timer -elapsed]
1420
+ append stats "TOTAL CPU TIME: $cpu_usr sec\n"
1421
+ append stats "ELAPSED TIME: $elps sec\n"
1422
+ if { $dlog_exists && ! $echo } {
1423
+ dlog add $stats
1424
+ } else {
1425
+ puts $stats
1426
+ }
1427
+
1428
+ # unset global vars
1429
+ uplevel unset casename groupname gridname dirname
1430
+ if { $tmp_imagedir } { uplevel unset imagedir test_image }
1431
+ }
1432
+
1433
+ # Internal procedure to check log of test execution and decide if it passed or failed
1434
+ proc _check_log {dir group gridname casename errors log {_summary {}} {_html_log {}}} {
1435
+ global env
1436
+ if { $_summary != "" } { upvar $_summary summary }
1437
+ if { $_html_log != "" } { upvar $_html_log html_log }
1438
+ set summary {}
1439
+ set html_log {}
1440
+ set errors_log {}
1441
+
1442
+ if [catch {
1443
+
1444
+ # load definition of 'bad words' indicating test failure
1445
+ # note that rules are loaded in the order of decreasing priority (grid - group - common),
1446
+ # thus grid rules will override group ones
1447
+ set badwords {}
1448
+ foreach rulesfile [list $dir/$group/$gridname/parse.rules $dir/$group/parse.rules $dir/parse.rules] {
1449
+ if [catch {set fd [open $rulesfile r]}] { continue }
1450
+ while { [gets $fd line] >= 0 } {
1451
+ # skip comments and empty lines
1452
+ if { [regexp "\[ \t\]*\#.*" $line] } { continue }
1453
+ if { [string trim $line] == "" } { continue }
1454
+ # extract regexp
1455
+ if { ! [regexp {^([^/]*)/([^/]*)/(.*)$} $line res status rexp comment] } {
1456
+ puts "Warning: cannot recognize parsing rule \"$line\" in file $rulesfile"
1457
+ continue
1458
+ }
1459
+ set status [string trim $status]
1460
+ if { $comment != "" } { append status " ([string trim $comment])" }
1461
+ set rexp [regsub -all {\\b} $rexp {\\y}] ;# convert regexp from Perl to Tcl style
1462
+ lappend badwords [list $status $rexp]
1463
+ }
1464
+ close $fd
1465
+ }
1466
+ if { [llength $badwords] <= 0 } {
1467
+ puts "Warning: no definition of error indicators found (check files parse.rules)"
1468
+ }
1469
+
1470
+ # analyse log line-by-line
1471
+ set todos {} ;# TODO statements
1472
+ set requs {} ;# REQUIRED statements
1473
+ set todo_incomplete -1
1474
+ set status ""
1475
+ foreach line [split $log "\n"] {
1476
+ # check if line defines specific treatment of some messages
1477
+ if [regexp -nocase {^[ \s]*TODO ([^:]*):(.*)$} $line res platforms pattern] {
1478
+ if { ! [regexp -nocase {\mAll\M} $platforms] &&
1479
+ ! [regexp -nocase "\\m[checkplatform]\\M" $platforms] } {
1480
+ lappend html_log [_html_highlight IGNORE $line]
1481
+ continue ;# TODO statement is for another platform
1482
+ }
1483
+
1484
+ # record TODOs that mark unstable cases
1485
+ if { [regexp {[\?]} $platforms] } {
1486
+ set todos_unstable([llength $todos]) 1
1487
+ }
1488
+
1489
+ # convert legacy regexps from Perl to Tcl style
1490
+ set pattern [regsub -all {\\b} [string trim $pattern] {\\y}]
1491
+
1492
+ # special case: TODO TEST INCOMPLETE
1493
+ if { [string trim $pattern] == "TEST INCOMPLETE" } {
1494
+ set todo_incomplete [llength $todos]
1495
+ }
1496
+
1497
+ lappend todos [list $pattern [llength $html_log] $line]
1498
+ lappend html_log [_html_highlight BAD $line]
1499
+ continue
1500
+ }
1501
+ if [regexp -nocase {^[ \s]*REQUIRED ([^:]*):[ \s]*(.*)$} $line res platforms pattern] {
1502
+ if { ! [regexp -nocase {\mAll\M} $platforms] &&
1503
+ ! [regexp -nocase "\\m[checkplatform]\\M" $platforms] } {
1504
+ lappend html_log [_html_highlight IGNORE $line]
1505
+ continue ;# REQUIRED statement is for another platform
1506
+ }
1507
+ lappend requs [list $pattern [llength $html_log] $line]
1508
+ lappend html_log [_html_highlight OK $line]
1509
+ continue
1510
+ }
1511
+
1512
+ # check for presence of required messages
1513
+ set ismarked 0
1514
+ for {set i 0} {$i < [llength $requs]} {incr i} {
1515
+ set pattern [lindex $requs $i 0]
1516
+ if { [regexp $pattern $line] } {
1517
+ incr required_count($i)
1518
+ lappend html_log [_html_highlight OK $line]
1519
+ set ismarked 1
1520
+ continue
1521
+ }
1522
+ }
1523
+ if { $ismarked } {
1524
+ continue
1525
+ }
1526
+
1527
+ # check for presence of messages indicating test result
1528
+ foreach bw $badwords {
1529
+ if { [regexp [lindex $bw 1] $line] } {
1530
+ # check if this is known bad case
1531
+ set is_known 0
1532
+ for {set i 0} {$i < [llength $todos]} {incr i} {
1533
+ set pattern [lindex $todos $i 0]
1534
+ if { [regexp $pattern $line] } {
1535
+ set is_known 1
1536
+ incr todo_count($i)
1537
+ lappend html_log [_html_highlight BAD $line]
1538
+ break
1539
+ }
1540
+ }
1541
+
1542
+ # if it is not in todo, define status
1543
+ if { ! $is_known } {
1544
+ set stat [lindex $bw 0 0]
1545
+ if {$errors} {
1546
+ lappend errors_log $line
1547
+ }
1548
+ lappend html_log [_html_highlight $stat $line]
1549
+ if { $status == "" && $stat != "OK" && ! [regexp -nocase {^IGNOR} $stat] } {
1550
+ set status [lindex $bw 0]
1551
+ }
1552
+ }
1553
+ set ismarked 1
1554
+ break
1555
+ }
1556
+ }
1557
+ if { ! $ismarked } {
1558
+ lappend html_log $line
1559
+ }
1560
+ }
1561
+
1562
+ # check for presence of TEST COMPLETED statement
1563
+ if { $status == "" && ! [regexp {TEST COMPLETED} $log] } {
1564
+ # check whether absence of TEST COMPLETED is known problem
1565
+ if { $todo_incomplete >= 0 } {
1566
+ incr todo_count($todo_incomplete)
1567
+ } else {
1568
+ set status "FAILED (no final message is found)"
1569
+ }
1570
+ }
1571
+
1572
+ # report test as failed if it doesn't contain required pattern
1573
+ if { $status == "" } {
1574
+ for {set i 0} {$i < [llength $requs]} {incr i} {
1575
+ if { ! [info exists required_count($i)] } {
1576
+ set linenum [lindex $requs $i 1]
1577
+ set html_log [lreplace $html_log $linenum $linenum [_html_highlight FAILED [lindex $requs $i 2]]]
1578
+ set status "FAILED (REQUIRED statement no. [expr $i + 1] is not found)"
1579
+ }
1580
+ }
1581
+ }
1582
+
1583
+ # check declared bad cases and diagnose possible improvement
1584
+ # (bad case declared but not detected).
1585
+ # Note that absence of the problem marked by TODO with question mark
1586
+ # (unstable) is not reported as improvement.
1587
+ if { $status == "" } {
1588
+ for {set i 0} {$i < [llength $todos]} {incr i} {
1589
+ if { ! [info exists todos_unstable($i)] &&
1590
+ (! [info exists todo_count($i)] || $todo_count($i) <= 0) } {
1591
+ set linenum [lindex $todos $i 1]
1592
+ set html_log [lreplace $html_log $linenum $linenum [_html_highlight IMPROVEMENT [lindex $todos $i 2]]]
1593
+ set status "IMPROVEMENT (expected problem TODO no. [expr $i + 1] is not detected)"
1594
+ break;
1595
+ }
1596
+ }
1597
+ }
1598
+
1599
+ # report test as known bad if at least one of expected problems is found
1600
+ if { $status == "" && [llength [array names todo_count]] > 0 } {
1601
+ set status "BAD (known problem)"
1602
+ }
1603
+
1604
+ # report normal OK
1605
+ if { $status == "" } {set status "OK" }
1606
+
1607
+ } res] {
1608
+ set status "FAILED ($res)"
1609
+ }
1610
+
1611
+ # put final message
1612
+ _log_and_puts summary "CASE $group $gridname $casename: $status"
1613
+ set summary [join $summary "\n"]
1614
+ if {$errors} {
1615
+ foreach error $errors_log {
1616
+ _log_and_puts summary " $error"
1617
+ }
1618
+ }
1619
+ set html_log "[_html_highlight [lindex $status 0] $summary]\n[join $html_log \n]"
1620
+ }
1621
+
1622
+ # Auxiliary procedure putting message to both cout and log variable (list)
1623
+ proc _log_and_puts {logvar message} {
1624
+ if { $logvar != "" } {
1625
+ upvar $logvar log
1626
+ lappend log $message
1627
+ }
1628
+ puts $message
1629
+ }
1630
+
1631
+ # Auxiliary procedure to log result on single test case
1632
+ proc _log_test_case {output logdir dir group grid casename logvar} {
1633
+ upvar $logvar log
1634
+ set show_errors 0
1635
+
1636
+ # check result and make HTML log
1637
+ _check_log $dir $group $grid $casename $show_errors $output summary html_log
1638
+ lappend log $summary
1639
+
1640
+ # save log to file
1641
+ if { $logdir != "" } {
1642
+ _log_html $logdir/$group/$grid/$casename.html $html_log "Test $group $grid $casename"
1643
+ _log_save $logdir/$group/$grid/$casename.log "$output\n$summary" "Test $group $grid $casename"
1644
+ }
1645
+
1646
+ # remove intermediate command file used to run test
1647
+ if { [file exists $logdir/$group/$grid/${casename}.tcl] } {
1648
+ file delete $logdir/$group/$grid/${casename}.tcl
1649
+ }
1650
+ }
1651
+
1652
+ # Auxiliary procedure to save log to file
1653
+ proc _log_save {file log {title {}}} {
1654
+ # create missing directories as needed
1655
+ catch {file mkdir [file dirname $file]}
1656
+
1657
+ # try to open a file
1658
+ if [catch {set fd [open $file w]} res] {
1659
+ error "Error saving log file $file: $res"
1660
+ }
1661
+
1662
+ # dump log and close
1663
+ puts $fd "$title\n"
1664
+ puts $fd $log
1665
+ close $fd
1666
+ return
1667
+ }
1668
+
1669
+ # Auxiliary procedure to make a (relative if possible) URL to a file for
1670
+ # inclusion a reference in HTML log
1671
+ proc _make_url {htmldir file} {
1672
+ set htmlpath [file split [file normalize $htmldir]]
1673
+ set filepath [file split [file normalize $file]]
1674
+ for {set i 0} {$i < [llength $htmlpath]} {incr i} {
1675
+ if { "[lindex $htmlpath $i]" != "[lindex $filepath $i]" } {
1676
+ if { $i == 0 } { break }
1677
+ return "[string repeat "../" [expr [llength $htmlpath] - $i - 1]][eval file join [lrange $filepath $i end]]"
1678
+ }
1679
+ }
1680
+
1681
+ # if relative path could not be made, return full file URL
1682
+ return "file://[file normalize $file]"
1683
+ }
1684
+
1685
+ # Auxiliary procedure to save log to file
1686
+ proc _log_html {file log {title {}}} {
1687
+ # create missing directories as needed
1688
+ catch {file mkdir [file dirname $file]}
1689
+
1690
+ # try to open a file
1691
+ if [catch {set fd [open $file w]} res] {
1692
+ error "Error saving log file $file: $res"
1693
+ }
1694
+
1695
+ # print header
1696
+ puts $fd "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>"
1697
+ puts $fd "<title>$title</title></head><body><h1>$title</h1>"
1698
+
1699
+ # add images if present; these should have either PNG, GIF, or JPG extension,
1700
+ # and start with name of the test script, with optional suffix separated
1701
+ # by underscore or dash
1702
+ set imgbasename [file rootname [file tail $file]]
1703
+ foreach img [lsort [glob -nocomplain -directory [file dirname $file] -tails \
1704
+ ${imgbasename}.gif ${imgbasename}.png ${imgbasename}.jpg \
1705
+ ${imgbasename}_*.gif ${imgbasename}_*.png ${imgbasename}_*.jpg \
1706
+ ${imgbasename}-*.gif ${imgbasename}-*.png ${imgbasename}-*.jpg]] {
1707
+ puts $fd "<p>[file tail $img]<br><img src=\"$img\"/><p>"
1708
+ }
1709
+
1710
+ # print log body, trying to add HTML links to script files on lines like
1711
+ # "Executing <filename>..."
1712
+ puts $fd "<pre>"
1713
+ foreach line [split $log "\n"] {
1714
+ if { [regexp {Executing[ \t]+([a-zA-Z0-9._/:-]+[^.])} $line res script] &&
1715
+ [file exists $script] } {
1716
+ set line [regsub $script $line "<a href=\"[_make_url $file $script]\">$script</a>"]
1717
+ }
1718
+ puts $fd $line
1719
+ }
1720
+ puts $fd "</pre></body></html>"
1721
+
1722
+ close $fd
1723
+ return
1724
+ }
1725
+
1726
+ # Auxiliary method to make text with HTML highlighting according to status
1727
+ proc _html_color {status} {
1728
+ # choose a color for the cell according to result
1729
+ if { $status == "OK" } {
1730
+ return lightgreen
1731
+ } elseif { [regexp -nocase {^FAIL} $status] } {
1732
+ return ff8080
1733
+ } elseif { [regexp -nocase {^BAD} $status] } {
1734
+ return yellow
1735
+ } elseif { [regexp -nocase {^IMP} $status] } {
1736
+ return orange
1737
+ } elseif { [regexp -nocase {^SKIP} $status] } {
1738
+ return gray
1739
+ } elseif { [regexp -nocase {^IGNOR} $status] } {
1740
+ return gray
1741
+ } else {
1742
+ puts "Warning: no color defined for status $status, using red as if FAILED"
1743
+ return red
1744
+ }
1745
+ }
1746
+
1747
+ # Format text line in HTML to be colored according to the status
1748
+ proc _html_highlight {status line} {
1749
+ return "<table><tr><td bgcolor=\"[_html_color $status]\">$line</td></tr></table>"
1750
+ }
1751
+
1752
+ # Internal procedure to generate HTML page presenting log of the tests
1753
+ # execution in tabular form, with links to reports on individual cases
1754
+ proc _log_html_summary {logdir log totals regressions improvements skipped total_time} {
1755
+ global _test_case_regexp
1756
+
1757
+ # create missing directories as needed
1758
+ file mkdir $logdir
1759
+
1760
+ # try to open a file and start HTML
1761
+ if [catch {set fd [open $logdir/summary.html w]} res] {
1762
+ error "Error creating log file: $res"
1763
+ }
1764
+
1765
+ # write HRML header, including command to refresh log if still in progress
1766
+ puts $fd "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>"
1767
+ puts $fd "<title>Tests summary</title>"
1768
+ if { $total_time == "" } {
1769
+ puts $fd "<meta http-equiv=\"refresh\" content=\"10\">"
1770
+ }
1771
+ puts $fd "<meta http-equiv=\"pragma\" content=\"NO-CACHE\">"
1772
+ puts $fd "</head><body>"
1773
+
1774
+ # put summary
1775
+ set legend(OK) "Test passed OK"
1776
+ set legend(FAILED) "Test failed (regression)"
1777
+ set legend(BAD) "Known problem"
1778
+ set legend(IMPROVEMENT) "Possible improvement (expected problem not detected)"
1779
+ set legend(SKIPPED) "Test skipped due to lack of data file"
1780
+ puts $fd "<h1>Summary</h1><table>"
1781
+ foreach nbstat $totals {
1782
+ set status [lindex $nbstat 1]
1783
+ if { [info exists legend($status)] } {
1784
+ set comment $legend($status)
1785
+ } else {
1786
+ set comment "User-defined status"
1787
+ }
1788
+ puts $fd "<tr><td align=\"right\">[lindex $nbstat 0]</td><td bgcolor=\"[_html_color $status]\">$status</td><td>$comment</td></tr>"
1789
+ }
1790
+ puts $fd "</table>"
1791
+
1792
+ # time stamp and elapsed time info
1793
+ puts $fd "<p>Generated on [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S}] on [info hostname]\n<p>"
1794
+ if { $total_time != "" } {
1795
+ puts $fd [join [split $total_time "\n"] "<p>"]
1796
+ } else {
1797
+ puts $fd "<p>NOTE: This is intermediate summary; the tests are still running! This page will refresh automatically until tests are finished."
1798
+ }
1799
+
1800
+ # print regressions and improvements
1801
+ foreach featured [list $regressions $improvements $skipped] {
1802
+ if { [llength $featured] <= 1 } { continue }
1803
+ set status [string trim [lindex $featured 0] { :}]
1804
+ puts $fd "<h2>$status</h2>"
1805
+ puts $fd "<table>"
1806
+ set groupgrid ""
1807
+ foreach test [lrange $featured 1 end] {
1808
+ if { ! [regexp {^(.*)\s+([\w\-.]+)$} $test res gg name] } {
1809
+ set gg UNKNOWN
1810
+ set name "Error building short list; check details"
1811
+ }
1812
+ if { $gg != $groupgrid } {
1813
+ if { $groupgrid != "" } { puts $fd "</tr>" }
1814
+ set groupgrid $gg
1815
+ puts $fd "<tr><td>$gg</td>"
1816
+ }
1817
+ puts $fd "<td bgcolor=\"[_html_color $status]\"><a href=\"[regsub -all { } $gg /]/${name}.html\">$name</a></td>"
1818
+ }
1819
+ if { $groupgrid != "" } { puts $fd "</tr>" }
1820
+ puts $fd "</table>"
1821
+ }
1822
+
1823
+ # put detailed log with TOC
1824
+ puts $fd "<hr><h1>Details</h1>"
1825
+ puts $fd "<div style=\"float:right; padding: 10px; border-style: solid; border-color: blue; border-width: 2px;\">"
1826
+
1827
+ # process log line-by-line
1828
+ set group {}
1829
+ set letter {}
1830
+ set body {}
1831
+ foreach line [lsort -dictionary $log] {
1832
+ # check that the line is case report in the form "CASE group grid name: result (explanation)"
1833
+ if { ! [regexp $_test_case_regexp $line res grp grd casename result message] } {
1834
+ continue
1835
+ }
1836
+
1837
+ # start new group
1838
+ if { $grp != $group } {
1839
+ if { $letter != "" } { lappend body "</tr></table>" }
1840
+ set letter {}
1841
+ set group $grp
1842
+ set grid {}
1843
+ puts $fd "<a href=\"#$group\">$group</a><br>"
1844
+ lappend body "<h2><a name=\"$group\">Group $group</a></h2>"
1845
+ }
1846
+
1847
+ # start new grid
1848
+ if { $grd != $grid } {
1849
+ if { $letter != "" } { lappend body "</tr></table>" }
1850
+ set letter {}
1851
+ set grid $grd
1852
+ puts $fd "&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"#$group-$grid\">$grid</a><br>"
1853
+ lappend body "<h2><a name=\"$group-$grid\">Grid $group $grid</a></h2>"
1854
+ }
1855
+
1856
+ # check if test case name is <letter><digit>;
1857
+ # if not, set alnum to period "." to recognize non-standard test name
1858
+ if { ! [regexp {\A([A-Za-z]{1,2})([0-9]{1,2})\Z} $casename res alnum number] &&
1859
+ ! [regexp {\A([A-Za-z0-9]+)_([0-9]+)\Z} $casename res alnum number] } {
1860
+ set alnum $casename
1861
+ }
1862
+
1863
+ # start new row when letter changes or for non-standard names
1864
+ if { $alnum != $letter || $alnum == "." } {
1865
+ if { $letter != "" } {
1866
+ lappend body "</tr><tr>"
1867
+ } else {
1868
+ lappend body "<table><tr>"
1869
+ }
1870
+ set letter $alnum
1871
+ }
1872
+
1873
+ lappend body "<td bgcolor=\"[_html_color $result]\"><a href=\"$group/$grid/${casename}.html\">$casename</a></td>"
1874
+ }
1875
+ puts $fd "</div>\n[join $body "\n"]</tr></table>"
1876
+
1877
+ # add remaining lines of log as plain text
1878
+ puts $fd "<h2>Plain text messages</h2>\n<pre>"
1879
+ foreach line $log {
1880
+ if { ! [regexp $_test_case_regexp $line] } {
1881
+ puts $fd "$line"
1882
+ }
1883
+ }
1884
+ puts $fd "</pre>"
1885
+
1886
+ # close file and exit
1887
+ puts $fd "</body>"
1888
+ close $fd
1889
+ return
1890
+ }
1891
+
1892
+ # Procedure to dump summary logs of tests
1893
+ proc _log_summarize {logdir log {total_time {}}} {
1894
+
1895
+ # sort log records alphabetically to have the same behavior on Linux and Windows
1896
+ # (also needed if tests are run in parallel)
1897
+ set loglist [lsort -dictionary $log]
1898
+
1899
+ # classify test cases by status
1900
+ foreach line $loglist {
1901
+ if { [regexp {^CASE ([^:]*): ([[:alnum:]]+).*$} $line res caseid status] } {
1902
+ lappend stat($status) $caseid
1903
+ }
1904
+ }
1905
+ set totals {}
1906
+ set improvements {Improvements:}
1907
+ set regressions {Failed:}
1908
+ set skipped {Skipped:}
1909
+ if { [info exists stat] } {
1910
+ foreach status [lsort [array names stat]] {
1911
+ lappend totals [list [llength $stat($status)] $status]
1912
+
1913
+ # separately count improvements (status starting with IMP), skipped (status starting with SKIP) and regressions (all except IMP, OK, BAD, and SKIP)
1914
+ if { [regexp -nocase {^IMP} $status] } {
1915
+ eval lappend improvements $stat($status)
1916
+ } elseif { [regexp -nocase {^SKIP} $status] } {
1917
+ eval lappend skipped $stat($status)
1918
+ } elseif { $status != "OK" && ! [regexp -nocase {^BAD} $status] && ! [regexp -nocase {^SKIP} $status] } {
1919
+ eval lappend regressions $stat($status)
1920
+ }
1921
+ }
1922
+ }
1923
+
1924
+ # if time is specified, add totals
1925
+ if { $total_time != "" } {
1926
+ if { [llength $improvements] > 1 } {
1927
+ _log_and_puts log [join $improvements "\n "]
1928
+ }
1929
+ if { [llength $regressions] > 1 } {
1930
+ _log_and_puts log [join $regressions "\n "]
1931
+ }
1932
+ if { [llength $skipped] > 1 } {
1933
+ _log_and_puts log [join $skipped "\n "]
1934
+ }
1935
+ if { [llength $improvements] == 1 && [llength $regressions] == 1 } {
1936
+ _log_and_puts log "No regressions"
1937
+ }
1938
+ _log_and_puts log "Total cases: [join $totals {, }]"
1939
+ _log_and_puts log $total_time
1940
+ }
1941
+
1942
+ # save log to files
1943
+ if { $logdir != "" } {
1944
+ _log_html_summary $logdir $log $totals $regressions $improvements $skipped $total_time
1945
+ _log_save $logdir/tests.log [join $log "\n"] "Tests summary"
1946
+ }
1947
+
1948
+ return
1949
+ }
1950
+
1951
+ # Internal procedure to generate XML log in JUnit style, for further
1952
+ # consumption by Jenkins or similar systems.
1953
+ #
1954
+ # The output is intended to conform to XML schema supported by Jenkins found at
1955
+ # https://svn.jenkins-ci.org/trunk/hudson/dtkit/dtkit-format/dtkit-junit-model/src/main/resources/com/thalesgroup/dtkit/junit/model/xsd/junit-4.xsd
1956
+ #
1957
+ # The mapping of the fields is inspired by annotated schema of Apache Ant JUnit XML format found at
1958
+ # http://windyroad.org/dl/Open%20Source/JUnit.xsd
1959
+ proc _log_xml_summary {logdir filename log include_cout} {
1960
+ global _test_case_regexp
1961
+
1962
+ catch {file mkdir [file dirname $filename]}
1963
+
1964
+ # try to open a file and start XML
1965
+ if [catch {set fd [open $filename w]} res] {
1966
+ error "Error creating XML summary file $filename: $res"
1967
+ }
1968
+ puts $fd "<?xml version='1.0' encoding='utf-8'?>"
1969
+ puts $fd "<testsuites>"
1970
+
1971
+ # prototype for command to generate test suite tag
1972
+ set time_and_host "timestamp=\"[clock format [clock seconds] -format {%Y-%m-%dT%H:%M:%S}]\" hostname=\"[info hostname]\""
1973
+ set cmd_testsuite {puts $fd "<testsuite name=\"$group $grid\" tests=\"$nbtests\" failures=\"$nbfail\" errors=\"$nberr\" time=\"$time\" skipped=\"$nbskip\" $time_and_host>\n$testcases\n</testsuite>\n"}
1974
+
1975
+ # sort log and process it line-by-line
1976
+ set group {}
1977
+ foreach line [lsort -dictionary $log] {
1978
+ # check that the line is case report in the form "CASE group grid name: result (explanation)"
1979
+ if { ! [regexp $_test_case_regexp $line res grp grd casename result message] } {
1980
+ continue
1981
+ }
1982
+ set message [string trim $message " \t\r\n()"]
1983
+
1984
+ # start new testsuite for each grid
1985
+ if { $grp != $group || $grd != $grid } {
1986
+
1987
+ # write previous test suite
1988
+ if [info exists testcases] { eval $cmd_testsuite }
1989
+
1990
+ set testcases {}
1991
+ set nbtests 0
1992
+ set nberr 0
1993
+ set nbfail 0
1994
+ set nbskip 0
1995
+ set time 0.
1996
+
1997
+ set group $grp
1998
+ set grid $grd
1999
+ }
2000
+
2001
+ incr nbtests
2002
+
2003
+ # parse test log and get its CPU time
2004
+ set testout {}
2005
+ set add_cpu {}
2006
+ if { [catch {set fdlog [open $logdir/$group/$grid/${casename}.log r]} ret] } {
2007
+ puts "Error: cannot open $logdir/$group/$grid/${casename}.log: $ret"
2008
+ } else {
2009
+ while { [gets $fdlog logline] >= 0 } {
2010
+ if { $include_cout } {
2011
+ append testout "$logline\n"
2012
+ }
2013
+ if [regexp -nocase {TOTAL CPU TIME:\s*([\d.]+)\s*sec} $logline res cpu] {
2014
+ set add_cpu " time=\"$cpu\""
2015
+ set time [expr $time + $cpu]
2016
+ }
2017
+ }
2018
+ close $fdlog
2019
+ }
2020
+ if { ! $include_cout } {
2021
+ set testout "$line\n"
2022
+ }
2023
+
2024
+ # record test case with its output and status
2025
+ # Mapping is: SKIPPED, BAD, and OK to OK, all other to failure
2026
+ append testcases "\n <testcase name=\"$casename\"$add_cpu status=\"$result\">\n"
2027
+ append testcases "\n <system-out>\n$testout </system-out>"
2028
+ if { $result != "OK" } {
2029
+ if { [regexp -nocase {^SKIP} $result] } {
2030
+ incr nberr
2031
+ append testcases "\n <error name=\"$result\" message=\"$message\"/>"
2032
+ } elseif { [regexp -nocase {^BAD} $result] } {
2033
+ incr nbskip
2034
+ append testcases "\n <skipped>$message</skipped>"
2035
+ } else {
2036
+ incr nbfail
2037
+ append testcases "\n <failure name=\"$result\" message=\"$message\"/>"
2038
+ }
2039
+ }
2040
+ append testcases "\n </testcase>"
2041
+ }
2042
+
2043
+ # write last test suite
2044
+ if [info exists testcases] { eval $cmd_testsuite }
2045
+
2046
+ # the end
2047
+ puts $fd "</testsuites>"
2048
+ close $fd
2049
+ return
2050
+ }
2051
+
2052
+ # Auxiliary procedure to split path specification (usually defined by
2053
+ # environment variable) into list of directories or files
2054
+ proc _split_path {pathspec} {
2055
+ global tcl_platform
2056
+
2057
+ # first replace all \ (which might occur on Windows) by /
2058
+ regsub -all "\\\\" $pathspec "/" pathspec
2059
+
2060
+ # split path by platform-specific separator
2061
+ return [split $pathspec [_path_separator]]
2062
+ }
2063
+
2064
+ # Auxiliary procedure to define platform-specific separator for directories in
2065
+ # path specification
2066
+ proc _path_separator {} {
2067
+ global tcl_platform
2068
+
2069
+ # split path by platform-specific separator
2070
+ if { $tcl_platform(platform) == "windows" } {
2071
+ return ";"
2072
+ } else {
2073
+ return ":"
2074
+ }
2075
+ }
2076
+
2077
+ # Procedure to make a diff and common of two lists
2078
+ proc _list_diff {list1 list2 _in1 _in2 _common} {
2079
+ upvar $_in1 in1
2080
+ upvar $_in2 in2
2081
+ upvar $_common common
2082
+
2083
+ set in1 {}
2084
+ set in2 {}
2085
+ set common {}
2086
+ foreach item $list1 {
2087
+ if { [lsearch -exact $list2 $item] >= 0 } {
2088
+ lappend common $item
2089
+ } else {
2090
+ lappend in1 $item
2091
+ }
2092
+ }
2093
+ foreach item $list2 {
2094
+ if { [lsearch -exact $common $item] < 0 } {
2095
+ lappend in2 $item
2096
+ }
2097
+ }
2098
+ return
2099
+ }
2100
+
2101
+ # procedure to load a file to Tcl string
2102
+ proc _read_file {filename} {
2103
+ set fd [open $filename r]
2104
+ set result [read -nonewline $fd]
2105
+ close $fd
2106
+ return $result
2107
+ }
2108
+
2109
+ # procedure to construct name for the mage diff file
2110
+ proc _diff_img_name {dir1 dir2 casepath imgfile} {
2111
+ return [file join $dir1 $casepath "diff-[file tail $dir2]-$imgfile"]
2112
+ }
2113
+
2114
+ # auxiliary procedure to produce string comparing two values
2115
+ proc _diff_show_ratio {value1 value2} {
2116
+ if {[expr double ($value2)] == 0.} {
2117
+ return "$value1 / $value2"
2118
+ } else {
2119
+ return "$value1 / $value2 \[[format "%+5.2f%%" [expr 100 * ($value1 - $value2) / double($value2)]]\]"
2120
+ }
2121
+ }
2122
+
2123
+ # auxiliary procedure to produce string comparing two values, where first value is a portion of second
2124
+ proc _diff_show_positive_ratio {value1 value2} {
2125
+ if {[expr double ($value2)] == 0.} {
2126
+ return "$value1 / $value2"
2127
+ } else {
2128
+ return "$value1 / $value2 \[[format "%4.2f%%" [expr 100 * double($value1) / double($value2)]]\]"
2129
+ }
2130
+ }
2131
+
2132
+ # procedure to check cpu user time
2133
+ proc _check_time {regexp_msg} {
2134
+ upvar log log
2135
+ upvar log1 log1
2136
+ upvar log2 log2
2137
+ upvar log_cpu log_cpu
2138
+ upvar cpu cpu
2139
+ upvar basename basename
2140
+ upvar casename casename
2141
+ set time1_list [dict create]
2142
+ set time2_list [dict create]
2143
+ set cpu_find UNDEFINED
2144
+
2145
+ foreach line1 [split $log1 "\n"] {
2146
+ if { [regexp "${regexp_msg}" $line1 dump chronometer_name cpu_find] } {
2147
+ dict set time1_list "${chronometer_name}" "${cpu_find}"
2148
+ }
2149
+ }
2150
+
2151
+ foreach line2 [split $log2 "\n"] {
2152
+ if { [regexp "${regexp_msg}" $line2 dump chronometer_name cpu_find] } {
2153
+ dict set time2_list "${chronometer_name}" "${cpu_find}"
2154
+ }
2155
+ }
2156
+
2157
+ if { [llength [dict keys $time1_list]] != [llength [dict keys $time2_list]] } {
2158
+ puts "Error: number of dchrono/chrono COUNTER are different in the same test cases"
2159
+ } else {
2160
+ foreach key [dict keys $time1_list] {
2161
+ set time1 [dict get $time1_list $key]
2162
+ set time2 [dict get $time2_list $key]
2163
+
2164
+ # compare CPU user time with 10% precision (but not less 0.5 sec)
2165
+ if { [expr abs ($time1 - $time2) > 0.5 + 0.05 * abs ($time1 + $time2)] } {
2166
+ if {$cpu != false} {
2167
+ _log_and_puts log_cpu "COUNTER $key: [split $basename /] $casename: [_diff_show_ratio $time1 $time2]"
2168
+ } else {
2169
+ _log_and_puts log "COUNTER $key: [split $basename /] $casename: [_diff_show_ratio $time1 $time2]"
2170
+ }
2171
+ }
2172
+ }
2173
+ }
2174
+ }
2175
+
2176
+ # Procedure to compare results of two runs of test cases
2177
+ proc _test_diff {dir1 dir2 basename image cpu memory status verbose _logvar _logimage _logcpu _logmemory {_statvar ""}} {
2178
+ upvar $_logvar log
2179
+ upvar $_logimage log_image
2180
+ upvar $_logcpu log_cpu
2181
+ upvar $_logmemory log_memory
2182
+
2183
+ # make sure to load diffimage command
2184
+ uplevel pload VISUALIZATION
2185
+
2186
+ # prepare variable (array) for collecting statistics
2187
+ if { "$_statvar" != "" } {
2188
+ upvar $_statvar stat
2189
+ } else {
2190
+ set stat(cpu1) 0
2191
+ set stat(cpu2) 0
2192
+ set stat(mem1) 0
2193
+ set stat(mem2) 0
2194
+ set stat(img1) 0
2195
+ set stat(img2) 0
2196
+ set log {}
2197
+ set log_image {}
2198
+ set log_cpu {}
2199
+ set log_memory {}
2200
+ }
2201
+
2202
+ # first check subdirectories
2203
+ set path1 [file join $dir1 $basename]
2204
+ set path2 [file join $dir2 $basename]
2205
+ set list1 [glob -directory $path1 -types d -tails -nocomplain *]
2206
+ set list2 [glob -directory $path2 -types d -tails -nocomplain *]
2207
+ if { [llength $list1] >0 || [llength $list2] > 0 } {
2208
+ _list_diff $list1 $list2 in1 in2 common
2209
+ if { "$verbose" > 1 } {
2210
+ if { [llength $in1] > 0 } { _log_and_puts log "Only in $path1: $in1" }
2211
+ if { [llength $in2] > 0 } { _log_and_puts log "Only in $path2: $in2" }
2212
+ }
2213
+ foreach subdir $common {
2214
+ if { "$verbose" > 2 } {
2215
+ _log_and_puts log "Checking [file join $basename $subdir]"
2216
+ }
2217
+ _test_diff $dir1 $dir2 [file join $basename $subdir] $image $cpu $memory $status $verbose log log_image log_cpu log_memory stat
2218
+ }
2219
+ } else {
2220
+ # check log files (only if directory has no subdirs)
2221
+ set list1 [glob -directory $path1 -types f -tails -nocomplain *.log]
2222
+ set list2 [glob -directory $path2 -types f -tails -nocomplain *.log]
2223
+ _list_diff $list1 $list2 in1 in2 common
2224
+ if { "$verbose" > 1 } {
2225
+ if { [llength $in1] > 0 } { _log_and_puts log "Only in $path1: $in1" }
2226
+ if { [llength $in2] > 0 } { _log_and_puts log "Only in $path2: $in2" }
2227
+ }
2228
+ set gcpu1 0
2229
+ set gcpu2 0
2230
+ set gmem1 0
2231
+ set gmem2 0
2232
+ foreach logfile $common {
2233
+ # load two logs
2234
+ set log1 [_read_file [file join $dir1 $basename $logfile]]
2235
+ set log2 [_read_file [file join $dir2 $basename $logfile]]
2236
+ set casename [file rootname $logfile]
2237
+
2238
+ # check execution statuses
2239
+ if {$image == false && $cpu == false && $memory == false} {
2240
+ set status1 UNDEFINED
2241
+ set status2 UNDEFINED
2242
+ if { ! [regexp {CASE [^:]*:\s*([\w]+)} $log1 res1 status1] ||
2243
+ ! [regexp {CASE [^:]*:\s*([\w]+)} $log2 res2 status2] ||
2244
+ "$status1" != "$status2" } {
2245
+ _log_and_puts log "STATUS [split $basename /] $casename: $status1 / $status2"
2246
+ # if test statuses are different, further comparison makes
2247
+ # no sense unless explicitly requested
2248
+ if { "$status" != "all" } {
2249
+ continue
2250
+ }
2251
+ }
2252
+ if { "$status" == "ok" && "$status1" != "OK" } {
2253
+ continue
2254
+ }
2255
+ }
2256
+
2257
+ if { ! $image } {
2258
+ # check CPU user time in test cases
2259
+ set checkCPURegexp "COUNTER (.+): (\[-0-9.+eE\]+)"
2260
+ if { [regexp "${checkCPURegexp}" $log1] &&
2261
+ [regexp "${checkCPURegexp}" $log2] } {
2262
+ _check_time "${checkCPURegexp}"
2263
+ }
2264
+ }
2265
+
2266
+ # check CPU times
2267
+ if {$cpu != false || ($image == false && $cpu == false && $memory == false)} {
2268
+ set cpu1 UNDEFINED
2269
+ set cpu2 UNDEFINED
2270
+ if { [regexp {TOTAL CPU TIME:\s*([\d.]+)} $log1 res1 cpu1] &&
2271
+ [regexp {TOTAL CPU TIME:\s*([\d.]+)} $log2 res1 cpu2] } {
2272
+ set stat(cpu1) [expr $stat(cpu1) + $cpu1]
2273
+ set stat(cpu2) [expr $stat(cpu2) + $cpu2]
2274
+ set gcpu1 [expr $gcpu1 + $cpu1]
2275
+ set gcpu2 [expr $gcpu2 + $cpu2]
2276
+
2277
+ # compare CPU times with 10% precision (but not less 0.5 sec)
2278
+ if { [expr abs ($cpu1 - $cpu2) > 0.5 + 0.05 * abs ($cpu1 + $cpu2)] } {
2279
+ if {$cpu != false} {
2280
+ _log_and_puts log_cpu "CPU [split $basename /] $casename: [_diff_show_ratio $cpu1 $cpu2]"
2281
+ } else {
2282
+ _log_and_puts log "CPU [split $basename /] $casename: [_diff_show_ratio $cpu1 $cpu2]"
2283
+ }
2284
+ }
2285
+ }
2286
+ }
2287
+
2288
+ # check memory delta
2289
+ if {$memory != false || ($image == false && $cpu == false && $memory == false)} {
2290
+ set mem1 UNDEFINED
2291
+ set mem2 UNDEFINED
2292
+ if { [regexp {MEMORY DELTA:\s*([\d.]+)} $log1 res1 mem1] &&
2293
+ [regexp {MEMORY DELTA:\s*([\d.]+)} $log2 res1 mem2] } {
2294
+ set stat(mem1) [expr $stat(mem1) + $mem1]
2295
+ set stat(mem2) [expr $stat(mem2) + $mem2]
2296
+ set gmem1 [expr $gmem1 + $mem1]
2297
+ set gmem2 [expr $gmem2 + $mem2]
2298
+
2299
+ # compare memory usage with 10% precision (but not less 16 KiB)
2300
+ if { [expr abs ($mem1 - $mem2) > 16 + 0.05 * abs ($mem1 + $mem2)] } {
2301
+ if {$memory != false} {
2302
+ _log_and_puts log_memory "MEMORY [split $basename /] $casename: [_diff_show_ratio $mem1 $mem2]"
2303
+ } else {
2304
+ _log_and_puts log "MEMORY [split $basename /] $casename: [_diff_show_ratio $mem1 $mem2]"
2305
+ }
2306
+ }
2307
+ }
2308
+ }
2309
+
2310
+ # check images
2311
+ if {$image != false || ($image == false && $cpu == false && $memory == false)} {
2312
+ set aCaseDiffColorTol 0
2313
+ if { [regexp {IMAGE_COLOR_TOLERANCE:\s*([\d.]+)} $log1 res1 imgtol1] } { set aCaseDiffColorTol $imgtol1 }
2314
+ set imglist1 [glob -directory $path1 -types f -tails -nocomplain ${casename}.{png,gif} ${casename}-*.{png,gif} ${casename}_*.{png,gif}]
2315
+ set imglist2 [glob -directory $path2 -types f -tails -nocomplain ${casename}.{png,gif} ${casename}-*.{png,gif} ${casename}_*.{png,gif}]
2316
+ _list_diff $imglist1 $imglist2 imgin1 imgin2 imgcommon
2317
+ if { "$verbose" > 1 } {
2318
+ # Differences in image lists might reflect changes in test case or in list of tests (new/removed test cases),
2319
+ # but might also reflect image dump failures.
2320
+ if { [llength $imgin1] > 0 } {
2321
+ set stat(img1) [expr $stat(img1) + [llength $imgin1]]
2322
+ set stat(img2) [expr $stat(img2) + [llength $imgin1]]
2323
+ if {$image != false} {
2324
+ _log_and_puts log_image "Only in $path1: $imgin1"
2325
+ } else {
2326
+ _log_and_puts log "Only in $path1: $imgin1"
2327
+ }
2328
+ }
2329
+ if { [llength $imgin2] > 0 } {
2330
+ set stat(img1) [expr $stat(img1) + [llength $imgin2]]
2331
+ if {$image != false} {
2332
+ _log_and_puts log_image "Only in $path2: $imgin2"
2333
+ } else {
2334
+ _log_and_puts log "Only in $path2: $imgin2"
2335
+ }
2336
+ }
2337
+ }
2338
+
2339
+ foreach imgfile $imgcommon {
2340
+ set stat(img2) [expr $stat(img2) + 1]
2341
+ # if { $verbose > 1 } { _log_and_puts log "Checking [split basename /] $casename: $imgfile" }
2342
+ set diffile [_diff_img_name $dir1 $dir2 $basename $imgfile]
2343
+ if { [catch {diffimage [file join $dir1 $basename $imgfile] \
2344
+ [file join $dir2 $basename $imgfile] \
2345
+ -toleranceOfColor 0.0 -blackWhite off -borderFilter off $diffile} diff] } {
2346
+ set stat(img1) [expr $stat(img1) + 1]
2347
+ if {$image != false} {
2348
+ _log_and_puts log_image "IMAGE [split $basename /] $casename: $imgfile cannot be compared"
2349
+ } else {
2350
+ _log_and_puts log "IMAGE [split $basename /] $casename: $imgfile cannot be compared"
2351
+ }
2352
+ file delete -force $diffile ;# clean possible previous result of diffimage
2353
+ } elseif { $diff != 0 } {
2354
+ set diff [string trimright $diff \n]
2355
+ if {$aCaseDiffColorTol != 0} {
2356
+ # retry with color tolerance
2357
+ if { [catch {diffimage [file join $dir1 $basename $imgfile] \
2358
+ [file join $dir2 $basename $imgfile] \
2359
+ -toleranceOfColor $aCaseDiffColorTol -blackWhite off -borderFilter off $diffile} diff2] } {
2360
+ set stat(img1) [expr $stat(img1) + 1]
2361
+ if {$image != false} {
2362
+ _log_and_puts log_image "IMAGE [split $basename /] $casename: $imgfile cannot be compared"
2363
+ } else {
2364
+ _log_and_puts log "IMAGE [split $basename /] $casename: $imgfile cannot be compared"
2365
+ }
2366
+ continue
2367
+ } elseif { $diff2 == 0 } {
2368
+ # exclude image diff within tolerance but still keep info in the log
2369
+ set toLogImageCase false
2370
+ file delete -force $diffile
2371
+ set stat(img1) [expr $stat(img1) + 1]
2372
+ if {$image != false} {
2373
+ _log_and_puts log_image "IMAGE [split $basename /] $casename: $imgfile is similar \[$diff different pixels\]"
2374
+ } else {
2375
+ _log_and_puts log "IMAGE [split $basename /] $casename: $imgfile is similar \[$diff different pixels\]"
2376
+ }
2377
+ continue
2378
+ }
2379
+ }
2380
+
2381
+ set stat(img1) [expr $stat(img1) + 1]
2382
+ if {$image != false} {
2383
+ _log_and_puts log_image "IMAGE [split $basename /] $casename: $imgfile differs \[$diff different pixels\]"
2384
+ } else {
2385
+ _log_and_puts log "IMAGE [split $basename /] $casename: $imgfile differs \[$diff different pixels\]"
2386
+ }
2387
+ } else {
2388
+ file delete -force $diffile ;# clean useless artifact of diffimage
2389
+ }
2390
+ }
2391
+ }
2392
+ }
2393
+
2394
+ # report CPU and memory difference in group if it is greater than 10%
2395
+ if {$cpu != false || ($image == false && $cpu == false && $memory == false)} {
2396
+ if { [expr abs ($gcpu1 - $gcpu2) > 0.5 + 0.005 * abs ($gcpu1 + $gcpu2)] } {
2397
+ if {$cpu != false} {
2398
+ _log_and_puts log_cpu "CPU [split $basename /]: [_diff_show_ratio $gcpu1 $gcpu2]"
2399
+ } else {
2400
+ _log_and_puts log "CPU [split $basename /]: [_diff_show_ratio $gcpu1 $gcpu2]"
2401
+ }
2402
+ }
2403
+ }
2404
+ if {$memory != false || ($image == false && $cpu == false && $memory == false)} {
2405
+ if { [expr abs ($gmem1 - $gmem2) > 16 + 0.005 * abs ($gmem1 + $gmem2)] } {
2406
+ if {$memory != false} {
2407
+ _log_and_puts log_memory "MEMORY [split $basename /]: [_diff_show_ratio $gmem1 $gmem2]"
2408
+ } else {
2409
+ _log_and_puts log "MEMORY [split $basename /]: [_diff_show_ratio $gmem1 $gmem2]"
2410
+ }
2411
+ }
2412
+ }
2413
+ }
2414
+
2415
+ if { "$_statvar" == "" } {
2416
+ if {$memory != false || ($image == false && $cpu == false && $memory == false)} {
2417
+ if {$memory != false} {
2418
+ _log_and_puts log_memory "Total MEMORY difference: [_diff_show_ratio $stat(mem1) $stat(mem2)]"
2419
+ } else {
2420
+ _log_and_puts log "Total MEMORY difference: [_diff_show_ratio $stat(mem1) $stat(mem2)]"
2421
+ }
2422
+ }
2423
+ if {$cpu != false || ($image == false && $cpu == false && $memory == false)} {
2424
+ if {$cpu != false} {
2425
+ _log_and_puts log_cpu "Total CPU difference: [_diff_show_ratio $stat(cpu1) $stat(cpu2)]"
2426
+ } else {
2427
+ _log_and_puts log "Total CPU difference: [_diff_show_ratio $stat(cpu1) $stat(cpu2)]"
2428
+ }
2429
+ }
2430
+ if {$image != false || ($image == false && $cpu == false && $memory == false)} {
2431
+ if {$image != false} {
2432
+ _log_and_puts log_image "Total IMAGE difference: [_diff_show_positive_ratio $stat(img1) $stat(img2)]"
2433
+ } else {
2434
+ _log_and_puts log "Total IMAGE difference: [_diff_show_positive_ratio $stat(img1) $stat(img2)]"
2435
+ }
2436
+ }
2437
+ }
2438
+ }
2439
+
2440
+ # Auxiliary procedure to save log of results comparison to file
2441
+ proc _log_html_diff {file log dir1 dir2 highlight_percent} {
2442
+ # create missing directories as needed
2443
+ catch {file mkdir [file dirname $file]}
2444
+
2445
+ # try to open a file
2446
+ if [catch {set fd [open $file w]} res] {
2447
+ error "Error saving log file $file: $res"
2448
+ }
2449
+
2450
+ # print header
2451
+ puts $fd "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>"
2452
+ puts $fd "<title>Diff $dir1 vs. $dir2</title></head><body>"
2453
+ puts $fd "<h1>Comparison of test results:</h1>"
2454
+ puts $fd "<h2>Version A \[NEW\] - $dir1</h2>"
2455
+ puts $fd "<h2>Version B \[REF\] - $dir2</h2>"
2456
+
2457
+ # add script for switching between images on click
2458
+ puts $fd ""
2459
+ puts $fd "<script type=\"text/javascript\">"
2460
+ puts $fd " function diffimage_toggle(img,url1,url2)"
2461
+ puts $fd " {"
2462
+ puts $fd " if (img.show2nd) { img.src = url1; img.show2nd = false; }"
2463
+ puts $fd " else { img.src = url2; img.show2nd = true; }"
2464
+ puts $fd " }"
2465
+ puts $fd " function diffimage_reset(img,url) { img.src = url; img.show2nd = true; }"
2466
+ puts $fd "</script>"
2467
+ puts $fd ""
2468
+
2469
+ # print log body
2470
+ puts $fd "<pre>"
2471
+ set logpath [file split [file normalize $file]]
2472
+ foreach line $log {
2473
+ # put a line; highlight considerable (> ${highlight_percent}%) deviations of CPU and memory
2474
+ if { [regexp "\[\\\[](\[0-9.e+-]+)%\[\]]" $line res value] &&
2475
+ [expr abs($value)] > ${highlight_percent} } {
2476
+ puts $fd "<table><tr><td bgcolor=\"[expr $value > 0 ? \"ff8080\" : \"lightgreen\"]\">$line</td></tr></table>"
2477
+ } elseif { [regexp {^IMAGE[ \t]+([^:]+):[ \t]+([A-Za-z0-9_.-]+) is similar} $line res case img] } {
2478
+ if { [catch {eval file join "" [lrange $case 0 end-1]} gridpath] } {
2479
+ # note: special handler for the case if test grid directoried are compared directly
2480
+ set gridpath ""
2481
+ }
2482
+ set aCaseName [lindex $case end]
2483
+ puts $fd "<table><tr><td bgcolor=\"orange\"><a href=\"[_make_url $file [file join $dir1 $gridpath $aCaseName.html]]\">$line</a></td></tr></table>"
2484
+ } elseif { [regexp {^IMAGE[ \t]+([^:]+):[ \t]+([A-Za-z0-9_.-]+)} $line res case img] } {
2485
+ # add images
2486
+ puts $fd $line
2487
+ if { [catch {eval file join "" [lrange $case 0 end-1]} gridpath] } {
2488
+ # note: special handler for the case if test grid directoried are compared directly
2489
+ set gridpath ""
2490
+ }
2491
+ set aCaseName [lindex $case end]
2492
+ set img1url [_make_url $file [file join $dir1 $gridpath $img]]
2493
+ set img2url [_make_url $file [file join $dir2 $gridpath $img]]
2494
+ set img1 "<a href=\"[_make_url $file [file join $dir1 $gridpath $aCaseName.html]]\"><img src=\"$img1url\"></a>"
2495
+ set img2 "<a href=\"[_make_url $file [file join $dir2 $gridpath $aCaseName.html]]\"><img src=\"$img2url\"></a>"
2496
+
2497
+ set difffile [_diff_img_name $dir1 $dir2 $gridpath $img]
2498
+ set imgdurl [_make_url $file $difffile]
2499
+ if { [file exists $difffile] } {
2500
+ set imgd "<img src=\"$imgdurl\" onmouseout=diffimage_reset(this,\"$imgdurl\") onclick=diffimage_toggle(this,\"$img1url\",\"$img2url\")>"
2501
+ } else {
2502
+ set imgd "N/A"
2503
+ }
2504
+
2505
+ puts $fd "<table><tr><th><abbr title=\"$dir1\">Version A</abbr></th><th><abbr title=\"$dir2\">Version B</abbr></th><th>Diff (click to toggle)</th></tr>"
2506
+ puts $fd "<tr><td>$img1</td><td>$img2</td><td>$imgd</td></tr></table>"
2507
+ } else {
2508
+ puts $fd $line
2509
+ }
2510
+ }
2511
+ puts $fd "</pre></body></html>"
2512
+
2513
+ close $fd
2514
+ return
2515
+ }
2516
+
2517
+ # get number of CPUs on the system
2518
+ proc _get_nb_cpus {} {
2519
+ global tcl_platform env
2520
+
2521
+ if { "$tcl_platform(platform)" == "windows" } {
2522
+ # on Windows, take the value of the environment variable
2523
+ if { [info exists env(NUMBER_OF_PROCESSORS)] &&
2524
+ ! [catch {expr $env(NUMBER_OF_PROCESSORS) > 0} res] && $res >= 0 } {
2525
+ return $env(NUMBER_OF_PROCESSORS)
2526
+ }
2527
+ } elseif { "$tcl_platform(os)" == "Linux" } {
2528
+ # on Linux, take number of logical processors listed in /proc/cpuinfo
2529
+ if { [catch {open "/proc/cpuinfo" r} fd] } {
2530
+ return 0 ;# should never happen, but...
2531
+ }
2532
+ set nb 0
2533
+ while { [gets $fd line] >= 0 } {
2534
+ if { [regexp {^processor[ \t]*:} $line] } {
2535
+ incr nb
2536
+ }
2537
+ }
2538
+ close $fd
2539
+ return $nb
2540
+ } elseif { "$tcl_platform(os)" == "Darwin" } {
2541
+ # on MacOS X, call sysctl command
2542
+ if { ! [catch {exec sysctl hw.ncpu} ret] &&
2543
+ [regexp {^hw[.]ncpu[ \t]*:[ \t]*([0-9]+)} $ret res nb] } {
2544
+ return $nb
2545
+ }
2546
+ }
2547
+
2548
+ # if cannot get good value, return 0 as default
2549
+ return 0
2550
+ }
2551
+
2552
+ # check two files for difference
2553
+ proc _diff_files {file1 file2} {
2554
+ set fd1 [open $file1 "r"]
2555
+ set fd2 [open $file2 "r"]
2556
+
2557
+ set differ f
2558
+ while {! $differ} {
2559
+ set nb1 [gets $fd1 line1]
2560
+ set nb2 [gets $fd2 line2]
2561
+ if { $nb1 != $nb2 } { set differ t; break }
2562
+ if { $nb1 < 0 } { break }
2563
+ if { [string compare $line1 $line2] } {
2564
+ set differ t
2565
+ }
2566
+ }
2567
+
2568
+ close $fd1
2569
+ close $fd2
2570
+
2571
+ return $differ
2572
+ }
2573
+
2574
+ # Check if file is in DOS encoding.
2575
+ # This check is done by presence of \r\n combination at the end of the first
2576
+ # line (i.e. prior to any other \n symbol).
2577
+ # Note that presence of non-ascii symbols typically used for recognition
2578
+ # of binary files is not suitable since some IGES and STEP files contain
2579
+ # non-ascii symbols.
2580
+ # Special check is added for PNG files which contain \r\n in the beginning.
2581
+ proc _check_dos_encoding {file} {
2582
+ set fd [open $file rb]
2583
+ set isdos f
2584
+ if { [gets $fd line] && [regexp {.*\r$} $line] &&
2585
+ ! [regexp {^.PNG} $line] } {
2586
+ set isdos t
2587
+ }
2588
+ close $fd
2589
+ return $isdos
2590
+ }
2591
+
2592
+ # procedure to recognize format of a data file by its first symbols (for OCCT
2593
+ # BREP and geometry DRAW formats, IGES, and STEP) and extension (all others)
2594
+ proc _check_file_format {file} {
2595
+ set fd [open $file rb]
2596
+ set line [read $fd 1024]
2597
+ close $fd
2598
+
2599
+ set warn f
2600
+ set ext [file extension $file]
2601
+ set format unknown
2602
+ if { [regexp {^DBRep_DrawableShape} $line] } {
2603
+ set format BREP
2604
+ if { "$ext" != ".brep" && "$ext" != ".rle" &&
2605
+ "$ext" != ".draw" && "$ext" != "" } {
2606
+ set warn t
2607
+ }
2608
+ } elseif { [regexp {^DrawTrSurf_} $line] } {
2609
+ set format DRAW
2610
+ if { "$ext" != ".rle" &&
2611
+ "$ext" != ".draw" && "$ext" != "" } {
2612
+ set warn t
2613
+ }
2614
+ } elseif { [regexp {^[ \t]*ISO-10303-21} $line] } {
2615
+ set format STEP
2616
+ if { "$ext" != ".step" && "$ext" != ".stp" } {
2617
+ set warn t
2618
+ }
2619
+ } elseif { [regexp {^.\{72\}S[0 ]\{6\}1} $line] } {
2620
+ set format IGES
2621
+ if { "$ext" != ".iges" && "$ext" != ".igs" } {
2622
+ set warn t
2623
+ }
2624
+ } elseif { "$ext" == ".igs" } {
2625
+ set format IGES
2626
+ } elseif { "$ext" == ".stp" } {
2627
+ set format STEP
2628
+ } else {
2629
+ set format [string toupper [string range $ext 1 end]]
2630
+ }
2631
+
2632
+ if { $warn } {
2633
+ puts "$file: Warning: extension ($ext) does not match format ($format)"
2634
+ }
2635
+
2636
+ return $format
2637
+ }
2638
+
2639
+ # procedure to load file knowing its format
2640
+ proc load_data_file {file format shape} {
2641
+ switch $format {
2642
+ BREP { uplevel restore $file $shape }
2643
+ DRAW { uplevel restore $file $shape }
2644
+ IGES { pload XSDRAW; uplevel igesbrep $file $shape * }
2645
+ STEP { pload XSDRAW; uplevel stepread $file __a *; uplevel renamevar __a_1 $shape }
2646
+ STL { pload XSDRAW; uplevel readstl $shape $file triangulation }
2647
+ default { error "Cannot read $format file $file" }
2648
+ }
2649
+ }
2650
+
2651
+ # procedure to get name of temporary directory,
2652
+ # ensuring it is existing and writeable
2653
+ proc _get_temp_dir {} {
2654
+ global env tcl_platform
2655
+
2656
+ # check typical environment variables
2657
+ foreach var {TempDir Temp Tmp} {
2658
+ # check different case
2659
+ foreach name [list [string toupper $var] $var [string tolower $var]] {
2660
+ if { [info exists env($name)] && [file isdirectory $env($name)] &&
2661
+ [file writable $env($name)] } {
2662
+ return [regsub -all {\\} $env($name) /]
2663
+ }
2664
+ }
2665
+ }
2666
+
2667
+ # check platform-specific locations
2668
+ set fallback tmp
2669
+ if { "$tcl_platform(platform)" == "windows" } {
2670
+ set paths "c:/TEMP c:/TMP /TEMP /TMP"
2671
+ if { [info exists env(HOMEDRIVE)] && [info exists env(HOMEPATH)] } {
2672
+ set fallback [regsub -all {\\} "$env(HOMEDRIVE)$env(HOMEPATH)/tmp" /]
2673
+ }
2674
+ } else {
2675
+ set paths "/tmp /var/tmp /usr/tmp"
2676
+ if { [info exists env(HOME)] } {
2677
+ set fallback "$env(HOME)/tmp"
2678
+ }
2679
+ }
2680
+ foreach dir $paths {
2681
+ if { [file isdirectory $dir] && [file writable $dir] } {
2682
+ return $dir
2683
+ }
2684
+ }
2685
+
2686
+ # fallback case: use subdir /tmp of home or current dir
2687
+ file mkdir $fallback
2688
+ return $fallback
2689
+ }
2690
+
2691
+ # extract of code from testgrid command used to process jobs running in
2692
+ # parallel until number of jobs in the queue becomes equal or less than
2693
+ # specified value
2694
+ proc _testgrid_process_jobs {worker {nb_ok 0}} {
2695
+ # bind local vars to variables of the caller procedure
2696
+ upvar log log
2697
+ upvar logdir logdir
2698
+ upvar job_def job_def
2699
+ upvar nbpooled nbpooled
2700
+ upvar userbreak userbreak
2701
+ upvar refresh refresh
2702
+ upvar refresh_timer refresh_timer
2703
+
2704
+ catch {tpool::resume $worker}
2705
+ while { ! $userbreak && $nbpooled > $nb_ok } {
2706
+ foreach job [tpool::wait $worker [array names job_def]] {
2707
+ eval _log_test_case \[tpool::get $worker $job\] $job_def($job) log
2708
+ unset job_def($job)
2709
+ incr nbpooled -1
2710
+ }
2711
+
2712
+ # check for user break
2713
+ if { "[info commands dbreak]" == "dbreak" && [catch dbreak] } {
2714
+ set userbreak 1
2715
+ }
2716
+
2717
+ # update summary log with requested period
2718
+ if { $logdir != "" && $refresh > 0 && [clock seconds] > $refresh_timer + $refresh } {
2719
+ _log_summarize $logdir $log
2720
+ set refresh_timer [clock seconds]
2721
+ }
2722
+ }
2723
+ catch {tpool::suspend $worker}
2724
+ }
2725
+
2726
+ help checkcolor {
2727
+ Check pixel color.
2728
+ Use: checkcolor x y red green blue
2729
+ x y - pixel coordinates
2730
+ red green blue - expected pixel color (values from 0 to 1)
2731
+ Function check color with tolerance (5x5 area)
2732
+ }
2733
+ # Procedure to check color using command vreadpixel with tolerance
2734
+ proc checkcolor { coord_x coord_y rd_get gr_get bl_get } {
2735
+ puts "Coordinate x = $coord_x"
2736
+ puts "Coordinate y = $coord_y"
2737
+ puts "RED color of RGB is $rd_get"
2738
+ puts "GREEN color of RGB is $gr_get"
2739
+ puts "BLUE color of RGB is $bl_get"
2740
+
2741
+ if { $coord_x <= 1 || $coord_y <= 1 } {
2742
+ puts "Error : minimal coordinate is x = 2, y = 2. But we have x = $coord_x y = $coord_y"
2743
+ return -1
2744
+ }
2745
+
2746
+ set color ""
2747
+ catch { [set color "[vreadpixel ${coord_x} ${coord_y} rgb]"] }
2748
+ if {"$color" == ""} {
2749
+ puts "Error : Pixel coordinates (${position_x}; ${position_y}) are out of view"
2750
+ }
2751
+ set rd [lindex $color 0]
2752
+ set gr [lindex $color 1]
2753
+ set bl [lindex $color 2]
2754
+ set rd_int [expr int($rd * 1.e+05)]
2755
+ set gr_int [expr int($gr * 1.e+05)]
2756
+ set bl_int [expr int($bl * 1.e+05)]
2757
+ set rd_ch [expr int($rd_get * 1.e+05)]
2758
+ set gr_ch [expr int($gr_get * 1.e+05)]
2759
+ set bl_ch [expr int($bl_get * 1.e+05)]
2760
+
2761
+ if { $rd_ch != 0 } {
2762
+ set tol_rd [expr abs($rd_ch - $rd_int)/$rd_ch]
2763
+ } else {
2764
+ set tol_rd $rd_int
2765
+ }
2766
+ if { $gr_ch != 0 } {
2767
+ set tol_gr [expr abs($gr_ch - $gr_int)/$gr_ch]
2768
+ } else {
2769
+ set tol_gr $gr_int
2770
+ }
2771
+ if { $bl_ch != 0 } {
2772
+ set tol_bl [expr abs($bl_ch - $bl_int)/$bl_ch]
2773
+ } else {
2774
+ set tol_bl $bl_int
2775
+ }
2776
+
2777
+ set status 0
2778
+ if { $tol_rd > 0.2 } {
2779
+ puts "Warning : RED light of additive color model RGB is invalid"
2780
+ set status 1
2781
+ }
2782
+ if { $tol_gr > 0.2 } {
2783
+ puts "Warning : GREEN light of additive color model RGB is invalid"
2784
+ set status 1
2785
+ }
2786
+ if { $tol_bl > 0.2 } {
2787
+ puts "Warning : BLUE light of additive color model RGB is invalid"
2788
+ set status 1
2789
+ }
2790
+
2791
+ if { $status != 0 } {
2792
+ puts "Warning : Colors of default coordinate are not equal"
2793
+ }
2794
+
2795
+ global stat
2796
+ if { $tol_rd > 0.2 || $tol_gr > 0.2 || $tol_bl > 0.2 } {
2797
+ set info [_checkpoint $coord_x $coord_y $rd_ch $gr_ch $bl_ch]
2798
+ set stat [lindex $info end]
2799
+ if { ${stat} != 1 } {
2800
+ puts "Error : Colors are not equal in default coordinate and in the near coordinates too"
2801
+ return $stat
2802
+ } else {
2803
+ puts "Point with valid color was found"
2804
+ return $stat
2805
+ }
2806
+ } else {
2807
+ set stat 1
2808
+ }
2809
+ }
2810
+
2811
+ # Procedure to check color in the point near default coordinate
2812
+ proc _checkpoint {coord_x coord_y rd_ch gr_ch bl_ch} {
2813
+ set x_start [expr ${coord_x} - 2]
2814
+ set y_start [expr ${coord_y} - 2]
2815
+ set mistake 0
2816
+ set i 0
2817
+ while { $mistake != 1 && $i <= 5 } {
2818
+ set j 0
2819
+ while { $mistake != 1 && $j <= 5 } {
2820
+ set position_x [expr ${x_start} + $j]
2821
+ set position_y [expr ${y_start} + $i]
2822
+ puts $position_x
2823
+ puts $position_y
2824
+
2825
+ set color ""
2826
+ catch { [set color "[vreadpixel ${position_x} ${position_y} rgb]"] }
2827
+ if {"$color" == ""} {
2828
+ puts "Warning : Pixel coordinates (${position_x}; ${position_y}) are out of view"
2829
+ incr j
2830
+ continue
2831
+ }
2832
+ set rd [lindex $color 0]
2833
+ set gr [lindex $color 1]
2834
+ set bl [lindex $color 2]
2835
+ set rd_int [expr int($rd * 1.e+05)]
2836
+ set gr_int [expr int($gr * 1.e+05)]
2837
+ set bl_int [expr int($bl * 1.e+05)]
2838
+
2839
+ if { $rd_ch != 0 } {
2840
+ set tol_rd [expr abs($rd_ch - $rd_int)/$rd_ch]
2841
+ } else {
2842
+ set tol_rd $rd_int
2843
+ }
2844
+ if { $gr_ch != 0 } {
2845
+ set tol_gr [expr abs($gr_ch - $gr_int)/$gr_ch]
2846
+ } else {
2847
+ set tol_gr $gr_int
2848
+ }
2849
+ if { $bl_ch != 0 } {
2850
+ set tol_bl [expr abs($bl_ch - $bl_int)/$bl_ch]
2851
+ } else {
2852
+ set tol_bl $bl_int
2853
+ }
2854
+
2855
+ if { $tol_rd > 0.2 || $tol_gr > 0.2 || $tol_bl > 0.2 } {
2856
+ puts "Warning : Point with true color was not found near default coordinates"
2857
+ set mistake 0
2858
+ } else {
2859
+ set mistake 1
2860
+ }
2861
+ incr j
2862
+ }
2863
+ incr i
2864
+ }
2865
+ return $mistake
2866
+ }
2867
+
2868
+ # Procedure to check if sequence of values in listval follows linear trend
2869
+ # adding the same delta on each step.
2870
+ #
2871
+ # The function does statistical estimation of the mean variation of the
2872
+ # values of the sequence, and dispersion, and returns true only if both
2873
+ # dispersion and deviation of the mean from expected delta are within
2874
+ # specified tolerance.
2875
+ #
2876
+ # If mean variation differs from expected delta on more than two dispersions,
2877
+ # the check fails and procedure raises error with specified message.
2878
+ #
2879
+ # Otherwise the procedure returns false meaning that more iterations are needed.
2880
+ # Note that false is returned in any case if length of listval is less than 3.
2881
+ #
2882
+ # See example of use to check memory leaks in bugs/caf/bug23489
2883
+ #
2884
+ proc checktrend {listval delta tolerance message} {
2885
+ set nbval [llength $listval]
2886
+ if { $nbval < 3} {
2887
+ return 0
2888
+ }
2889
+
2890
+ # calculate mean value
2891
+ set mean 0.
2892
+ set prev [lindex $listval 0]
2893
+ foreach val [lrange $listval 1 end] {
2894
+ set mean [expr $mean + ($val - $prev)]
2895
+ set prev $val
2896
+ }
2897
+ set mean [expr $mean / ($nbval - 1)]
2898
+
2899
+ # calculate dispersion
2900
+ set sigma 0.
2901
+ set prev [lindex $listval 0]
2902
+ foreach val [lrange $listval 1 end] {
2903
+ set d [expr ($val - $prev) - $mean]
2904
+ set sigma [expr $sigma + $d * $d]
2905
+ set prev $val
2906
+ }
2907
+ set sigma [expr sqrt ($sigma / ($nbval - 2))]
2908
+
2909
+ puts "Checking trend: nb = $nbval, mean delta = $mean, sigma = $sigma"
2910
+
2911
+ # check if deviation is definitely too big
2912
+ if { abs ($mean - $delta) > $tolerance + 2. * $sigma } {
2913
+ puts "Checking trend failed: mean delta per step = $mean, sigma = $sigma, expected delta = $delta"
2914
+ error "$message"
2915
+ }
2916
+
2917
+ # check if deviation is clearly within a range
2918
+ return [expr abs ($mean - $delta) <= $sigma && $sigma <= $tolerance]
2919
+ }
2920
+
2921
+ # Procedure to clean up test results by removing skipped test directories
2922
+ help cleanuptest {
2923
+ Clean up test results by removing skipped test case directories and non-essential files.
2924
+ Use: cleanuptest results_dir
2925
+ Where results_dir is the directory containing test results including tests.log
2926
+ }
2927
+ proc cleanuptest {results_dir} {
2928
+ # Function to extract test case path from test case name
2929
+ proc get_test_path {test_case} {
2930
+ # Extract directory parts from test case string
2931
+ # Format: "CASE group grid case: status"
2932
+ if { [regexp {^CASE ([^ ]+) (.*[^ ]) ([^ ]+):} $test_case -> group grid case] } {
2933
+ # Remove any extra spaces from grid
2934
+ set grid [string trim $grid]
2935
+ return [file join $group $grid $case]
2936
+ }
2937
+ puts "Error: Cannot parse test case: $test_case"
2938
+ return ""
2939
+ }
2940
+
2941
+ set log_file [file join $results_dir "tests.log"]
2942
+ if { ! [file exists $log_file] } {
2943
+ puts "Error: No tests.log found in $results_dir"
2944
+ return
2945
+ }
2946
+
2947
+ # Process tests.log and find skipped tests
2948
+ set fd [open $log_file r]
2949
+ while {[gets $fd line] >= 0} {
2950
+ if {[regexp {^CASE.*: SKIPPED \(data file is missing\)$} $line]} {
2951
+ set test_path [get_test_path $line]
2952
+ if { $test_path != "" } {
2953
+ set full_path [file join $results_dir $test_path]
2954
+ # Delete any files with this base path (any extension)
2955
+ set files_to_delete [glob -nocomplain "${full_path}*"]
2956
+ foreach file $files_to_delete {
2957
+ if {[file exists $file]} {
2958
+ file delete -force $file
2959
+ }
2960
+ }
2961
+ # Delete directory if it exists
2962
+ if {[file isdirectory $full_path]} {
2963
+ file delete -force $full_path
2964
+ }
2965
+ }
2966
+ }
2967
+ }
2968
+ close $fd
2969
+ }