1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Include test fixture. 6GEN_INCLUDE(['net_internals_test.js']); 7 8// Anonymous namespace 9(function() { 10 11// Range of time set on a log once loaded used by sanity checks. 12// Used by sanityCheckWithTimeRange. 13var startTime = null; 14var endTime = null; 15 16function timelineView() { 17 return TimelineView.getInstance(); 18} 19 20function graphView() { 21 return timelineView().graphView_; 22} 23 24function scrollbar() { 25 return graphView().scrollbar_; 26} 27 28function canvas() { 29 return graphView().canvas_; 30} 31 32/** 33 * A Task that creates a log dump, modifies it so |timeTicks| are all in UTC, 34 * clears all events from the log, and then adds two new SOCKET events, which 35 * have the specified start and end times. 36 * 37 * Most of these tests start with this task first. This gives us a known 38 * starting state, and prevents the data from automatically updating. 39 * 40 * @param {int} startTime Time of the begin event. 41 * @param {int} endTime Time of the end event. 42 * @extends {NetInternalsTest.Task} 43 */ 44function LoadLogWithNewEventsTask(startTime, endTime) { 45 NetInternalsTest.Task.call(this); 46 this.startTime_ = startTime; 47 this.endTime_ = endTime; 48} 49 50LoadLogWithNewEventsTask.prototype = { 51 __proto__: NetInternalsTest.Task.prototype, 52 53 /** 54 * Starts creating a log dump. 55 */ 56 start: function() { 57 log_util.createLogDumpAsync('test', this.onLogDumpCreated.bind(this), true); 58 }, 59 60 /** 61 * Modifies the log dump and loads it. 62 */ 63 onLogDumpCreated: function(logDumpText) { 64 var logDump = JSON.parse(logDumpText); 65 66 logDump.constants.timeTickOffset = '0'; 67 logDump.events = []; 68 69 var source = new NetInternalsTest.Source(1, EventSourceType.SOCKET); 70 logDump.events.push( 71 NetInternalsTest.createBeginEvent(source, EventType.SOCKET_ALIVE, 72 this.startTime_, null)); 73 logDump.events.push( 74 NetInternalsTest.createMatchingEndEvent(logDump.events[0], 75 this.endTime_, null)); 76 logDumpText = JSON.stringify(logDump); 77 78 assertEquals('Log loaded.', log_util.loadLogFile(logDumpText)); 79 80 endTime = this.endTime_; 81 startTime = this.startTime_; 82 if (startTime >= endTime) 83 --startTime; 84 85 sanityCheckWithTimeRange(false); 86 87 this.onTaskDone(); 88 } 89}; 90 91/** 92 * Checks certain invariant properties of the TimelineGraphView and the 93 * scroll bar. 94 */ 95function sanityCheck() { 96 expectLT(graphView().startTime_, graphView().endTime_); 97 expectLE(0, scrollbar().getPosition()); 98 expectLE(scrollbar().getPosition(), scrollbar().getRange()); 99} 100 101/** 102 * Checks what sanityCheck does, but also checks that |startTime| and |endTime| 103 * are the same as those used by the graph, as well as whether we have a timer 104 * running to update the graph's end time. To avoid flake, this should only 105 * be used synchronously relative to when |startTime| and |endTime| were set, 106 * unless |expectUpdateTimer| is false. 107 * @param {bool} expectUpdateTimer true if the TimelineView should currently 108 * have an update end time timer running. 109 */ 110function sanityCheckWithTimeRange(expectUpdateTimer) { 111 if (!expectUpdateTimer) { 112 expectEquals(null, timelineView().updateIntervalId_); 113 } else { 114 expectNotEquals(null, timelineView().updateIntervalId_); 115 } 116 assertNotEquals(startTime, null); 117 assertNotEquals(endTime, null); 118 expectEquals(startTime, graphView().startTime_); 119 expectEquals(endTime, graphView().endTime_); 120 sanityCheck(false); 121} 122 123/** 124 * Checks what sanityCheck does, but also checks that |startTime| and |endTime| 125 * are the same as those used by the graph. 126 */ 127function sanityCheckNotUpdating() { 128 expectEquals(null, timelineView().updateIntervalId_); 129 sanityCheckWithTimeRange(); 130} 131 132/** 133 * Simulates mouse wheel movement over the canvas element. 134 * @param {number} ticks Number of mouse wheel ticks to simulate. 135 */ 136function mouseZoom(ticks) { 137 var scrollbarStartedAtEnd = 138 (scrollbar().getRange() == scrollbar().getPosition()); 139 140 var event = new WheelEvent('mousewheel', {deltaX: 0, deltaY: -ticks}); 141 canvas().dispatchEvent(event); 142 143 // If the scrollbar started at the end of the range, make sure it ends there 144 // as well. 145 if (scrollbarStartedAtEnd) 146 expectEquals(scrollbar().getRange(), scrollbar().getPosition()); 147 148 sanityCheck(); 149} 150 151/** 152 * Simulates moving the mouse wheel up. 153 * @param {number} ticks Number of mouse wheel ticks to simulate. 154 */ 155function mouseZoomIn(ticks) { 156 assertGT(ticks, 0); 157 var oldScale = graphView().scale_; 158 var oldRange = scrollbar().getRange(); 159 160 mouseZoom(ticks); 161 162 if (oldScale == graphView().scale_) { 163 expectEquals(oldScale, TimelineGraphView.MIN_SCALE); 164 } else { 165 expectLT(graphView().scale_, oldScale); 166 } 167 expectGE(scrollbar().getRange(), oldRange); 168} 169 170/** 171 * Simulates moving the mouse wheel down. 172 * @param {number} ticks Number of mouse wheel ticks to simulate. 173 */ 174function mouseZoomOut(ticks) { 175 assertGT(ticks, 0); 176 var oldScale = graphView().scale_; 177 var oldRange = scrollbar().getRange(); 178 179 mouseZoom(-ticks); 180 181 expectGT(graphView().scale_, oldScale); 182 expectLE(scrollbar().getRange(), oldRange); 183} 184 185/** 186 * Simulates zooming all the way with multiple mouse wheel events. 187 */ 188function mouseZoomAllTheWayIn() { 189 expectLT(TimelineGraphView.MIN_SCALE, graphView().scale_); 190 while (graphView().scale_ != TimelineGraphView.MIN_SCALE) 191 mouseZoomIn(8); 192 // Verify that zooming in when already at max zoom works. 193 mouseZoomIn(1); 194} 195 196/** 197 * A Task that scrolls the scrollbar by manipulating the DOM, and then waits 198 * for the scroll to complete. Has to be a task because onscroll and DOM 199 * manipulations both occur asynchronously. 200 * 201 * Not safe to use when other asynchronously running code may try to 202 * manipulate the scrollbar itself, or adjust the length of the scrollbar. 203 * 204 * @param {int} position Position to scroll to. 205 * @extends {NetInternalsTest.Task} 206 */ 207function MouseScrollTask(position) { 208 NetInternalsTest.Task.call(this); 209 this.position_ = position; 210 // If the scrollbar's |position| and its node's |scrollLeft| values don't 211 // currently match, we set this to true and wait for |scrollLeft| to be 212 // updated, which will trigger an onscroll event. 213 this.waitingToStart_ = false; 214} 215 216MouseScrollTask.prototype = { 217 __proto__: NetInternalsTest.Task.prototype, 218 219 start: function() { 220 this.waitingToStart_ = false; 221 // If the scrollbar is already in the correct position, do nothing. 222 if (scrollbar().getNode().scrollLeft == this.position_) { 223 // We may still have a timer going to adjust the position of the 224 // scrollbar to some other value. If so, this will clear it. 225 scrollbar().setPosition(this.position_); 226 this.onTaskDone(); 227 return; 228 } 229 230 // Replace the onscroll event handler with our own. 231 this.oldOnScroll_ = scrollbar().getNode().onscroll; 232 scrollbar().getNode().onscroll = this.onScroll_.bind(this); 233 if (scrollbar().getNode().scrollLeft != scrollbar().getPosition()) { 234 this.waitingToStart_ = true; 235 return; 236 } 237 238 window.setTimeout(this.startScrolling_.bind(this), 0); 239 }, 240 241 onScroll_: function(event) { 242 // Restore the original onscroll function. 243 scrollbar().getNode().onscroll = this.oldOnScroll_; 244 // Call the original onscroll function. 245 this.oldOnScroll_(event); 246 247 if (this.waitingToStart_) { 248 this.start(); 249 return; 250 } 251 252 assertEquals(this.position_, scrollbar().getNode().scrollLeft); 253 assertEquals(this.position_, scrollbar().getPosition()); 254 255 sanityCheck(); 256 this.onTaskDone(); 257 }, 258 259 startScrolling_: function() { 260 scrollbar().getNode().scrollLeft = this.position_; 261 } 262}; 263 264/** 265 * Tests setting and updating range. 266 */ 267TEST_F('NetInternalsTest', 'netInternalsTimelineViewRange', function() { 268 NetInternalsTest.switchToView('timeline'); 269 270 // Set startTime/endTime for sanity checks. 271 startTime = graphView().startTime_; 272 endTime = graphView().endTime_; 273 sanityCheckWithTimeRange(true); 274 275 startTime = 0; 276 endTime = 10; 277 graphView().setDateRange(new Date(startTime), new Date(endTime)); 278 sanityCheckWithTimeRange(true); 279 280 endTime = (new Date()).getTime(); 281 graphView().updateEndDate(); 282 283 expectGE(graphView().endTime_, endTime); 284 sanityCheck(); 285 286 testDone(); 287}); 288 289/** 290 * Tests using the scroll bar. 291 */ 292TEST_F('NetInternalsTest', 'netInternalsTimelineViewScrollbar', function() { 293 // The range we want the graph to have. 294 var expectedGraphRange = canvas().width; 295 296 function checkGraphRange() { 297 expectEquals(expectedGraphRange, scrollbar().getRange()); 298 } 299 300 var taskQueue = new NetInternalsTest.TaskQueue(true); 301 // Load a log and then switch to the timeline view. The end time is 302 // calculated so that the range is exactly |expectedGraphRange|. 303 taskQueue.addTask( 304 new LoadLogWithNewEventsTask( 305 55, 306 55 + graphView().scale_ * (canvas().width + expectedGraphRange))); 307 taskQueue.addFunctionTask( 308 NetInternalsTest.switchToView.bind(null, 'timeline')); 309 taskQueue.addFunctionTask(checkGraphRange); 310 311 taskQueue.addTask(new MouseScrollTask(0)); 312 taskQueue.addTask(new MouseScrollTask(expectedGraphRange)); 313 taskQueue.addTask(new MouseScrollTask(1)); 314 taskQueue.addTask(new MouseScrollTask(expectedGraphRange - 1)); 315 316 taskQueue.addFunctionTask(checkGraphRange); 317 taskQueue.addFunctionTask(sanityCheckWithTimeRange.bind(null, false)); 318 taskQueue.run(); 319}); 320 321/** 322 * Dumps a log file to memory, modifies its events, loads it again, and 323 * makes sure the range is correctly set and not automatically updated. 324 */ 325TEST_F('NetInternalsTest', 'netInternalsTimelineViewLoadLog', function() { 326 // After loading the log file, the rest of the test runs synchronously. 327 function testBody() { 328 NetInternalsTest.switchToView('timeline'); 329 sanityCheckWithTimeRange(false); 330 331 // Make sure everything's still fine when we switch to another view. 332 NetInternalsTest.switchToView('events'); 333 sanityCheckWithTimeRange(false); 334 } 335 336 // Load a log and then run the rest of the test. 337 var taskQueue = new NetInternalsTest.TaskQueue(true); 338 taskQueue.addTask(new LoadLogWithNewEventsTask(55, 10055)); 339 taskQueue.addFunctionTask(testBody); 340 taskQueue.run(); 341}); 342 343/** 344 * Zooms out twice, and then zooms in once. 345 */ 346TEST_F('NetInternalsTest', 'netInternalsTimelineViewZoomOut', function() { 347 // After loading the log file, the rest of the test runs synchronously. 348 function testBody() { 349 NetInternalsTest.switchToView('timeline'); 350 mouseZoomOut(1); 351 mouseZoomOut(1); 352 mouseZoomIn(1); 353 sanityCheckWithTimeRange(false); 354 } 355 356 // Load a log and then run the rest of the test. 357 var taskQueue = new NetInternalsTest.TaskQueue(true); 358 taskQueue.addTask(new LoadLogWithNewEventsTask(55, 10055)); 359 taskQueue.addFunctionTask(testBody); 360 taskQueue.run(); 361}); 362 363/** 364 * Zooms in as much as allowed, and zooms out once. 365 */ 366TEST_F('NetInternalsTest', 'netInternalsTimelineViewZoomIn', function() { 367 // After loading the log file, the rest of the test runs synchronously. 368 function testBody() { 369 NetInternalsTest.switchToView('timeline'); 370 mouseZoomAllTheWayIn(); 371 mouseZoomOut(1); 372 sanityCheckWithTimeRange(false); 373 } 374 375 // Load a log and then run the rest of the test. 376 var taskQueue = new NetInternalsTest.TaskQueue(true); 377 taskQueue.addTask(new LoadLogWithNewEventsTask(55, 10055)); 378 taskQueue.addFunctionTask(testBody); 379 taskQueue.run(); 380}); 381 382/** 383 * Tests case of all events having the same time. 384 */ 385TEST_F('NetInternalsTest', 'netInternalsTimelineViewDegenerate', function() { 386 // After loading the log file, the rest of the test runs synchronously. 387 function testBody() { 388 NetInternalsTest.switchToView('timeline'); 389 mouseZoomOut(1); 390 mouseZoomAllTheWayIn(); 391 mouseZoomOut(1); 392 sanityCheckWithTimeRange(false); 393 } 394 395 // Load a log and then run the rest of the test. 396 var taskQueue = new NetInternalsTest.TaskQueue(true); 397 taskQueue.addTask(new LoadLogWithNewEventsTask(55, 55)); 398 taskQueue.addFunctionTask(testBody); 399 taskQueue.run(); 400}); 401 402/** 403 * Tests case of having no events. Runs synchronously. 404 */ 405TEST_F('NetInternalsTest', 'netInternalsTimelineViewNoEvents', function() { 406 // Click the button to clear all the captured events, and then switch to 407 // timeline 408 $(CaptureView.RESET_BUTTON_ID).click(); 409 NetInternalsTest.switchToView('timeline'); 410 411 // Set startTime/endTime for sanity checks. 412 startTime = graphView().startTime_; 413 endTime = graphView().endTime_; 414 415 sanityCheckWithTimeRange(true); 416 417 mouseZoomOut(1); 418 sanityCheckWithTimeRange(true); 419 420 mouseZoomAllTheWayIn(); 421 sanityCheckWithTimeRange(true); 422 423 mouseZoomOut(1); 424 sanityCheckWithTimeRange(true); 425 426 testDone(); 427}); 428 429})(); // Anonymous namespace 430