Ignore:
Timestamp:
Feb 1, 2011 5:37:45 PM (13 years ago)
Author:
mmc
Message:

Some preliminary changes toward a new way of exploring test results.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tester/testtree.tcl

    r2068 r2077  
    3333    inherit itk::Widget
    3434
    35     itk_option define -selectcommand selectCommand SelectCommand ""
    36     itk_option define -testdir testDir TestDir ""
    37     itk_option define -toolxml toolXml ToolXml ""
    38 
    3935    constructor {args} { #defined later }
    4036    destructor { #defined later }
    4137
    42     public method getTest {args}
    43     public method refresh {args}
    44 
    45     protected method getData {id}
    46     protected method getLeaves {{id 0}}
    47     protected method getSelected {}
    48     protected method populate {args}
    49     protected method runSelected {}
    50     protected method runTest {id}
    51     protected method setData {id data}
    52     protected method updateLabel {}
    53 
     38    public method add {args}
     39    public method clear {}
     40    public method curselection {}
     41
     42    protected method _getLeaves {{id 0}}
     43    protected method _getTest {id}
     44    protected method _refresh {args}
     45
     46    # add support for a spinning icon
     47    proc spinner {op}
     48
     49    private common spinner
     50    set spinner(frames) 8
     51    set spinner(current) 0
     52    set spinner(pending) ""
     53    set spinner(uses) 0
     54
     55    for {set n 0} {$n < $spinner(frames)} {incr n} {
     56        set spinner(frame$n) [Rappture::icon circle-ball[expr {$n+1}]]
     57    }
     58    set spinner(image) [image create photo -width [image width $spinner(frame0)] -height [image height $spinner(frame0)]]
    5459}
    5560 
     
    6671            -xscrollmode auto -yscrollmode auto
    6772    }
     73    pack $itk_component(scrollbars) -expand yes -fill both
     74
    6875    itk_component add treeview {
    6976        blt::treeview $itk_component(scrollbars).treeview -separator | \
    70             -autocreate true -selectmode multiple
     77            -autocreate true -selectmode multiple \
     78            -icons [list [Rappture::icon folder] [Rappture::icon folder2]] \
     79            -activeicons ""
    7180    } {
    7281        keep -foreground -font -cursor
    73     }
    74     $itk_component(treeview) column insert 0 result -width 75
     82        keep -selectcommand
     83    }
     84    $itk_component(treeview) column insert 0 result -title "Result"
    7585    $itk_component(treeview) column insert end test -hide yes
     86    $itk_component(treeview) column configure treeView -justify left -title "Test Case"
    7687    $itk_component(scrollbars) contents $itk_component(treeview)
    7788
     
    7990        frame $itk_interior.bottomBar
    8091    }
    81     pack $itk_component(bottomBar) -fill x -side bottom
     92    pack $itk_component(bottomBar) -fill x -side bottom -pady {8 0}
     93
     94    itk_component add selLabel {
     95        label $itk_component(bottomBar).selLabel -anchor w -text "Select:"
     96    }
     97    pack $itk_component(selLabel) -side left
    8298
    8399    itk_component add bSelectAll {
    84         button $itk_component(bottomBar).bSelectAll -text "Select all" \
     100        button $itk_component(bottomBar).bSelectAll -text "All" \
    85101            -command "$itk_component(treeview) selection set 0 end"
    86102    }
     
    88104
    89105    itk_component add bSelectNone {
    90         button $itk_component(bottomBar).bSelectNone -text "Select none" \
     106        button $itk_component(bottomBar).bSelectNone -text "None" \
    91107            -command "$itk_component(treeview) selection clearall"
    92108    }
    93109    pack $itk_component(bSelectNone) -side left
    94110
    95     itk_component add bRun {
    96         button $itk_component(bottomBar).bRun -text "Run" -state disabled \
    97             -command [itcl::code $this runSelected]
    98     }
    99     pack $itk_component(bRun) -side right
    100 
    101     itk_component add lSelected {
    102         label $itk_component(bottomBar).lSelected -text "0 tests selected"
    103     }
    104     pack $itk_component(lSelected) -side right -padx 5
    105 
    106     # TODO: Fix black empty space when columns are shrunk
    107 
    108     pack $itk_component(scrollbars) -side left -expand yes -fill both
    109111
    110112    eval itk_initialize $args
    111 
    112     if {$itk_option(-testdir) == ""} {
    113         error "no -testdir configuration option given."
    114     }
    115     if {$itk_option(-toolxml) == ""} {
    116         error "no -toolxml configuration option given."
    117     }
    118 
    119113}
    120114
     
    123117# ----------------------------------------------------------------------
    124118itcl::body Rappture::Tester::TestTree::destructor {} {
    125     foreach id [getLeaves] {
    126         itcl::delete object [getTest $id]
    127     }
    128 }
    129 
    130 # ----------------------------------------------------------------------
    131 # CONFIGURATION OPTION: -testdir
    132 #
    133 # Location of the directory containing a set of test xml files.
    134 # Repopulate the tree if -testdir option is changed, but only if
    135 # -toolxml has already been defined.
    136 # ----------------------------------------------------------------------
    137 itcl::configbody Rappture::Tester::TestTree::testdir {
    138     if {[file isdirectory $itk_option(-testdir)]} {
    139         if {$itk_option(-toolxml) != ""} {
    140             populate
    141         }
    142     } else {
    143         error "Test directory \"$itk_option(-testdir)\" does not exist"
    144     }
    145 }
    146 
    147 # ----------------------------------------------------------------------
    148 # CONFIGURATION OPTION: -toolxml
    149 #
    150 # Location of the tool.xml for the tool being tested.  Repopulate the
    151 # tree if -toolxml is changed, but only if -testdir has already been
    152 # defined.
    153 # ----------------------------------------------------------------------
    154 itcl::configbody Rappture::Tester::TestTree::toolxml {
    155     if {[file exists $itk_option(-toolxml)]} {
    156         if {$itk_option(-testdir) != ""} {
    157             populate
    158         }
    159     } else {
    160         error "Tool \"$itk_option(-testdir)\" does not exist"
    161     }
    162 }
    163 
    164 # ----------------------------------------------------------------------
    165 # CONFIGURATION OPTION: -selectcommand
    166 #
    167 # Forward the TestTree's selectcommand to the treeview, but tack on the
    168 # updateLabel method to keep the label refreshed when selection is
    169 # changed
    170 # ----------------------------------------------------------------------
    171 itcl::configbody Rappture::Tester::TestTree::selectcommand {
    172     $itk_component(treeview) configure -selectcommand \
    173         "[itcl::code $this updateLabel]; $itk_option(-selectcommand)"
    174 }
    175 
    176 # ----------------------------------------------------------------------
    177 # USAGE getTest ?id?
     119    clear
     120}
     121
     122# ----------------------------------------------------------------------
     123# USAGE: add ?<testObj> <testObj> ...?
     124#
     125# Adds one or more Test objects to the tree shown in this viewer.
     126# Once added, these objects become property of this widget and
     127# are destroyed when the widget is cleared or deleted.
     128# ----------------------------------------------------------------------
     129itcl::body Rappture::Tester::TestTree::add {args} {
     130    set icon [Rappture::icon testcase]
     131
     132    foreach obj $args {
     133        if {[catch {$obj isa Rappture::Tester::Test} valid] || !$valid} {
     134            error "bad value \"$obj\": should be Test object"
     135        }
     136
     137        # add each Test object into the tree
     138        set testpath [$obj getTestInfo test.label]
     139        set n [$itk_component(treeview) insert end $testpath \
     140             -data [list test $obj] -icons [list $icon $icon]]
     141
     142        # tag this node so we can find it easily later
     143        $itk_component(treeview) tag add $obj $n
     144
     145        # monitor state changes on the object
     146        $obj configure -notifycommand [itcl::code $this _refresh]
     147    }
     148}
     149
     150# ----------------------------------------------------------------------
     151# USAGE: clear
     152#
     153# Clears the contents of the tree so that it's completely empty.
     154# All Test objects stored internally are destroyed.
     155# ----------------------------------------------------------------------
     156itcl::body Rappture::Tester::TestTree::clear {} {
     157    foreach id [_getLeaves] {
     158        itcl::delete object [_getTest $id]
     159    }
     160    $itk_component(treeview) delete 0
     161}
     162
     163# ----------------------------------------------------------------------
     164# USAGE: curselection
     165#
     166# Returns a list ids for all currently selected tests (leaf nodes) and
     167# the child tests of any currently selected branch nodes.  Tests can
     168# only be leaf nodes in the tree (the ids in the returned list will
     169# correspond to leaf nodes only).
     170# ----------------------------------------------------------------------
     171itcl::body Rappture::Tester::TestTree::curselection {} {
     172    set rlist ""
     173    foreach id [$itk_component(treeview) curselection] {
     174        foreach node [_getLeaves $id] {
     175            catch {unset data}
     176            array set data [$itk_component(treeview) entry cget $node -data]
     177
     178            if {[lsearch -exact $rlist $data(test)] < 0} {
     179                lappend rlist $data(test)
     180            }
     181        }
     182    }
     183    return $rlist
     184}
     185
     186# ----------------------------------------------------------------------
     187# USAGE _getTest <nodeId>
    178188#
    179189# Returns the test object associated with a given treeview node id.  If
     
    182192# branch node.
    183193# ----------------------------------------------------------------------
    184 itcl::body Rappture::Tester::TestTree::getTest {args} {
    185     if {[llength $args] == 0} {
    186          set id [$itk_component(treeview) index focus]
    187     } elseif {[llength $args] == 1} {
    188         set id [lindex $args 0]
    189     } else {
    190         error "wrong # args: should be getTest ?id?"
    191     }
    192     array set darray [getData $id]
    193     if {[lsearch -exact [getLeaves] $id] == -1} {
     194itcl::body Rappture::Tester::TestTree::_getTest {id} {
     195    if {[lsearch -exact [_getLeaves] $id] < 0} {
    194196        # Return empty string if branch node selected
    195197        return ""
    196198    }
     199    array set darray [$itk_component(treeview) entry cget $id -data]
    197200    return $darray(test)
    198201}
    199202
    200203# ----------------------------------------------------------------------
    201 # USAGE: refresh ?id?
    202 #
    203 # Refreshes the result column and any other information which may be
    204 # added later for the given tree node id.  Mainly needed to update the
    205 # result from Fail to Pass after regoldenizing a test.  If no id is
    206 # given, refresh all tests and search the test directory again to check
    207 # for new tests.
    208 # ----------------------------------------------------------------------
    209 itcl::body Rappture::Tester::TestTree::refresh {args} {
    210     if {[llength $args] == 0} {
    211         foreach id [getLeaves] {
    212             refresh $id
    213         }
    214         populate -noclear
    215     } elseif {[llength $args] == 1} {
    216         set id [lindex $args 0]
    217         if {[lsearch -exact [getLeaves] $id] == -1} {
    218             error "given id $id is not a leaf node."
    219         }
    220         set test [getTest $id]
    221         setData $id [list result [$test getResult] test $test]
    222     } else {
    223         error "wrong # args: should be refresh ?id?"
    224     }
    225 }
    226 
    227 # ----------------------------------------------------------------------
    228 # USAGE: getData <id>
    229 #
    230 # Returns a list of key-value pairs representing the column data stored
    231 # at the tree node with the given id.
    232 # ----------------------------------------------------------------------
    233 itcl::body Rappture::Tester::TestTree::getData {id} {
    234     return [$itk_component(treeview) entry cget $id -data]
    235 }
    236 
    237 # ----------------------------------------------------------------------
    238 # USAGE: getLeaves ?id?
     204# USAGE: _refresh ?<testObj> <testObj> ...?
     205#
     206# Invoked whenever the state of a <testObj> changes.  Finds the
     207# corresponding entry in the tree and updates the "Result" column
     208# to show the new status.
     209# ----------------------------------------------------------------------
     210itcl::body Rappture::Tester::TestTree::_refresh {args} {
     211    foreach obj $args {
     212        set n [$itk_component(treeview) index $obj]
     213        if {$n ne ""} {
     214            catch {unset data}
     215            array set data [$itk_component(treeview) entry cget $n -data]
     216
     217            # getting rid of a spinner? then drop it
     218            if {[info exists data(result)]
     219                  && $data(result) == "@$spinner(image)"} {
     220                spinner drop
     221            }
     222
     223            # plug in the new icon
     224            switch -- [$obj getResult] {
     225                Pass    { set data(result) "@[Rappture::icon pass16]" }
     226                Fail    { set data(result) "@[Rappture::icon fail16]" }
     227                Waiting { set data(result) "@[Rappture::icon wait]" }
     228                Running { set data(result) "@[spinner use]" }
     229                default { set data(result) "" }
     230            }
     231puts "ICON: $data(result)"
     232            $itk_component(treeview) entry configure $n -data [array get data]
     233        }
     234    }
     235}
     236
     237# ----------------------------------------------------------------------
     238# USAGE: _getLeaves ?id?
    239239#
    240240# Returns a list of ids for all tests contained in the tree.  If an
     
    243243# id.  Tests can only be leaf nodes.
    244244# ----------------------------------------------------------------------
    245 itcl::body Rappture::Tester::TestTree::getLeaves {{id 0}} {
     245itcl::body Rappture::Tester::TestTree::_getLeaves {{id 0}} {
    246246    set clist [$itk_component(treeview) entry children $id]
    247247    if {$clist == "" && $id == 0} {
     
    254254    set tests [list]
    255255    foreach child $clist {
    256         set tests [concat $tests [getLeaves $child]]
     256        set tests [concat $tests [_getLeaves $child]]
    257257    }
    258258    return $tests
     
    260260
    261261# ----------------------------------------------------------------------
    262 # USAGE: getSelected
    263 #
    264 # Returns a list ids for all currently selected tests (leaf nodes) and
    265 # the child tests of any currently selected branch nodes.  Tests can
    266 # only be leaf nodes in the tree (the ids in the returned list will
    267 # correspond to leaf nodes only).
    268 # ----------------------------------------------------------------------
    269 itcl::body Rappture::Tester::TestTree::getSelected {} {
    270     set selection [$itk_component(treeview) curselection]
    271     set selectedTests [list]
    272     foreach id $selection {
    273         foreach node [getLeaves $id] {
    274             if {[lsearch -exact $selectedTests $node] == -1} {
    275                 lappend selectedTests $node
    276             }
    277         }
    278     }
    279     return $selectedTests
    280 }
    281 
    282 # ----------------------------------------------------------------------
    283 # USAGE: populate ?-noclear?
    284 #
    285 # Used internally to insert nodes into the treeview for each test xml
    286 # found in the test directory.  Skips any xml files that do not contain
    287 # information at path test.label.  Relies on the autocreate treeview
    288 # option so that branch nodes need not be explicitly created.  Deletes
    289 # any existing contents unless -noclear is given as an argument.
    290 # ----------------------------------------------------------------------
    291 itcl::body Rappture::Tester::TestTree::populate {args} {
    292     if {[lsearch $args -noclear] == -1} {
    293         foreach id [getLeaves] {
    294             itcl::delete object [getTest $id]
    295         }
    296         $itk_component(treeview) delete 0
    297         $itk_component(treeview) selection clearall
    298     }
    299     # TODO: add an appropriate icon
    300     set icon [Rappture::icon molvis-3dorth]
    301     # TODO: Descend through subdirectories inside testdir?
    302     foreach testxml [glob -nocomplain -directory $itk_option(-testdir) *.xml] {
    303         set lib [Rappture::library $testxml]
    304         set testpath [$lib get test.label]
    305         if {$testpath != "" && \
    306             [$itk_component(treeview) find -full $testpath] == ""} {
    307             set test [Rappture::Tester::Test ::#auto \
    308                 $itk_option(-toolxml) $testxml]
    309             $itk_component(treeview) insert end $testpath -data \
    310                  [list test $test] -icons "$icon $icon" \
    311                  -activeicons "$icon $icon"
    312         }
    313     }
    314     $itk_component(treeview) open -recurse root
    315     # TODO: Fix width of main treeview column
    316     updateLabel
    317 }
    318 
    319 # ----------------------------------------------------------------------
    320 # USAGE: runSelected
    321 #
    322 # Invoked by the run button to run all currently selected tests.
    323 # After completion, call selectcommand to re-select the newly completed
    324 # focused node.
    325 # ----------------------------------------------------------------------
    326 itcl::body Rappture::Tester::TestTree::runSelected {} {
    327     foreach id [$this getSelected] {
    328         runTest $id
    329     }
    330     # Try calling selectcommand with the -refresh option.  If selectcommand
    331     # does not accept this argument, then call it with no arguments.
    332     if {[catch {eval $itk_option(-selectcommand) -refresh}]} {
    333         eval $itk_option(-selectcommand)
    334     }
    335 }
    336 
    337 # ----------------------------------------------------------------------
    338 # USAGE: runTest id
    339 #
    340 # Runs the test located at the tree node with the given id.  The id
    341 # must be a leaf node, because tests may not be located at branch nodes.
    342 # ----------------------------------------------------------------------
    343 itcl::body Rappture::Tester::TestTree::runTest {id} {
    344     if {[lsearch -exact [getLeaves] $id] == -1} {
    345         error "given id $id is not a leaf node"
    346     }
    347     set test [getTest $id]
    348     setData $id [list result Running test $test]
    349     $test run
    350     setData $id [list result [$test getResult] test $test]
    351 }
    352 
    353 # ----------------------------------------------------------------------
    354 # USAGE: setData <id> <data>
    355 #
    356 # Accepts a node id and a list of key-value pairs.  Stored the list as
    357 # column data associated with the tree node with the given id.
    358 # ----------------------------------------------------------------------
    359 itcl::body Rappture::Tester::TestTree::setData {id data} {
    360     $itk_component(treeview) entry configure $id -data $data
    361 }
    362 
    363 # ----------------------------------------------------------------------
    364 # USAGE: updateLabel
    365 #
    366 # Used internally to update the label which indicates how many tests
    367 # are currently selected.  Also disables the run button if no tests are
    368 # selected.
    369 # ----------------------------------------------------------------------
    370 itcl::body Rappture::Tester::TestTree::updateLabel {} {
    371     set n [llength [getSelected]]
    372     if {$n == 1} {
    373         $itk_component(lSelected) configure -text "1 test selected"
    374     } else {
    375         $itk_component(lSelected) configure -text "$n tests selected"
    376     }
    377 
    378     if {$n > 0} {
    379         $itk_component(bRun) configure -state normal
    380     } else {
    381         $itk_component(bRun) configure -state disabled
    382     }
    383 }
    384 
     262# USAGE: spinner use|drop|next
     263#
     264# Used to update the spinner icon that represents running test cases.
     265# The "use" option returns the spinner icon and starts the animation,
     266# if it isn't already running.  The "drop" operation lets go of the
     267# spinner.  If nobody is using it, the animation stops.  The "next"
     268# option is used internally to change the animation to the next frame.
     269# ----------------------------------------------------------------------
     270itcl::body Rappture::Tester::TestTree::spinner {op} {
     271    switch -- $op {
     272        use {
     273            if {$spinner(pending) == ""} {
     274                set spinner(current) 0
     275                set spinner(pending) [after 100 Rappture::Tester::TestTree::spinner next]
     276            }
     277            incr spinner(uses)
     278            return $spinner(image)
     279        }
     280        drop {
     281            if {[incr spinner(uses) -1] <= 0} {
     282                after cancel $spinner(pending)
     283                set spinner(pending) ""
     284                set spinner(uses) 0
     285            }
     286        }
     287        next {
     288            set n $spinner(current)
     289            $spinner(image) copy $spinner(frame$n)
     290
     291            # go to the next frame
     292            if {[incr spinner(current)] >= $spinner(frames)} {
     293                set spinner(current) 0
     294            }
     295
     296            # update again after a short delay
     297            set spinner(pending) [after 100 Rappture::Tester::TestTree::spinner next]
     298        }
     299        default {
     300            error "bad option \"$op\": should be use, drop, next"
     301        }
     302    }
     303}
Note: See TracChangeset for help on using the changeset viewer.