Skip to content

Commit 46f54e5

Browse files
committed
Merge pull request #4 from secdec/feature/instrumentation-filter
Feature/instrumentation filter
2 parents 1398447 + a27de5c commit 46f54e5

25 files changed

+496
-130
lines changed

codepulse/src/main/resources/toserve/pages/traces/PackageController.js

Lines changed: 129 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,68 @@
2626
// build this into a Map[packageId -> packageWidget.selectedProp]
2727
var stateTemplate = {}
2828

29+
// build this into a Map[packageId -> packageWidget.instrumentationSelectedProp]
30+
var instrumentedTemplate = {}
31+
2932
var widgets = {}
3033

34+
function eligibleForWidget(node){
35+
if(!node) return false
36+
37+
return node.kind == 'package' ||
38+
node.kind == 'group' ||
39+
node.kind == 'root'
40+
}
41+
42+
// Get the partial selection state for the trace instrumentation flag, for nodes that
43+
// would exist in the package list. Note that <self> nodes will take their original
44+
// node's `id` and `traced` properties, and the original's `traced` will be set to
45+
// undefined. This function sets a `partialTraceSelection` value on nodes recursively,
46+
// with `1 = fully selected`, `0 = unselected`, and `undefined = partially selected`.
47+
(function calculatePartialTraceSelections(node){
48+
var isFullSelected = true,
49+
isPartialSelected = false
50+
51+
if(node.traced == 0) isFullSelected = false
52+
53+
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+
}
59+
})
60+
61+
var selectionValue = isFullSelected ? 1 : isPartialSelected ? undefined : 0
62+
node.partialTraceSelection = selectionValue
63+
return selectionValue
64+
})(treeData.root)
65+
66+
// special case: the root is never selected
67+
treeData.root.partialTraceSelection = 0
68+
3169
// initialize the `stateTemplate` and `widgets` maps
3270
// based on the package nodes in `treeData`
3371
;(function setupTreeHierarchy(packageParentNode, node){
34-
if(node.kind == 'package' || node.kind == 'root'){
72+
if(eligibleForWidget(node)){
3573

3674
var pw = new PackageWidget()
3775
widgets[node.id] = pw
3876

77+
pw.instrumentationSelected(node.partialTraceSelection)
78+
3979
pw.collapseChildren(/* collapsed = */true, /* animate = */false)
4080

4181
stateTemplate[node.id] = pw.selectedProp
4282
pw.selectionClicks.onValue(function(){
4383
handleSelectionClick(node, pw)
4484
})
4585

86+
instrumentedTemplate[node.id] = pw.instrumentationSelectedProp
87+
pw.instrumentationSelectedClicks.onValue(function(){
88+
handleInstrumentationSelectionClick(node, pw)
89+
})
90+
4691
pw.uiParts.collapser.click(function(event){
4792
pw.collapseChildren('toggle', true)
4893
event.stopPropagation()
@@ -52,9 +97,10 @@
5297
pw.uiParts.main.appendTo($totalsContainer)
5398
pw.abbreviatedLabel('Overall Coverage')
5499
pw.selectable(false)
100+
pw.instrumentationSelectable(false)
55101
}
56102

57-
if(node.kind == 'package'){
103+
if(node.kind == 'group' || node.kind == 'package'){
58104
if(packageParentNode){
59105
widgets[packageParentNode.id].children.add(pw)
60106
} else {
@@ -64,16 +110,22 @@
64110
if(node.isSelfNode){
65111
pw.fullLabel(node.name).abbreviatedLabel(node.name)
66112
} else {
67-
var parentName = packageParentNode ? packageParentNode.name : '',
68-
abbrevName = node.name.substr(parentName.length)
113+
var abbrevName
114+
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+
}
69121

70122
pw.fullLabel(node.name).abbreviatedLabel(abbrevName)
71123
}
72124
}
73125
}
74126

75127
;(node.children || []).forEach(function(kid){
76-
var nextParent = (node.kind == 'package')? node : packageParentNode
128+
var nextParent = (node.kind == 'group' || node.kind == 'package')? node : packageParentNode
77129
setupTreeHierarchy(nextParent, kid)
78130
})
79131

@@ -87,40 +139,75 @@
87139
return found
88140
}
89141

90-
function handleSelectionClick(node, widget){
91-
// selected may be one of [1, 0, undefined].
92-
// transition [1 -> 0], [0 -> 1], [undefined -> 1]
93-
var newSel = +!widget.selected()
94-
95-
// apply the new selection to this node and each of its children
96-
;(function bubbleDown(w){
97-
w.selected(newSel)
98-
w.children.forEach(bubbleDown)
99-
})(widget)
100-
101-
// climb the tree, re-checking the full/partial selection state of node's ancestors
102-
;(function bubbleUp(n){
103-
if(!n || n.kind == 'root') return
104-
var w = widgets[n.id]
105-
if(!w) return
106-
107-
var isFullSelected = true,
108-
isPartialSelected = false
109-
110-
w.children.forEach(function(c){
111-
var s = c.selected()
112-
if(!s) isFullSelected = false
113-
if(s == 1 || s == undefined) isPartialSelected = true
114-
})
142+
// Disable all of the widgets while the trace is running
143+
Trace.running.onValue(function(isRunning){
144+
for(var id in widgets){
145+
var pw = widgets[id],
146+
node = treeData.getNode(id)
147+
if(node.kind != 'root'){
148+
pw.instrumentationSelectable(!isRunning)
149+
}
150+
}
151+
})
115152

116-
var s = isFullSelected ? 1 : isPartialSelected ? undefined : 0
117-
w.selected(s)
153+
// checkSelected = function(widget){ return <is widget selected> }
154+
// setSelected = function(widget, sel){ <set widget.selected to sel> }
155+
// Returns a function(node, widget):
156+
// node is the starting point in the data tree
157+
// widget is the corresponding widget for the node
158+
//
159+
// The function applies partial selection logic as if the widget
160+
// had been clicked.
161+
function bubblePartialSelection(checkSelected, setSelected){
162+
return function(node, widget){
163+
// selected may be one of [1, 0, undefined].
164+
// transition [1 -> 0], [0 -> 1], [undefined -> 1]
165+
var newSel = +!checkSelected(widget)
166+
167+
// apply the new selection to this node and each of its children
168+
;(function bubbleDown(w){
169+
setSelected(w, newSel)
170+
w.children.forEach(bubbleDown)
171+
})(widget)
172+
173+
// climb the tree, re-checking the full/partial selection state of node's ancestors
174+
;(function bubbleUp(n){
175+
if(!n || n.kind == 'root') return
176+
var w = widgets[n.id]
177+
if(!w) return
178+
179+
var isFullSelected = true,
180+
isPartialSelected = false
181+
182+
w.children.forEach(function(c){
183+
var s = checkSelected(c)
184+
if(!s) isFullSelected = false
185+
if(s == 1 || s == undefined) isPartialSelected = true
186+
})
187+
188+
var s = isFullSelected ? 1 : isPartialSelected ? undefined : 0
189+
setSelected(w, s)
190+
191+
bubbleUp(n.parent)
192+
193+
})(node.parent)
194+
var p = node.parent, pw = p && widgets[p.id]
195+
}
196+
}
118197

119-
bubbleUp(n.parent)
198+
// a function(node,widget) that toggles the tri-state 'selected' property,
199+
// starting from the given node+widget
200+
var handleSelectionClick = bubblePartialSelection(
201+
/*get*/ function(w){ return w.selected() },
202+
/*set*/ function(w,s){ return w.selected(s) }
203+
)
120204

121-
})(node.parent)
122-
var p = node.parent, pw = p && widgets[p.id]
123-
}
205+
// a function(node, widget) that toggles the tri-state 'instrumentationSelected'
206+
// property, starting from the given node+widget
207+
var handleInstrumentationSelectionClick = bubblePartialSelection(
208+
/*get*/ function(w){ return w.instrumentationSelected() },
209+
/*set*/ function(w,s){ return w.instrumentationSelected(s) }
210+
)
124211

125212
// set the `methodCount` property for all widgets
126213
applyMethodCounts(treeData, widgets)
@@ -131,6 +218,12 @@
131218
*/
132219
this.selectedWidgets = Bacon.combineTemplate(stateTemplate).debounce(10)
133220

221+
/**
222+
* Exposes the 'instrumented' state of each of the widgets, as
223+
* a Map[package.id -> isInstrumented]
224+
*/
225+
this.instrumentedWidgets = Bacon.combineTemplate(instrumentedTemplate).debounce(10)
226+
134227
// Decide whether or not to show the "clear all selections" button
135228
// depending on whether or not there are selected widgets
136229
this.selectedWidgets

codepulse/src/main/resources/toserve/pages/traces/PackageWidget.css

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
}
3535

3636
.packages-header .header-label,
37+
.packages-header .header-trace,
3738
.packages-header .header-method-count,
3839
.packages-header .header-barchart {
3940

@@ -48,18 +49,26 @@
4849
line-height: 40px;
4950
background-color: #7E7E7E;
5051
}
52+
53+
.packages-header .header-trace {
54+
width: 40px;
55+
text-align: center;
56+
font-size: small;
57+
line-height: 40px;
58+
background-color: #9C9C9C;
59+
}
5160

5261
.packages-header .header-method-count {
5362
width: 50px;
5463
text-align: center;
5564
font-size: small;
56-
background-color: #9C9C9C;
65+
background-color: #7E7E7E;
5766
}
5867

5968
.packages-header .header-barchart {
6069
background-color: #6D6D6D;
6170

62-
width: calc(100% - 250px);
71+
width: calc(100% - 290px);
6372
line-height: 40px;
6473
padding-left: 5px;
6574
}
@@ -211,10 +220,66 @@
211220
content: '\f0c8'; /* fa-minus-square */
212221
}
213222

223+
/*******************************\
224+
| Widget Tracing Toggler Styles |
225+
\*******************************/
226+
227+
.widget-trace-toggle {
228+
width: 40px;
229+
float: left;
230+
text-align: center;
231+
background-color: #9C9C9C;
232+
}
233+
234+
.tri-state-toggle {
235+
width: 20px;
236+
margin: auto;
237+
cursor: pointer;
238+
}
239+
240+
.tri-state-toggle.disabled {
241+
cursor: initial;
242+
}
243+
244+
.tri-state-toggle:before {
245+
font-family: FontAwesome;
246+
-webkit-font-smoothing: antialiased;
247+
248+
width: 20px;
249+
text-align: center;
250+
font-size: 13px;
251+
252+
content: '\f096'; /* fa-square-o */
253+
color: #FFF;
254+
}
255+
256+
.tri-state-toggle.full-select:before {
257+
content: '\f00c'; /* fa-check */
258+
}
259+
260+
.tri-state-toggle.partial-select:before {
261+
content: '\f0c8'; /* fa-minus-square */
262+
}
263+
214264
/*****************************\
215265
| Widget Method Counts Styles |
216266
\*****************************/
217267

268+
.widget-method-count {
269+
background-color: #7E7E7E;
270+
width: 50px;
271+
display: inline-block;
272+
float: left;
273+
height: 20px;
274+
text-align: center;
275+
box-sizing: border-box;
276+
color: white;
277+
}
278+
279+
.widget-method-count .count-number {
280+
font-size: small;
281+
}
282+
218283
/* Some parts of the .widget-label will be highlighted, by showing a
219284
* .highlight div behind the regular content.
220285
*/
@@ -249,33 +314,12 @@
249314
opacity: 0; /* opacity should be set and transitioned with javascript */
250315
}
251316

252-
253-
/*****************************\
254-
| Widget Method Counts Styles |
255-
\*****************************/
256-
257-
.widget-method-count {
258-
background-color: #9C9C9C;
259-
width: 50px;
260-
display: inline-block;
261-
float: left;
262-
height: 20px;
263-
text-align: center;
264-
box-sizing: border-box;
265-
color: white;
266-
}
267-
268-
.widget-method-count .count-number {
269-
font-size: small;
270-
}
271-
272-
273317
/************************\
274318
| Widget Barchart Styles |
275319
\************************/
276320

277321
.widget-barchart {
278-
width: calc(100% - 250px);
322+
width: calc(100% - 290px);
279323
display: inline-block;
280324
height: 20px;
281325
}
@@ -357,6 +401,9 @@
357401
.package-totals .widget-label .content .label-text {
358402
line-height: 30px;
359403
}
404+
.package-totals .widget-trace-toggle {
405+
line-height: 30px;
406+
}
360407
.package-totals .widget-method-count {
361408
height: 30px;
362409
line-height: 30px;

0 commit comments

Comments
 (0)