| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| |
| <link rel="import" href="/tracing/base/iteration_helpers.html"> |
| <link rel="import" href="/tracing/importer/import.html"> |
| <link rel="import" href="/tracing/model/counter.html"> |
| <link rel="import" href="/tracing/model/model.html"> |
| <link rel="import" href="/tracing/model/slice.html"> |
| <link rel="import" href="/tracing/model/slice_group.html"> |
| <link rel="import" href="/tracing/model/stack_frame.html"> |
| <link rel="import" href="/tracing/model/thread_time_slice.html"> |
| <link rel="import" href="/tracing/model/user_model/stub_expectation.html"> |
| |
| <script> |
| 'use strict'; |
| |
| /** |
| * @fileoverview Helper functions for use in tracing tests. |
| */ |
| tr.exportTo('tr.c', function() { |
| var ColorScheme = tr.b.ColorScheme; |
| |
| function _getStartAndCpuDurationFromDict( |
| options, required, startFieldName, durationFieldName, endFieldName) { |
| |
| if (options[startFieldName] === undefined) { |
| if (required) |
| throw new Error('Too little information.'); |
| else |
| return {start: undefined, duration: undefined}; |
| } |
| if (options[durationFieldName] !== undefined && |
| options[endFieldName] !== undefined) { |
| throw new Error('Too much information.'); |
| } |
| if (options[durationFieldName] === undefined && |
| options[endFieldName] === undefined) { |
| if (required) |
| throw new Error('Too little information.'); |
| else |
| return {start: undefined, duration: undefined}; |
| } |
| |
| var duration; |
| if (options[durationFieldName] !== undefined) { |
| duration = options[durationFieldName]; |
| } else { |
| duration = options[endFieldName] - options[startFieldName]; |
| } |
| |
| return { |
| start: options[startFieldName], |
| duration: duration |
| }; |
| } |
| |
| function _maybeGetCpuStartAndCpuDurationFromDict(options) { |
| return _getStartAndCpuDurationFromDict( |
| options, false, 'cpuStart', 'cpuDuration', 'cpuEnd'); |
| } |
| |
| function TestUtils() { |
| } |
| |
| TestUtils.getStartAndDurationFromDict = function(options) { |
| return _getStartAndCpuDurationFromDict( |
| options, true, 'start', 'duration', 'end'); |
| }; |
| |
| TestUtils.newAsyncSlice = function(start, duration, startThread, endThread) { |
| return TestUtils.newAsyncSliceNamed( |
| 'a', start, duration, startThread, endThread); |
| }; |
| |
| TestUtils.newAsyncSliceNamed = function( |
| name, start, duration, startThread, endThread) { |
| var asyncSliceConstructor = |
| tr.model.AsyncSlice.getConstructor('', name); |
| |
| var s = new asyncSliceConstructor('', name, 0, start); |
| s.duration = duration; |
| s.startThread = startThread; |
| s.endThread = endThread; |
| return s; |
| }; |
| |
| function getColorId(colorId) { |
| if (colorId) { |
| if (colorId === 'random') { |
| colorId = Math.floor( |
| Math.random() * |
| ColorScheme.proprties.numGeneralPurposeColorIds); |
| } |
| } else { |
| colorId = 0; |
| } |
| return colorId; |
| } |
| |
| TestUtils.newAsyncSliceEx = function(options) { |
| var sd = TestUtils.getStartAndDurationFromDict(options); |
| |
| var cat = options.cat ? options.cat : 'cat'; |
| var title = options.title ? options.title : 'a'; |
| var colorId = getColorId(options.colorId); |
| |
| var isTopLevel; |
| if (options.isTopLevel !== undefined) |
| isTopLevel = options.isTopLevel; |
| else |
| isTopLevel = false; |
| |
| var asyncSliceConstructor = |
| tr.model.AsyncSlice.getConstructor(cat, title); |
| |
| var slice = new asyncSliceConstructor( |
| cat, |
| title, |
| colorId, |
| sd.start, |
| options.args ? options.args : {}, |
| sd.duration, isTopLevel); |
| |
| if (options.id) |
| slice.id = options.id; |
| else |
| slice.id = tr.b.GUID.allocate(); |
| |
| if (options.startStackFrame) |
| slice.startStackFrame = options.startStackFrame; |
| if (options.endStackFrame) |
| slice.endStackFrame = options.endStackFrame; |
| if (options.important) |
| slice.important = options.important; |
| if (options.startThread) |
| slice.startThread = options.startThread; |
| if (options.endThread) |
| slice.endThread = options.endThread; |
| return slice; |
| }; |
| |
| TestUtils.newCounter = function(parent) { |
| return TestUtils.newCounterNamed(parent, 'a'); |
| }; |
| |
| TestUtils.newCounterNamed = function(parent, name) { |
| var s = new tr.model.Counter(parent, name, null, name); |
| return s; |
| }; |
| |
| TestUtils.newCounterCategory = function(parent, category, name) { |
| var s = new tr.model.Counter(parent, name, category, name); |
| return s; |
| }; |
| |
| TestUtils.newCounterSeries = function() { |
| var s = new tr.model.CounterSeries('a', 0); |
| return s; |
| }; |
| |
| TestUtils.newFlowEventEx = function(options) { |
| if (options.start === undefined) |
| throw new Error('Too little info'); |
| |
| var title = options.title ? options.title : 'a'; |
| |
| var colorId = options.colorId ? options.colorId : 0; |
| |
| var sd = TestUtils.getStartAndDurationFromDict(options); |
| |
| var id; |
| if (options.id !== undefined) |
| id = options.id; |
| else |
| id = tr.b.GUID.allocate(); |
| |
| var event = new tr.model.FlowEvent( |
| options.cat ? options.cat : 'cat', |
| id, |
| title, |
| colorId, |
| sd.start, |
| options.args ? options.args : {}, |
| sd.duration); |
| |
| if (options.startStackFrame) |
| event.startStackFrame = options.startStackFrame; |
| if (options.endStackFrame) |
| event.endStackFrame = options.endStackFrame; |
| if (options.important) |
| event.important = options.important; |
| if (options.startSlice) { |
| event.startSlice = options.startSlice; |
| event.startSlice.outFlowEvents.push(event); |
| } |
| if (options.endSlice) { |
| event.endSlice = options.endSlice; |
| event.endSlice.inFlowEvents.push(event); |
| } |
| return event; |
| }; |
| |
| TestUtils.newThreadSlice = function(thread, state, start, duration, opt_cpu) { |
| var s = new tr.model.ThreadTimeSlice( |
| thread, state, 'cat', start, {}, duration); |
| if (opt_cpu) |
| s.cpuOnWhichThreadWasRunning = opt_cpu; |
| return s; |
| }; |
| |
| TestUtils.newSampleNamed = function( |
| thread, sampleName, category, frameNames, start) { |
| var model; |
| if (thread.parent) |
| model = thread.parent.model; |
| else |
| model = undefined; |
| var sf = TestUtils.newStackTrace(model, frameNames); |
| var s = new tr.model.Sample(undefined, thread, |
| sampleName, start, |
| sf, |
| 1); |
| return s; |
| }; |
| |
| TestUtils.newSliceEx = function(options) { |
| var sd = TestUtils.getStartAndDurationFromDict(options); |
| |
| var title = options.title ? options.title : 'a'; |
| |
| var colorId = options.colorId ? options.colorId : 0; |
| |
| var cpuSD = _maybeGetCpuStartAndCpuDurationFromDict(options); |
| |
| var type; |
| if (options.type) |
| type = options.type; |
| else |
| type = tr.model.Slice; |
| |
| var slice = new type( |
| options.cat ? options.cat : 'cat', |
| title, |
| colorId, |
| sd.start, |
| options.args ? options.args : {}, |
| sd.duration, |
| cpuSD.start, cpuSD.duration); |
| |
| |
| return slice; |
| }; |
| |
| TestUtils.newStackTrace = function(model, titles) { |
| var frame = undefined; |
| titles.forEach(function(title) { |
| frame = new tr.model.StackFrame(frame, tr.b.GUID.allocate(), title, 7); |
| if (model) |
| model.addStackFrame(frame); |
| }); |
| return frame; |
| }; |
| |
| TestUtils.findSliceNamed = function(slices, name) { |
| if (slices instanceof tr.model.SliceGroup) |
| slices = slices.slices; |
| for (var i = 0; i < slices.length; i++) |
| if (slices[i].title == name) |
| return slices[i]; |
| return undefined; |
| }; |
| |
| TestUtils.newInteractionRecord = function(parentModel, start, duration) { |
| return new tr.model.um.StubExpectation({ |
| parentModel: parentModel, start: start, duration: duration}); |
| }; |
| |
| TestUtils.newModel = function(customizeModelCallback) { |
| return TestUtils.newModelWithEvents([], { |
| shiftWorldToZero: false, |
| pruneEmptyContainers: false, |
| customizeModelCallback: customizeModelCallback |
| }); |
| }; |
| |
| TestUtils.newModelWithEvents = function(events, opts) { |
| if (!(events instanceof Array)) |
| events = [events]; |
| |
| opts = opts || {}; |
| |
| var io = new tr.importer.ImportOptions(); |
| io.showImportWarnings = false; |
| io.customizeModelCallback = opts.customizeModelCallback; |
| io.trackDetailedModelStats = opts.trackDetailedModelStats === undefined ? |
| false : opts.trackDetailedModelStats; |
| io.shiftWorldToZero = opts.shiftWorldToZero === undefined ? |
| true : opts.shiftWorldToZero; |
| io.pruneEmptyContainers = opts.pruneEmptyContainers === undefined ? |
| true : opts.pruneEmptyContainers; |
| io.auditorConstructors = opts.auditorConstructors === undefined ? |
| [] : opts.auditorConstructors; |
| |
| var m = new tr.Model(); |
| var i = new tr.importer.Import(m, io); |
| i.importTraces(events); |
| return m; |
| }; |
| |
| TestUtils.newModelWithAuditor = function(customizeModelCallback, auditor) { |
| return TestUtils.newModelWithEvents([], { |
| shiftWorldToZero: false, |
| pruneEmptyContainers: false, |
| customizeModelCallback: customizeModelCallback, |
| auditorConstructors: [auditor] |
| }); |
| }; |
| |
| TestUtils.newFakeThread = function() { |
| var process = {model: {}}; |
| return new tr.model.Thread(process); |
| }; |
| |
| /** @constructor */ |
| TestUtils.SourceGenerator = function() { |
| this.sourceList_ = []; |
| this.currentLineCommentList_ = []; |
| this.currentIndent_ = 0; |
| this.currentLineEmpty_ = true; |
| }; |
| |
| TestUtils.SourceGenerator.prototype = { |
| push: function(/* arguments */) { |
| if (this.currentLineEmpty_) { |
| this.sourceList_.push(' '.repeat(this.currentIndent_)); |
| this.currentLineEmpty_ = false; |
| } |
| this.sourceList_.push.apply( |
| this.sourceList_, Array.prototype.slice.call(arguments)); |
| }, |
| |
| pushComment: function(/* arguments */) { |
| this.currentLineCommentList_.push.apply( |
| this.currentLineCommentList_, Array.prototype.slice.call(arguments)); |
| }, |
| |
| build: function() { |
| this.finishLine_(); |
| return this.sourceList_.join(''); |
| }, |
| |
| breakLine: function() { |
| this.finishLine_(); |
| this.push('\n'); |
| this.currentLineEmpty_ = true; |
| }, |
| |
| finishLine_: function() { |
| if (this.currentLineCommentList_.length === 0) |
| return; |
| this.push(' // '); |
| this.push.apply(this, this.currentLineCommentList_); |
| this.push('.'); |
| this.currentLineCommentList_ = []; |
| }, |
| |
| indentBlock: function(spaces, breakLine, blockCallback, opt_this) { |
| opt_this = opt_this || this; |
| this.currentIndent_ += spaces; |
| if (breakLine) |
| this.breakLine(); |
| blockCallback.call(opt_this); |
| this.currentIndent_ -= spaces; |
| }, |
| |
| formatSingleLineList: function(list, itemCallback, opt_this) { |
| opt_this = opt_this || this; |
| this.push('['); |
| tr.b.asArray(list).forEach(function(item, index) { |
| if (index > 0) |
| this.push(', '); |
| itemCallback.call(opt_this, item, index); |
| }, this); |
| this.push(']'); |
| }, |
| |
| formatMultiLineList: function(list, itemCallback, opt_this) { |
| opt_this = opt_this || this; |
| this.push('['); |
| this.indentBlock(2, false /* don't break line */, function() { |
| tr.b.asArray(list).forEach(function(item, index) { |
| if (index > 0) |
| this.push(','); |
| this.breakLine(); |
| itemCallback.call(opt_this, item, index); |
| }, this); |
| }, this); |
| if (list.length > 0) |
| this.breakLine(); |
| this.push(']'); |
| }, |
| |
| formatString: function(string) { |
| if (string === undefined) |
| this.push('undefined'); |
| else |
| this.push('\'', string, '\''); |
| } |
| }; |
| |
| TestUtils.addSourceListing = function(test, source) { |
| var testSourceEl = document.createElement('pre'); |
| testSourceEl.style.fontFamily = 'monospace'; |
| testSourceEl.textContent = source; |
| |
| var copyButtonEl = document.createElement('button'); |
| copyButtonEl.textContent = 'Copy into to clipboard'; |
| copyButtonEl.addEventListener('click', function() { |
| var selection = window.getSelection(); |
| |
| // Store the original selection. |
| var originalRanges = new Array(selection.rangeCount); |
| for (var i = 0; i < originalRanges.length; i++) |
| originalRanges[i] = selection.getRangeAt(i); |
| |
| // Copy the generated test source code into clipboard. |
| selection.removeAllRanges(); |
| var range = document.createRange(); |
| range.selectNode(testSourceEl); |
| selection.addRange(range); |
| document.execCommand('copy'); |
| |
| // Restore the original selection. |
| selection.removeAllRanges(); |
| for (var i = 0; i < originalRanges.length; i++) |
| selection.addRange(originalRanges[i]); |
| }); |
| |
| var outputEl = document.createElement('div'); |
| outputEl.appendChild(copyButtonEl); |
| outputEl.appendChild(testSourceEl); |
| test.addHTMLOutput(outputEl); |
| }; |
| |
| TestUtils.newInstantEvent = function(options) { |
| var title = options.title; |
| var start = options.start; |
| if ((title === undefined) || |
| (title === '') || |
| (start === undefined)) |
| throw new Error('too little information'); |
| |
| var category = options.category || 'category'; |
| var colorId = getColorId(options.colorId); |
| var args = options.args || {}; |
| return new tr.model.InstantEvent( |
| category, title, colorId, start, args); |
| }; |
| |
| return { |
| TestUtils: TestUtils |
| }; |
| }); |
| </script> |