source: trunk/gui/apps/rpdiff @ 2588

Last change on this file since 2588 was 2577, checked in by mmc, 13 years ago

Added a new "rpdiff" command that can be used to diff the output from
two different Rappture run.xml files. Returns exit code 0 if the files
are identical, or 1 if they differ (along with a list of diffs printed
on standard output).

  • Property svn:executable set to *
File size: 21.3 KB
Line 
1#!/bin/sh
2# ----------------------------------------------------------------------
3#  RPDIFF
4#
5#  Compares two Rappture run files and looks for differences in the
6#  output results.  This is useful for the BOINC integration and other
7#  job execution infrastructures, so we can compare the results from
8#  multiple runs.
9#
10#    USAGE:
11#    % rpdiff <runFile1> <runFile2>
12#
13#  Exits with status 0 if the files are the same, and non-zero status
14#  if the files are different.  Prints differences on stdout.  Prints
15#  any errors on stderr.
16# ======================================================================
17#  AUTHOR:  Michael McLennan, Purdue University
18#  Copyright (c) 2004-2011  Purdue Research Foundation
19#
20#  See the file "license.terms" for information on usage and
21#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
22# ======================================================================
23# \
24exec tclsh "$0" ${1+"$@"}
25# tclsh executes the rest...
26
27package require Rappture
28
29# ----------------------------------------------------------------------
30# USAGE: diff <path> <lib1> <lib2>
31#
32# Used to compare two Rappture library objects at the specified <path>
33# in <lib1> and <lib2>.  Returns "" if they are the same, and a list
34# of differences if they are not.
35# ----------------------------------------------------------------------
36proc diff {path lib1 lib2} {
37    set knowntypes {boolean choice cloud curve field group histogram image integer loader log mesh note number periodicelement phase sequence string structure table unirect2d}
38
39    set type1 [$lib1 element -as type $path]
40    set type2 [$lib2 element -as type $path]
41    if {($type1 eq "" && $type2 ne "") || ($type1 ne "" && $type2 eq "")} {
42        return "missing component at $path"
43    } elseif {$type1 ne $type2} {
44        return "component mismatch at $path"
45    }
46
47    set diffs ""
48    switch -- $type1 {
49        input - output - group - phase {
50            set cpath1 [$lib1 children -as path $path]
51            set cpath2 [$lib2 children -as path $path]
52
53            # scan through all components in lib1 and diff against lib2
54            foreach cp $cpath1 {
55                set ct [$lib1 element -as type $cp]
56                if {[lsearch $knowntypes $ct] < 0} {
57                    # this isn't a real rappture object -- skip it
58                    set i [lsearch -exact $cpath2 $cp]
59                    if {$i >= 0} {
60                        set cpath2 [lreplace $cpath2 $i $i]
61                    }
62                    continue
63                }
64
65                set i [lsearch -exact $cpath2 $cp]
66                if {$i < 0} {
67                    lappend diffs "missing missing component at $cp"
68                } else {
69                    set cpath2 [lreplace $cpath2 $i $i]
70                    eval lappend diffs [diff $cp $lib1 $lib2]
71                }
72            }
73
74            # components left over that are in lib2 but not in lib1?
75            foreach cp $cpath2 {
76                lappend diffs "missing component at $cp"
77            }
78        }
79
80        boolean {
81            eval lappend diffs [diffattrs {
82                about.label about.description default
83            } $path $lib1 $lib2]
84
85            set v1 [$lib1 get $path.current]
86            if {$v1 ne "" && [catch {expr $v1} newval] == 0} { set v1 $newval }
87
88            set v2 [$lib2 get $path.current]
89            if {$v2 ne "" && [catch {expr $v2} newval] == 0} { set v2 $newval }
90
91            if {$v1 ne $v2} {
92                lappend diffs "value mismatch at $path.current"
93            }
94        }
95        choice {
96            eval lappend diffs [diffattrs {
97                about.label about.description default
98            } $path $lib1 $lib2]
99
100            set v1 [$lib1 get $path.current]
101            set v2 [$lib2 get $path.current]
102            if {$v1 ne $v2} {
103                lappend diffs "value mismatch at $path.current"
104            }
105        }
106        cloud {
107            eval lappend diffs [diffattrs {
108                about.label about.description units hide
109            } $path $lib1 $lib2]
110
111            eval lappend diffs [diffdatalines $path.points $lib1 $lib2]
112        }
113        curve {
114            eval lappend diffs [diffattrs {
115                about.label about.description
116                xaxis.label xaxis.description xaxis.units
117                yaxis.label yaxis.description yaxis.units
118            } $path $lib1 $lib2]
119
120            set cpath2 [$lib2 children -as path $path.component]
121            foreach elem [$lib1 children -as path $path.component] {
122                set i [lsearch -exact $cpath2 $elem]
123                if {$i >= 0} {
124                    set cpath2 [lreplace $cpath2 $i $i]
125                }
126
127                set data1 [$lib1 get $elem]
128                set data2 [$lib2 get $elem]
129                if {[llength $data1] != [llength $data2]} {
130                    lappend diffs "value mismatch at $elem"
131                    break
132                }
133
134                foreach d1 $data1 d2 $data2 {
135                    if {[Rappture::objects::ObjVal::cmpdbl $d1 $d2] != 0} {
136                        lappend diffs "value mismatch at $elem"
137                        break
138                    }
139                }
140            }
141            foreach elem $cpath2 {
142                # anything left in lib2 that's not in lib1?
143                lappend diffs "missing component at $elem"
144            }
145        }
146        field {
147            eval lappend diffs [diffattrs {
148                about.label about.description
149            } $path $lib1 $lib2]
150
151            set cpath2 [$lib2 children -as path -type component $path]
152            foreach elem [$lib1 children -as path -type component $path] {
153                set i [lsearch -exact $cpath2 $elem]
154                if {$i >= 0} {
155                    set cpath2 [lreplace $cpath2 $i $i]
156                }
157
158                # the flow attributes may or may not exist
159                eval lappend diffs [diffattrs {
160                    flow.label flow.description
161                } $elem $lib1 $lib2]
162
163                # must have the same dx (if defined)
164                set v1 [$lib1 get $elem.dx]
165                set v2 [$lib2 get $elem.dx]
166                if {$v1 ne $v2} {
167                    lappend diffs "value mismatch at $elem.dx"
168                    continue
169                }
170
171                # must have the same mesh
172                set v1 [$lib1 get $elem.mesh]
173                set v2 [$lib2 get $elem.mesh]
174                if {$v1 ne $v2} {
175                    lappend diffs "value mismatch at $elem.mesh"
176                    continue
177                }
178
179                # must have the same values
180                eval lappend diffs [diffdatalines $elem.values $lib1 $lib2]
181            }
182            foreach elem $cpath2 {
183                # anything left in lib2 that's not in lib1?
184                lappend diffs "missing component at $elem"
185            }
186        }
187        histogram {
188            eval lappend diffs [diffattrs {
189                about.label about.description
190                xaxis.label xaxis.description xaxis.units
191                yaxis.label yaxis.description yaxis.units
192            } $path $lib1 $lib2]
193
194            set cpath2 [$lib2 children -as path $path.component]
195            foreach elem [$lib1 children -as path $path.component] {
196                set i [lsearch -exact $cpath2 $elem]
197                if {$i >= 0} {
198                    set cpath2 [lreplace $cpath2 $i $i]
199                }
200
201                eval lappend diffs [diffdatalines $elem $lib1 $lib2]
202            }
203            foreach elem $cpath2 {
204                # anything left in lib2 that's not in lib1?
205                lappend diffs "missing component at $elem"
206            }
207        }
208        image {
209            eval lappend diffs [diffattrs {
210                about.label about.description resize convert
211            } $path $lib1 $lib2]
212
213            set v1 [$lib1 get $path.current]
214            set v2 [$lib2 get $path.current]
215            if {$v1 ne $v2} {
216                lappend diffs "value mismatch at $path.current"
217            }
218        }
219        integer {
220            eval lappend diffs [diffattrs {
221                about.label about.description min max default
222            } $path $lib1 $lib2]
223
224            set v1 [$lib1 get $path.current]
225            set v2 [$lib2 get $path.current]
226            if {[string is integer -strict $v1]
227                  && [string is integer -strict $v2]} {
228                if {$v1 != $v2} {
229                    lappend diffs "value mismatch at $path.current"
230                }
231            } elseif {$v1 ne $v2} {
232                lappend diffs "value mismatch at $path.current"
233            }
234        }
235        log {
236            set v1 [$lib1 get $path]
237            set v2 [$lib2 get $path]
238            if {$v1 ne $v2} {
239                lappend diffs "value mismatch at $path"
240            }
241        }
242        mesh {
243            eval lappend diffs [diffattrs {
244                about.label about.description units hide
245            } $path $lib1 $lib2]
246
247            # look for differences in nodes
248            set cpath2 [$lib2 children -as path -type node $path]
249            foreach elem [$lib1 children -as path -type node $path] {
250                set i [lsearch -exact $cpath2 $elem]
251                if {$i >= 0} {
252                    set cpath2 [lreplace $cpath2 $i $i]
253                }
254
255                eval lappend diffs [diffdatalines $elem $lib1 $lib2]
256            }
257            foreach elem $cpath2 {
258                # anything left in lib2 that's not in lib1?
259                lappend diffs "missing component at $elem"
260            }
261
262            # look for differences in elements
263            set cpath2 [$lib2 children -as path -type element $path]
264            foreach elem [$lib1 children -as path -type element $path] {
265                set i [lsearch -exact $cpath2 $elem]
266                if {$i >= 0} {
267                    set cpath2 [lreplace $cpath2 $i $i]
268                }
269
270                set v1 [$lib1 get $elem.node]
271                set v2 [$lib2 get $elem.node]
272                if {$v1 ne $v2} {
273                    lappend diffs "value mismatch at $path.node"
274                }
275            }
276            foreach elem $cpath2 {
277                # anything left in lib2 that's not in lib1?
278                lappend diffs "missing component at $elem"
279            }
280        }
281        note {
282            eval lappend diffs [diffattrs {contents} $path $lib1 $lib2]
283        }
284        number {
285            eval lappend diffs [diffattrs {
286                about.label about.description about.icon
287                min max units color default
288            } $path $lib1 $lib2]
289
290            set v1 [$lib1 get $path.current]
291            set v2 [$lib2 get $path.current]
292            if {[string is double -strict $v1]
293                  && [string is double -strict $v2]} {
294                if {[Rappture::objects::ObjVal::cmpdbl $v1 $v2] != 0} {
295                    lappend diffs "value mismatch at $path.current"
296                }
297            } elseif {$v1 ne $v2} {
298                lappend diffs "value mismatch at $path.current"
299            }
300        }
301        sequence {
302            eval lappend diffs [diffattrs {
303                about.label about.description index.label
304            } $path $lib1 $lib2]
305
306            set cpath2 [$lib2 children -as path -type element $path]
307            foreach elem [$lib1 children -as path -type element $path] {
308                set i [lsearch -exact $cpath2 $elem]
309                if {$i >= 0} {
310                    set cpath2 [lreplace $cpath2 $i $i]
311                }
312
313                # must have the same index
314                set v1 [$lib1 get $elem.index]
315                set v2 [$lib2 get $elem.index]
316                if {$v1 ne $v2} {
317                    lappend diffs "value mismatch at $elem.index"
318                    continue
319                }
320
321                # must have the same values
322                set parts1 [lsort [$lib1 children -as path $elem]]
323                set parts2 [lsort [$lib2 children -as path $elem]]
324                if {$parts1 ne $parts2} {
325                    lappend diffs "value mismatch at $elem"
326                    continue
327                }
328
329                foreach p $parts1 {
330                    set ptype [$lib1 element -as type $p]
331                    if {[lsearch $knowntypes $ptype] >= 0} {
332                        eval lappend diffs [diff $p $lib1 $lib2]
333                    }
334                }
335            }
336            foreach elem $cpath2 {
337                # anything left in lib2 that's not in lib1?
338                lappend diffs "missing component at $elem"
339            }
340        }
341        string {
342            eval lappend diffs [diffattrs {
343                about.label about.description default size hints
344            } $path $lib1 $lib2]
345
346            set v1 [$lib1 get $path.current]
347            set v2 [$lib2 get $path.current]
348            if {$v1 ne $v2} {
349                lappend diffs "value mismatch at $path.current"
350            }
351        }
352        structure {
353            eval lappend diffs [diffattrs {
354                about.label about.description
355            } $path $lib1 $lib2]
356
357            # check all parameters associated with the structure
358            set cpath2 [$lib2 children -as path $path.current.parameters]
359            foreach elem [$lib1 children -as path $path.current.parameters] {
360                set i [lsearch -exact $cpath2 $elem]
361                if {$i >= 0} {
362                    set cpath2 [lreplace $cpath2 $i $i]
363                }
364                eval lappend diffs [diff $elem $lib1 $lib2]
365            }
366            foreach elem $cpath2 {
367                # anything left in lib2 that's not in lib1?
368                lappend diffs "missing component at $elem"
369            }
370
371            # check all components in the structure
372            set cpath2 [$lib2 children -as path $path.current.components]
373            foreach elem [$lib1 children -as path $path.current.components] {
374                set i [lsearch -exact $cpath2 $elem]
375                if {$i >= 0} {
376                    set cpath2 [lreplace $cpath2 $i $i]
377                }
378
379                switch -- [$lib1 element -as type $elem] {
380                    box {
381                        eval lappend diffs [diffattrs {
382                            about.label about.color about.icon material
383                        } $elem $lib1 $lib2]
384
385                        set c2 [$lib2 children -as path -type corner $elem]
386                        foreach c [$lib1 children -as path -type corner $elem] {
387                            set i [lsearch -exact $c2 $c]
388                            if {$i >= 0} {
389                                set c2 [lreplace $c2 $i $i]
390                            }
391
392                            set v1 [$lib1 get $c]
393                            set v2 [$lib2 get $c]
394                            if {$v1 ne $v2} {
395                                lappend diffs "value mismatch at $c"
396                            }
397                        }
398                        foreach c $c2 {
399                            # anything left in lib2 that's not in lib1?
400                            lappend diffs "missing component at $c"
401                        }
402                    }
403                    molecule {
404                        eval lappend diffs [diffattrs {
405                            about.emblems formula
406                        } $elem $lib1 $lib2]
407
408                        # check for PDB data
409                        set v1 [$lib1 get $elem.pdb]
410                        set v2 [$lib2 get $elem.pdb]
411                        if {$v1 ne $v2} {
412                            lappend diffs "value mismatch at $elem.pdb"
413                        }
414
415                        # check for the old-style atom data
416                        set c2 [$lib2 children -as path -type atom $elem]
417                        foreach c [$lib1 children -as path -type atom $elem] {
418                            set i [lsearch -exact $c2 $c]
419                            if {$i >= 0} {
420                                set c2 [lreplace $c2 $i $i]
421                            }
422
423                            set v1 [$lib1 get $c.symbol]
424                            set v2 [$lib2 get $c.symbol]
425                            if {$v1 ne $v2} {
426                                lappend diffs "value mismatch at $c"
427                            }
428
429                            set df [diffdatalines $c.xyz $lib1 $lib2]
430                            if {$df ne ""} {
431                                eval lappend diffs $df
432                            }
433                        }
434                        foreach c $c2 {
435                            # anything left in lib2 that's not in lib1?
436                            lappend diffs "missing component at $c"
437                        }
438                    }
439                    default {
440                        error "don't know how to diff $elem"
441                    }
442                }
443            }
444            foreach elem $cpath2 {
445                # anything left in lib2 that's not in lib1?
446                lappend diffs "missing component at $elem"
447            }
448        }
449        table {
450            eval lappend diffs [diffattrs {
451                about.label about.description
452            } $path $lib1 $lib2]
453
454            set cpath2 [$lib2 children -as path -type column $path]
455            foreach elem [$lib1 children -as path -type column $path] {
456                set i [lsearch -exact $cpath2 $elem]
457                if {$i >= 0} {
458                    set cpath2 [lreplace $cpath2 $i $i]
459                }
460
461                eval lappend diffs [diffattrs {label units} $elem $lib1 $lib2]
462            }
463            foreach elem $cpath2 {
464                # anything left in lib2 that's not in lib1?
465                lappend diffs "missing component at $elem"
466            }
467
468            # make sure the table data matches
469            eval lappend diffs [diffdatalines $path.data $lib1 $lib2]
470        }
471        unirect2D {
472            eval lappend diffs [diffattrs {
473                about.label about.description hide
474                xaxis.label xaxis.units xaxis.numpoints
475                yaxis.label yaxis.units yaxis.numpoints
476            } $path $lib1 $lib2]
477
478            foreach elem {xaxis.min xaxis.max yaxis.min yaxis.max} {
479                set v1 [$lib1 get $path.$elem]
480                set v2 [$lib2 get $path.$elem]
481
482                if {[string is double -strict $v1]
483                      && [string is double -strict $v2]} {
484                    if {[Rappture::objects::ObjVal::cmpdbl $v1 $v2] != 0} {
485                        lappend diffs "value mismatch at $path.$elem"
486                    }
487                } elseif {$v1 ne $v2} {
488                    lappend diffs "value mismatch at $path.$elem"
489                }
490            }
491        }
492        default {
493            error "don't know how to compare type \"$type\""
494        }
495    }
496    return $diffs
497}
498
499# ----------------------------------------------------------------------
500# USAGE: diffattrs <attrList> <path> <lib1> <lib2>
501#
502# Used internally by the "diff" proc to look for a list of attributes
503# at the given <path> and see if there are any differences.  It is
504# considered a difference if the attribute is defined on one object
505# but not another, or if the string values are different.  Returns ""
506# if they are the same, and a list of differences if they are not.
507# ----------------------------------------------------------------------
508proc diffattrs {attrlist path lib1 lib2} {
509    set diffs ""
510    foreach name $attrlist {
511        set t1 [$lib1 element -as type $path.$name]
512        set t2 [$lib2 element -as type $path.$name]
513        if {$t1 ne "" || $t2 ne ""} {
514            if {[$lib1 get $path.$name] ne [$lib2 get $path.$name]} {
515                lappend diffs "attribute mismatch at $path.$name"
516            }
517        }
518    }
519    return $diffs
520}
521
522# ----------------------------------------------------------------------
523# USAGE: diffdatalines <path> <lib1> <lib2>
524#
525# Used internally by the "diff" proc to look for a list of attributes
526# at the given <path> and see if there are any differences.  It is
527# considered a difference if the attribute is defined on one object
528# but not another, or if the string values are different.  Returns ""
529# if they are the same, and a list of differences if they are not.
530# ----------------------------------------------------------------------
531proc diffdatalines {path lib1 lib2} {
532    set diffs ""
533
534    set data1 [split [string trim [$lib1 get $path]] \n]
535    set data2 [split [string trim [$lib2 get $path]] \n]
536    if {[llength $data1] != [llength $data2]} {
537        return [list "value mismatch at $path"]
538    }
539
540    foreach line1 $data1 line2 $data2 {
541        if {[llength $line1] != [llength $line2]} {
542            return [list "value mismatch at $path"]
543        }
544        foreach d1 $line1 d2 $line2 {
545            if {[string is double -strict $d1]
546                  && [string is double -strict $d2]} {
547                if {[Rappture::objects::ObjVal::cmpdbl $d1 $d2] != 0} {
548                    return [list "value mismatch at $path"]
549                }
550            } elseif {$d1 ne $d2} {
551                return [list "value mismatch at $path"]
552            }
553        }
554    }
555    return ""
556}
557
558# ======================================================================
559if {$argc != 2} {
560    puts stderr "USAGE: rpdiff file1.xml file2.xml"
561    exit 9
562}
563set lib1 [Rappture::library [lindex $argv 0]]
564set lib2 [Rappture::library [lindex $argv 1]]
565
566# compute the differences
567set diffs [diff output $lib1 $lib2]
568
569if {[llength $diffs] == 0} {
570    exit 0
571}
572
573# print a list of differences and exit
574foreach mesg $diffs {
575    puts $mesg
576}
577exit 1
Note: See TracBrowser for help on using the repository browser.