1<!DOCTYPE html> 2<!-- 3Copyright (c) 2012 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7 8<link rel="import" href="/tracing/base/iteration_helpers.html"> 9<link rel="import" href="/tracing/importer/import.html"> 10<link rel="import" href="/tracing/model/counter.html"> 11<link rel="import" href="/tracing/model/model.html"> 12<link rel="import" href="/tracing/model/slice.html"> 13<link rel="import" href="/tracing/model/slice_group.html"> 14<link rel="import" href="/tracing/model/stack_frame.html"> 15<link rel="import" href="/tracing/model/thread_time_slice.html"> 16<link rel="import" href="/tracing/model/user_model/stub_expectation.html"> 17 18<script> 19'use strict'; 20 21/** 22 * @fileoverview Helper functions for use in tracing tests. 23 */ 24tr.exportTo('tr.c', function() { 25 var ColorScheme = tr.b.ColorScheme; 26 27 function _getStartAndCpuDurationFromDict( 28 options, required, startFieldName, durationFieldName, endFieldName) { 29 30 if (options[startFieldName] === undefined) { 31 if (required) 32 throw new Error('Too little information.'); 33 else 34 return {start: undefined, duration: undefined}; 35 } 36 if (options[durationFieldName] !== undefined && 37 options[endFieldName] !== undefined) { 38 throw new Error('Too much information.'); 39 } 40 if (options[durationFieldName] === undefined && 41 options[endFieldName] === undefined) { 42 if (required) 43 throw new Error('Too little information.'); 44 else 45 return {start: undefined, duration: undefined}; 46 } 47 48 var duration; 49 if (options[durationFieldName] !== undefined) { 50 duration = options[durationFieldName]; 51 } else { 52 duration = options[endFieldName] - options[startFieldName]; 53 } 54 55 return { 56 start: options[startFieldName], 57 duration: duration 58 }; 59 } 60 61 function _maybeGetCpuStartAndCpuDurationFromDict(options) { 62 return _getStartAndCpuDurationFromDict( 63 options, false, 'cpuStart', 'cpuDuration', 'cpuEnd'); 64 } 65 66 function TestUtils() { 67 } 68 69 TestUtils.getStartAndDurationFromDict = function(options) { 70 return _getStartAndCpuDurationFromDict( 71 options, true, 'start', 'duration', 'end'); 72 }; 73 74 TestUtils.newAsyncSlice = function(start, duration, startThread, endThread) { 75 return TestUtils.newAsyncSliceNamed( 76 'a', start, duration, startThread, endThread); 77 }; 78 79 TestUtils.newAsyncSliceNamed = function( 80 name, start, duration, startThread, endThread) { 81 var asyncSliceConstructor = 82 tr.model.AsyncSlice.getConstructor('', name); 83 84 var s = new asyncSliceConstructor('', name, 0, start); 85 s.duration = duration; 86 s.startThread = startThread; 87 s.endThread = endThread; 88 return s; 89 }; 90 91 function getColorId(colorId) { 92 if (colorId) { 93 if (colorId === 'random') { 94 colorId = Math.floor( 95 Math.random() * 96 ColorScheme.proprties.numGeneralPurposeColorIds); 97 } 98 } else { 99 colorId = 0; 100 } 101 return colorId; 102 } 103 104 TestUtils.newAsyncSliceEx = function(options) { 105 var sd = TestUtils.getStartAndDurationFromDict(options); 106 107 var cat = options.cat ? options.cat : 'cat'; 108 var title = options.title ? options.title : 'a'; 109 var colorId = getColorId(options.colorId); 110 111 var isTopLevel; 112 if (options.isTopLevel !== undefined) 113 isTopLevel = options.isTopLevel; 114 else 115 isTopLevel = false; 116 117 var asyncSliceConstructor = 118 tr.model.AsyncSlice.getConstructor(cat, title); 119 120 var slice = new asyncSliceConstructor( 121 cat, 122 title, 123 colorId, 124 sd.start, 125 options.args ? options.args : {}, 126 sd.duration, isTopLevel); 127 128 if (options.id) 129 slice.id = options.id; 130 else 131 slice.id = tr.b.GUID.allocate(); 132 133 if (options.startStackFrame) 134 slice.startStackFrame = options.startStackFrame; 135 if (options.endStackFrame) 136 slice.endStackFrame = options.endStackFrame; 137 if (options.important) 138 slice.important = options.important; 139 if (options.startThread) 140 slice.startThread = options.startThread; 141 if (options.endThread) 142 slice.endThread = options.endThread; 143 return slice; 144 }; 145 146 TestUtils.newCounter = function(parent) { 147 return TestUtils.newCounterNamed(parent, 'a'); 148 }; 149 150 TestUtils.newCounterNamed = function(parent, name) { 151 var s = new tr.model.Counter(parent, name, null, name); 152 return s; 153 }; 154 155 TestUtils.newCounterCategory = function(parent, category, name) { 156 var s = new tr.model.Counter(parent, name, category, name); 157 return s; 158 }; 159 160 TestUtils.newCounterSeries = function() { 161 var s = new tr.model.CounterSeries('a', 0); 162 return s; 163 }; 164 165 TestUtils.newFlowEventEx = function(options) { 166 if (options.start === undefined) 167 throw new Error('Too little info'); 168 169 var title = options.title ? options.title : 'a'; 170 171 var colorId = options.colorId ? options.colorId : 0; 172 173 var sd = TestUtils.getStartAndDurationFromDict(options); 174 175 var id; 176 if (options.id !== undefined) 177 id = options.id; 178 else 179 id = tr.b.GUID.allocate(); 180 181 var event = new tr.model.FlowEvent( 182 options.cat ? options.cat : 'cat', 183 id, 184 title, 185 colorId, 186 sd.start, 187 options.args ? options.args : {}, 188 sd.duration); 189 190 if (options.startStackFrame) 191 event.startStackFrame = options.startStackFrame; 192 if (options.endStackFrame) 193 event.endStackFrame = options.endStackFrame; 194 if (options.important) 195 event.important = options.important; 196 if (options.startSlice) { 197 event.startSlice = options.startSlice; 198 event.startSlice.outFlowEvents.push(event); 199 } 200 if (options.endSlice) { 201 event.endSlice = options.endSlice; 202 event.endSlice.inFlowEvents.push(event); 203 } 204 return event; 205 }; 206 207 TestUtils.newThreadSlice = function(thread, state, start, duration, opt_cpu) { 208 var s = new tr.model.ThreadTimeSlice( 209 thread, state, 'cat', start, {}, duration); 210 if (opt_cpu) 211 s.cpuOnWhichThreadWasRunning = opt_cpu; 212 return s; 213 }; 214 215 TestUtils.newSampleNamed = function( 216 thread, sampleName, category, frameNames, start) { 217 var model; 218 if (thread.parent) 219 model = thread.parent.model; 220 else 221 model = undefined; 222 var sf = TestUtils.newStackTrace(model, frameNames); 223 var s = new tr.model.Sample(undefined, thread, 224 sampleName, start, 225 sf, 226 1); 227 return s; 228 }; 229 230 TestUtils.newSliceEx = function(options) { 231 var sd = TestUtils.getStartAndDurationFromDict(options); 232 233 var title = options.title ? options.title : 'a'; 234 235 var colorId = options.colorId ? options.colorId : 0; 236 237 var cpuSD = _maybeGetCpuStartAndCpuDurationFromDict(options); 238 239 var type; 240 if (options.type) 241 type = options.type; 242 else 243 type = tr.model.Slice; 244 245 var slice = new type( 246 options.cat ? options.cat : 'cat', 247 title, 248 colorId, 249 sd.start, 250 options.args ? options.args : {}, 251 sd.duration, 252 cpuSD.start, cpuSD.duration); 253 254 255 return slice; 256 }; 257 258 TestUtils.newStackTrace = function(model, titles) { 259 var frame = undefined; 260 titles.forEach(function(title) { 261 frame = new tr.model.StackFrame(frame, tr.b.GUID.allocate(), title, 7); 262 if (model) 263 model.addStackFrame(frame); 264 }); 265 return frame; 266 }; 267 268 TestUtils.findSliceNamed = function(slices, name) { 269 if (slices instanceof tr.model.SliceGroup) 270 slices = slices.slices; 271 for (var i = 0; i < slices.length; i++) 272 if (slices[i].title == name) 273 return slices[i]; 274 return undefined; 275 }; 276 277 TestUtils.newInteractionRecord = function(parentModel, start, duration) { 278 return new tr.model.um.StubExpectation({ 279 parentModel: parentModel, start: start, duration: duration}); 280 }; 281 282 TestUtils.newModel = function(customizeModelCallback) { 283 return TestUtils.newModelWithEvents([], { 284 shiftWorldToZero: false, 285 pruneEmptyContainers: false, 286 customizeModelCallback: customizeModelCallback 287 }); 288 }; 289 290 TestUtils.newModelWithEvents = function(events, opts) { 291 if (!(events instanceof Array)) 292 events = [events]; 293 294 opts = opts || {}; 295 296 var io = new tr.importer.ImportOptions(); 297 io.showImportWarnings = false; 298 io.customizeModelCallback = opts.customizeModelCallback; 299 io.trackDetailedModelStats = opts.trackDetailedModelStats === undefined ? 300 false : opts.trackDetailedModelStats; 301 io.shiftWorldToZero = opts.shiftWorldToZero === undefined ? 302 true : opts.shiftWorldToZero; 303 io.pruneEmptyContainers = opts.pruneEmptyContainers === undefined ? 304 true : opts.pruneEmptyContainers; 305 io.auditorConstructors = opts.auditorConstructors === undefined ? 306 [] : opts.auditorConstructors; 307 308 var m = new tr.Model(); 309 var i = new tr.importer.Import(m, io); 310 i.importTraces(events); 311 return m; 312 }; 313 314 TestUtils.newModelWithAuditor = function(customizeModelCallback, auditor) { 315 return TestUtils.newModelWithEvents([], { 316 shiftWorldToZero: false, 317 pruneEmptyContainers: false, 318 customizeModelCallback: customizeModelCallback, 319 auditorConstructors: [auditor] 320 }); 321 }; 322 323 TestUtils.newFakeThread = function() { 324 var process = {model: {}}; 325 return new tr.model.Thread(process); 326 }; 327 328 /** @constructor */ 329 TestUtils.SourceGenerator = function() { 330 this.sourceList_ = []; 331 this.currentLineCommentList_ = []; 332 this.currentIndent_ = 0; 333 this.currentLineEmpty_ = true; 334 }; 335 336 TestUtils.SourceGenerator.prototype = { 337 push: function(/* arguments */) { 338 if (this.currentLineEmpty_) { 339 this.sourceList_.push(' '.repeat(this.currentIndent_)); 340 this.currentLineEmpty_ = false; 341 } 342 this.sourceList_.push.apply( 343 this.sourceList_, Array.prototype.slice.call(arguments)); 344 }, 345 346 pushComment: function(/* arguments */) { 347 this.currentLineCommentList_.push.apply( 348 this.currentLineCommentList_, Array.prototype.slice.call(arguments)); 349 }, 350 351 build: function() { 352 this.finishLine_(); 353 return this.sourceList_.join(''); 354 }, 355 356 breakLine: function() { 357 this.finishLine_(); 358 this.push('\n'); 359 this.currentLineEmpty_ = true; 360 }, 361 362 finishLine_: function() { 363 if (this.currentLineCommentList_.length === 0) 364 return; 365 this.push(' // '); 366 this.push.apply(this, this.currentLineCommentList_); 367 this.push('.'); 368 this.currentLineCommentList_ = []; 369 }, 370 371 indentBlock: function(spaces, breakLine, blockCallback, opt_this) { 372 opt_this = opt_this || this; 373 this.currentIndent_ += spaces; 374 if (breakLine) 375 this.breakLine(); 376 blockCallback.call(opt_this); 377 this.currentIndent_ -= spaces; 378 }, 379 380 formatSingleLineList: function(list, itemCallback, opt_this) { 381 opt_this = opt_this || this; 382 this.push('['); 383 tr.b.asArray(list).forEach(function(item, index) { 384 if (index > 0) 385 this.push(', '); 386 itemCallback.call(opt_this, item, index); 387 }, this); 388 this.push(']'); 389 }, 390 391 formatMultiLineList: function(list, itemCallback, opt_this) { 392 opt_this = opt_this || this; 393 this.push('['); 394 this.indentBlock(2, false /* don't break line */, function() { 395 tr.b.asArray(list).forEach(function(item, index) { 396 if (index > 0) 397 this.push(','); 398 this.breakLine(); 399 itemCallback.call(opt_this, item, index); 400 }, this); 401 }, this); 402 if (list.length > 0) 403 this.breakLine(); 404 this.push(']'); 405 }, 406 407 formatString: function(string) { 408 if (string === undefined) 409 this.push('undefined'); 410 else 411 this.push('\'', string, '\''); 412 } 413 }; 414 415 TestUtils.addSourceListing = function(test, source) { 416 var testSourceEl = document.createElement('pre'); 417 testSourceEl.style.fontFamily = 'monospace'; 418 testSourceEl.textContent = source; 419 420 var copyButtonEl = document.createElement('button'); 421 copyButtonEl.textContent = 'Copy into to clipboard'; 422 copyButtonEl.addEventListener('click', function() { 423 var selection = window.getSelection(); 424 425 // Store the original selection. 426 var originalRanges = new Array(selection.rangeCount); 427 for (var i = 0; i < originalRanges.length; i++) 428 originalRanges[i] = selection.getRangeAt(i); 429 430 // Copy the generated test source code into clipboard. 431 selection.removeAllRanges(); 432 var range = document.createRange(); 433 range.selectNode(testSourceEl); 434 selection.addRange(range); 435 document.execCommand('copy'); 436 437 // Restore the original selection. 438 selection.removeAllRanges(); 439 for (var i = 0; i < originalRanges.length; i++) 440 selection.addRange(originalRanges[i]); 441 }); 442 443 var outputEl = document.createElement('div'); 444 outputEl.appendChild(copyButtonEl); 445 outputEl.appendChild(testSourceEl); 446 test.addHTMLOutput(outputEl); 447 }; 448 449 TestUtils.newInstantEvent = function(options) { 450 var title = options.title; 451 var start = options.start; 452 if ((title === undefined) || 453 (title === '') || 454 (start === undefined)) 455 throw new Error('too little information'); 456 457 var category = options.category || 'category'; 458 var colorId = getColorId(options.colorId); 459 var args = options.args || {}; 460 return new tr.model.InstantEvent( 461 category, title, colorId, start, args); 462 }; 463 464 return { 465 TestUtils: TestUtils 466 }; 467}); 468</script> 469