|
31 | 31 |
|
32 | 32 | var widgets = {} |
33 | 33 |
|
34 | | - function eligibleForWidget(node){ |
35 | | - if(!node) return false |
36 | | - |
37 | | - return node.kind == 'package' || |
38 | | - node.kind == 'group' || |
39 | | - node.kind == 'root' |
40 | | - } |
| 34 | + // build this into a Map[node.id -> packageNode] |
| 35 | + // Note: the package tree doesn't include all nodes; instead, it includes |
| 36 | + // an 'otherDescendantIds' array, to represent all of the 'direct' descendants |
| 37 | + // of each package|group. |
| 38 | + var nodePackageParents = {} |
41 | 39 |
|
42 | 40 | // Get the partial selection state for the trace instrumentation flag, for nodes that |
43 | 41 | // would exist in the package list. Note that <self> nodes will take their original |
44 | 42 | // node's `id` and `traced` properties, and the original's `traced` will be set to |
45 | 43 | // undefined. This function sets a `partialTraceSelection` value on nodes recursively, |
46 | 44 | // with `1 = fully selected`, `0 = unselected`, and `undefined = partially selected`. |
47 | | - (function calculatePartialTraceSelections(node){ |
| 45 | + ;(function calculatePartialTraceSelections(node){ |
48 | 46 | var isFullSelected = true, |
49 | 47 | isPartialSelected = false |
50 | 48 |
|
51 | 49 | if(node.traced == 0) isFullSelected = false |
52 | 50 |
|
53 | 51 | node.children.forEach(function(kid){ |
54 | | - if(eligibleForWidget(kid)){ |
55 | | - var kidSelection = calculatePartialTraceSelections(kid) |
56 | | - if(!kidSelection) isFullSelected = false |
57 | | - if(kidSelection || kidSelection == undefined) isPartialSelected = true |
58 | | - } |
| 52 | + var kidSelection = calculatePartialTraceSelections(kid) |
| 53 | + if(!kidSelection) isFullSelected = false |
| 54 | + if(kidSelection || kidSelection == undefined) isPartialSelected = true |
59 | 55 | }) |
60 | 56 |
|
61 | 57 | var selectionValue = isFullSelected ? 1 : isPartialSelected ? undefined : 0 |
|
66 | 62 | // special case: the root is never selected |
67 | 63 | treeData.root.partialTraceSelection = 0 |
68 | 64 |
|
| 65 | + // walk the tree in order to calculate the `nodePackageParents` map. |
| 66 | + ;(function calculateNodePackageParents(node, parent){ |
| 67 | + if(parent) nodePackageParents[node.id] = parent |
| 68 | + |
| 69 | + node.children.forEach(function(child){ |
| 70 | + calculateNodePackageParents(child, node) |
| 71 | + }) |
| 72 | + |
| 73 | + if(node.otherDescendantIds) node.otherDescendantIds.forEach(function(childId){ |
| 74 | + nodePackageParents[childId] = node |
| 75 | + }) |
| 76 | + |
| 77 | + })(treeData.root, undefined) |
| 78 | + |
69 | 79 | // initialize the `stateTemplate` and `widgets` maps |
70 | 80 | // based on the package nodes in `treeData` |
71 | 81 | ;(function setupTreeHierarchy(packageParentNode, node){ |
72 | | - if(eligibleForWidget(node)){ |
73 | 82 |
|
74 | | - var pw = new PackageWidget() |
75 | | - widgets[node.id] = pw |
| 83 | + var pw = new PackageWidget() |
| 84 | + widgets[node.id] = pw |
76 | 85 |
|
77 | | - pw.instrumentationSelected(node.partialTraceSelection) |
| 86 | + pw.instrumentationSelected(node.partialTraceSelection) |
78 | 87 |
|
79 | | - pw.collapseChildren(/* collapsed = */true, /* animate = */false) |
| 88 | + pw.collapseChildren(/* collapsed = */true, /* animate = */false) |
80 | 89 |
|
81 | | - stateTemplate[node.id] = pw.selectedProp |
82 | | - pw.selectionClicks.onValue(function(){ |
83 | | - handleSelectionClick(node, pw) |
84 | | - }) |
| 90 | + stateTemplate[node.id] = pw.selectedProp |
| 91 | + pw.selectionClicks.onValue(function(){ |
| 92 | + handleSelectionClick(node, pw) |
| 93 | + }) |
85 | 94 |
|
86 | | - instrumentedTemplate[node.id] = pw.instrumentationSelectedProp |
87 | | - pw.instrumentationSelectedClicks.onValue(function(){ |
88 | | - handleInstrumentationSelectionClick(node, pw) |
89 | | - }) |
| 95 | + instrumentedTemplate[node.id] = pw.instrumentationSelectedProp |
| 96 | + pw.instrumentationSelectedClicks.onValue(function(){ |
| 97 | + handleInstrumentationSelectionClick(node, pw) |
| 98 | + }) |
90 | 99 |
|
91 | | - pw.uiParts.collapser.click(function(event){ |
92 | | - pw.collapseChildren('toggle', true) |
93 | | - event.stopPropagation() |
94 | | - }) |
| 100 | + pw.uiParts.collapser.click(function(event){ |
| 101 | + pw.collapseChildren('toggle', true) |
| 102 | + event.stopPropagation() |
| 103 | + }) |
95 | 104 |
|
96 | | - if(node.kind == 'root'){ |
97 | | - pw.uiParts.main.appendTo($totalsContainer) |
98 | | - pw.abbreviatedLabel('Overall Coverage') |
99 | | - pw.selectable(false) |
100 | | - pw.instrumentationSelectable(false) |
101 | | - } |
102 | 105 |
|
103 | | - if(node.kind == 'group' || node.kind == 'package'){ |
104 | | - if(packageParentNode){ |
105 | | - widgets[packageParentNode.id].children.add(pw) |
106 | | - } else { |
107 | | - pw.uiParts.main.appendTo($container) |
108 | | - } |
| 106 | + if(node.kind == 'root'){ |
| 107 | + pw.uiParts.main.appendTo($totalsContainer) |
| 108 | + pw.abbreviatedLabel('Overall Coverage') |
| 109 | + pw.selectable(false) |
| 110 | + pw.instrumentationSelectable(false) |
| 111 | + |
| 112 | + var totalMethodCount = 0 |
| 113 | + node.children.forEach(function(child){ |
| 114 | + totalMethodCount += child.methodCount |
| 115 | + }) |
| 116 | + pw.methodCount(totalMethodCount) |
| 117 | + } else { |
| 118 | + |
| 119 | + pw.methodCount(node.methodCount) |
109 | 120 |
|
110 | | - if(node.isSelfNode){ |
111 | | - pw.fullLabel(node.name).abbreviatedLabel(node.name) |
112 | | - } else { |
113 | | - var abbrevName |
| 121 | + if(packageParentNode){ |
| 122 | + widgets[packageParentNode.id].children.add(pw) |
| 123 | + } else { |
| 124 | + pw.uiParts.main.appendTo($container) |
| 125 | + } |
114 | 126 |
|
115 | | - if (node.kind != 'group' && packageParentNode && packageParentNode.kind == 'group') |
116 | | - abbrevName = node.name |
117 | | - else { |
118 | | - var parentName = packageParentNode ? packageParentNode.name : '', |
119 | | - abbrevName = node.name.substr(parentName.length) |
120 | | - } |
| 127 | + if(node.isSelfNode){ |
| 128 | + pw.fullLabel(node.label).abbreviatedLabel(node.label) |
| 129 | + } else { |
| 130 | + var abbrevName |
121 | 131 |
|
122 | | - pw.fullLabel(node.name).abbreviatedLabel(abbrevName) |
| 132 | + if (node.kind != 'group' && packageParentNode && packageParentNode.kind == 'group') |
| 133 | + abbrevName = node.label |
| 134 | + else { |
| 135 | + var parentName = packageParentNode ? packageParentNode.label : '', |
| 136 | + abbrevName = node.label.substr(parentName.length) |
123 | 137 | } |
| 138 | + |
| 139 | + pw.fullLabel(node.label).abbreviatedLabel(abbrevName) |
124 | 140 | } |
125 | 141 | } |
126 | 142 |
|
127 | | - ;(node.children || []) |
| 143 | + node.children |
128 | 144 | .sort(function(a,b){ |
129 | | - // alphabetic sort by node.name |
130 | | - var an = a.name.toUpperCase(), |
131 | | - bn = b.name.toUpperCase() |
| 145 | + // alphabetic sort by node.label |
| 146 | + var an = a.label.toUpperCase(), |
| 147 | + bn = b.label.toUpperCase() |
132 | 148 |
|
133 | 149 | if(an < bn) return -1 |
134 | 150 | if(an > bn) return 1 |
|
141 | 157 |
|
142 | 158 | })(undefined, treeData.root) |
143 | 159 |
|
144 | | - function hasPackageChild(node){ |
145 | | - var found = false |
146 | | - ;(node.children || []).forEach(function(child){ |
147 | | - if(child.kind == 'package') found = true |
148 | | - }) |
149 | | - return found |
150 | | - } |
151 | | - |
152 | 160 | // Disable all of the widgets while the trace is running |
153 | 161 | Trace.running.onValue(function(isRunning){ |
154 | 162 | for(var id in widgets){ |
155 | 163 | var pw = widgets[id], |
156 | 164 | node = treeData.getNode(id) |
| 165 | + |
157 | 166 | if(node.kind != 'root'){ |
158 | 167 | pw.instrumentationSelectable(!isRunning) |
159 | 168 | } |
|
219 | 228 | /*set*/ function(w,s){ return w.instrumentationSelected(s) } |
220 | 229 | ) |
221 | 230 |
|
222 | | - // set the `methodCount` property for all widgets |
223 | | - applyMethodCounts(treeData, widgets) |
224 | | - |
225 | 231 | /** |
226 | 232 | * Exposes the selection state of each of the widgets, as a |
227 | 233 | * Map[package.id -> isSelected] |
|
270 | 276 | } |
271 | 277 |
|
272 | 278 | this.highlightPackages = function(methodIds){ |
273 | | - highlightPackages(treeData, widgets, methodIds) |
| 279 | + highlightPackages(treeData, widgets, methodIds, nodePackageParents) |
274 | 280 | } |
275 | 281 |
|
276 | 282 | this.applyMethodCoverage = function(coverageRecords, activeRecordings){ |
277 | | - applyMethodCoverage(treeData, widgets, coverageRecords, activeRecordings) |
278 | | - } |
279 | | - } |
280 | | - |
281 | | - // Returns an array of package-kind Tree Nodes. |
282 | | - function findPackages(treeData){ |
283 | | - var a = [] |
284 | | - a.push(treeData.root) |
285 | | - treeData.forEachNode(function(node){ |
286 | | - if(node.kind == 'package'){ |
287 | | - a.push(node) |
288 | | - } |
289 | | - }) |
290 | | - return a |
291 | | - } |
292 | | - |
293 | | - // Compute and set the `methodCount` property for all of the widgets |
294 | | - function applyMethodCounts(treeData, widgets){ |
295 | | - function recurse(node){ |
296 | | - var count = 0 |
297 | | - |
298 | | - if(node.kind == 'method') ++count |
299 | | - |
300 | | - ;(node.children || []).forEach(function(kid){ |
301 | | - count += recurse(kid) |
302 | | - }) |
303 | | - |
304 | | - if(widgets[node.id]) widgets[node.id].methodCount(count) |
305 | | - |
306 | | - return count |
307 | | - |
308 | | - /* Alternate Strategy: |
309 | | - * Only count methods that fall directly beneath each package; |
310 | | - * This means that 'com.foo' would not include methods defined in |
311 | | - * 'com.foo.bar'. To do this, uncomment the following, and comment |
312 | | - * out the above return statement. |
313 | | - */ |
314 | | - /* |
315 | | - if(node.kind == 'package') return 0 |
316 | | - else return count |
317 | | - */ |
| 283 | + applyMethodCoverage(treeData, widgets, coverageRecords, activeRecordings, nodePackageParents) |
318 | 284 | } |
319 | | - recurse(treeData.root) |
320 | 285 | } |
321 | 286 |
|
322 | 287 | // Trigger a `flashHighlight` on the appropriate package widgets |
323 | 288 | // |
324 | 289 | // @param treeData - the tree structure being managed |
325 | 290 | // @param widgets - a Map[packageNode.id -> PackageWidget] |
326 | 291 | // @param methodIds - an Array[method.id] |
327 | | - function highlightPackages(treeData, widgets, methodIds){ |
| 292 | + function highlightPackages(treeData, widgets, methodIds, nodePackageParents){ |
328 | 293 | var toHighlight = {} |
329 | 294 |
|
330 | 295 |
|
|
343 | 308 | } |
344 | 309 |
|
345 | 310 | methodIds.forEach(function(id){ |
346 | | - bubbleUp(treeData.getNode(id)) |
| 311 | + var bubbleFrom = treeData.getNode(id) || nodePackageParents[id] |
| 312 | + // bubbleUp(treeData.getNode(id)) |
| 313 | + bubbleUp(bubbleFrom) |
347 | 314 | }) |
348 | 315 |
|
349 | 316 | for(var id in toHighlight){ |
|
365 | 332 | * @param activeRecordings - A set of recording IDs. Coverage only |
366 | 333 | * counts if the recording that covered a method was selected. |
367 | 334 | */ |
368 | | - function applyMethodCoverage(treeData, widgets, coverageRecords, activeRecordings){ |
| 335 | + function applyMethodCoverage(treeData, widgets, coverageRecords, activeRecordings, nodePackageParents){ |
369 | 336 |
|
370 | 337 | // If anything is selected, use that selection to determine coverage. |
371 | 338 | // If nothing is selected, all coverage data counts (i.e. 'all activity' is what's covered) |
372 | 339 | var noSelectedRecordings = activeRecordings.empty() |
373 | 340 |
|
374 | | - function isCoveredBySelectedRecording(node){ |
375 | | - var records = coverageRecords[node.id] |
| 341 | + function isCoveredBySelectedRecording(nodeId){ |
| 342 | + var records = coverageRecords[nodeId] |
376 | 343 | if(!records) return false |
377 | 344 |
|
378 | 345 | if(noSelectedRecordings){ |
|
393 | 360 | // if(coverageRecords[node.id] && coverageRecords[node.id].length){ |
394 | 361 | // coverage += 1 |
395 | 362 | // } |
396 | | - coverage += isCoveredBySelectedRecording(node) |
| 363 | + coverage += isCoveredBySelectedRecording(node.id) |
| 364 | + |
| 365 | + if(node.otherDescendantIds) node.otherDescendantIds.forEach(function(id){ |
| 366 | + coverage += isCoveredBySelectedRecording(id) |
| 367 | + }) |
397 | 368 |
|
398 | 369 | // also include the sum of the childrens' coverages (recursive) |
399 | 370 | ;(node.children || []).forEach(function(kid){ |
|
0 commit comments