diff options
author | tyoverby@chromium.org <tyoverby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-26 17:45:59 +0000 |
---|---|---|
committer | tyoverby@chromium.org <tyoverby@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-26 17:45:59 +0000 |
commit | 446cbb7551632e737d32998107b652e37a2f5ca7 (patch) | |
tree | 77793fca3dbd4dd6ec003e56d2adb49750ec40a0 /content/browser/resources | |
parent | 7bbf03c589baa5f2bc26b3748c84ccd979e056de (diff) | |
download | chromium_src-446cbb7551632e737d32998107b652e37a2f5ca7.zip chromium_src-446cbb7551632e737d32998107b652e37a2f5ca7.tar.gz chromium_src-446cbb7551632e737d32998107b652e37a2f5ca7.tar.bz2 |
First part of the new media-internals javascript frontend
BUG= 260005
Review URL: https://chromiumcodereview.appspot.com/20063004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213949 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/resources')
7 files changed, 707 insertions, 6 deletions
diff --git a/content/browser/resources/media/new/media_internals.html b/content/browser/resources/media/new/media_internals.html index 288a7b9..0e95353 100644 --- a/content/browser/resources/media/new/media_internals.html +++ b/content/browser/resources/media/new/media_internals.html @@ -1,11 +1,9 @@ -<!DOCTYPE HTML> - <!-- Copyright 2013 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. --> - +<!DOCTYPE html> <html i18n-values="dir:textdirection"> <head> <meta charset="utf-8"> diff --git a/content/browser/resources/media/new/media_internals.js b/content/browser/resources/media/new/media_internals.js index 11f6749..103ef74 100644 --- a/content/browser/resources/media/new/media_internals.js +++ b/content/browser/resources/media/new/media_internals.js @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var media = {} +var media = {}; -var doNothing = function () {}; +var doNothing = function() {}; +// Silence the backend calls. media.initialize = doNothing; media.addAudioStream = doNothing; media.cacheEntriesByKey = doNothing; -media.onReceiveEverything = doNothing; +media.onReceiveEverything = doNothing; media.onItemDeleted = doNothing; media.onRendererTerminated = doNothing; media.onNetUpdate = doNothing; diff --git a/content/browser/resources/media/new/player_info.js b/content/browser/resources/media/new/player_info.js new file mode 100644 index 0000000..af1f194 --- /dev/null +++ b/content/browser/resources/media/new/player_info.js @@ -0,0 +1,80 @@ +// Copyright 2013 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. + +/** + * @fileoverview A class for keeping track of the details of a player. + */ + +var PlayerInfo = (function() { + 'use strict'; + + /** + * A class that keeps track of properties on a media player. + * @param id A unique id that can be used to identify this player. + */ + function PlayerInfo(id) { + this.id = id; + // The current value of the properties for this player. + this.properties = {}; + // All of the past (and present) values of the properties. + this.pastValues = {}; + + // Every single event in the order in which they were received. + this.allEvents = []; + this.lastRendered = 0; + + this.firstTimestamp_ = -1; + } + + PlayerInfo.prototype = { + /** + * Adds or set a property on this player. + * This is the default logging method as it keeps track of old values. + * @param timestamp The time in milliseconds since the Epoch. + * @param key A String key that describes the property. + * @param value The value of the property. + */ + addProperty: function(timestamp, key, value) { + // The first timestamp that we get will be recorded. + // Then, all future timestamps are deltas of that. + if (this.firstTimestamp_ === -1) { + this.firstTimestamp_ = timestamp; + } + + if (typeof key !== 'string') { + throw new Error(typeof key + ' is not a valid key type'); + } + + this.properties[key] = value; + + if (!this.pastValues[key]) { + this.pastValues[key] = []; + } + + var recordValue = { + time: timestamp - this.firstTimestamp_, + key: key, + value: value + }; + + this.pastValues[key].push(recordValue); + this.allEvents.push(recordValue); + }, + + /** + * Adds or set a property on this player. + * Does not keep track of old values. This is better for + * values that get spammed repeatedly. + * @param timestamp The time in milliseconds since the Epoch. + * @param key A String key that describes the property. + * @param value The value of the property. + */ + addPropertyNoRecord: function(timestamp, key, value) { + this.addProperty(timestamp, key, value); + this.allEvents.pop(); + } + }; + + return PlayerInfo; +}()); diff --git a/content/browser/resources/media/new/player_info_test.html b/content/browser/resources/media/new/player_info_test.html new file mode 100644 index 0000000..46cc05ee --- /dev/null +++ b/content/browser/resources/media/new/player_info_test.html @@ -0,0 +1,146 @@ +<!-- +Copyright 2013 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. +--> +<!DOCTYPE html> +<html> + <head> + <script src="webui_resource_test.js"></script> + <script src="player_manager.js"></script> + <script src="player_info.js"></script> + </head> + <body> + <script> + window.setUp = function() { + window.pi = new PlayerInfo('example_id'); + }; + + window.tearDown = function() { + window.pi = null; + }; + + // Test that an ID is set correctly. + window.testConstructorStringID = function() { + assertEquals('example_id', window.pi.id); + }; + + // Test that numerical IDs are valid. + window.testConstructorNumberId = function() { + var pi = new PlayerInfo(5); + assertEquals(5, pi.id); + }; + + // Make sure that a new PlayerInfo has no events. + window.testEmptyEvents = function() { + assertEquals(0, window.pi.allEvents.length); + }; + + // Check that the most recent property gets updated. + window.testAddProperty = function() { + var key = 'key', + value = 'value', + value2 = 'value2'; + + window.pi.addProperty(0, key, value); + assertEquals(value, window.pi.properties[key]); + + window.pi.addProperty(0, key, value2); + assertEquals(value2, window.pi.properties[key]); + + }; + + // Make sure that the first timestamp that gets sent + // is recorded as the base timestamp. + window.testFirstTimestamp = function() { + var pi = new PlayerInfo('example_ID'); + var timestamp = 5000; + pi.addProperty(timestamp, 'key', 'value'); + + assertEquals(timestamp, pi.firstTimestamp_); + }; + + // Adding a property with a non-string key should + // throw an exception. + window.testWrongKeyType = function() { + var pi = new PlayerInfo('example_ID'); + assertThrows(function() { + pi.addProperty(0, 5, 'some value'); + }); + }; + + // Subsequent events should have their log offset based + // on the first timestamp added. + window.testAddPropertyTimestampOffset = function() { + var firstTimestamp = 500, + secondTimestamp = 550, + deltaT = secondTimestamp - firstTimestamp, + key = 'key', + value = 'value'; + + var pi = new PlayerInfo('example_ID'); + pi.addProperty(firstTimestamp, key, value); + pi.addProperty(secondTimestamp, key, value); + + assertEquals(firstTimestamp, pi.firstTimestamp_); + assertEquals(0, pi.allEvents[0].time); + assertEquals(deltaT, pi.allEvents[1].time); + + assertTrue(undefined !== pi.pastValues[key]); + + console.log(pi.pastValues); + + assertEquals(0, pi.pastValues[key][0].time); + assertEquals(deltaT, pi.pastValues[key][1].time); + }; + + // Check to make sure that properties are correctly + // added to the relevant pastValues array. + window.testAddPropertyPastValues = function() { + var pi = new PlayerInfo('example_ID'), + timestamp = 50, + key = 'key', + value = 'value'; + + pi.addProperty(timestamp, key, value); + + assertEquals(value, pi.pastValues[key][0].value); + assertEquals(key, pi.pastValues[key][0].key); + assertEquals(0, pi.pastValues[key][0].time); + }; + + // The list of all events should be recorded in correctly. + window.testAllEvents = function() { + var pi = new PlayerInfo('example_ID'), + timestamp = 50, + key = 'key', + value = 'value', + key2 = 'key2', + value2 = 'value2'; + + pi.addProperty(timestamp, key, value); + assertEquals(value, pi.allEvents[0].value); + assertEquals(key, pi.allEvents[0].key); + + pi.addProperty(timestamp, key2, value2); + assertEquals(value2, pi.allEvents[1].value); + assertEquals(key2, pi.allEvents[1].key); + }; + + // Using noRecord should make it not show up in allEvents, + // but it should still show up in pastValues[key]. + window.testNoRecord = function() { + var pi = new PlayerInfo('example_ID'), + timestamp = 50, + key = 'key', + value = 'value'; + pi.addPropertyNoRecord(timestamp, key, value); + + assertEquals(value, pi.properties[key]); + assertEquals(0, pi.allEvents.length); + assertEquals(1, pi.pastValues[key].length); + }; + runTests(); + </script> + </body> +</html> diff --git a/content/browser/resources/media/new/player_manager.js b/content/browser/resources/media/new/player_manager.js new file mode 100644 index 0000000..3de9335 --- /dev/null +++ b/content/browser/resources/media/new/player_manager.js @@ -0,0 +1,111 @@ +// Copyright 2013 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. + +/** + * @fileoverview Keeps track of all the existing + * PlayerProperty objects and is the entry-point for messages from the backend. + */ +var PlayerManager = (function() { + 'use strict'; + + function PlayerManager(renderManager) { + this.players_ = {}; + this.renderman_ = renderManager; + renderManager.playerManager = this; + + this.shouldRemovePlayer_ = function() { + // This is only temporary until we get the UI hooked up. + return true; + }; + } + + PlayerManager.prototype = { + + /** + * Adds a player to the list of players to manage. + */ + addPlayer: function(id) { + if (this.players_[id]) { + return; + } + // Make the PlayerProperty and add it to the mapping + this.players_[id] = new PlayerInfo(id); + + this.renderman_.redrawList(); + }, + + /** + * Attempts to remove a player from the UI. + * @param id The ID of the player to remove. + */ + removePlayer: function(id) { + // Look at the check box to see if we should actually + // remove it from the UI + if (this.shouldRemovePlayer_()) { + delete this.players_[id]; + this.renderman_.redrawList(); + } else if (this.players_[id]) { + // Set a property on it to be removed at a later time + this.players_[id].toRemove = true; + } + }, + + /** + * Selects a player and displays it on the UI. + * This method is called from the UI. + * @param id The ID of the player to display. + */ + selectPlayer: function(id) { + if (!this.players_[id]) { + throw new Error('[selectPlayer] Id ' + id + ' does not exist.'); + } + + this.renderman_.select(id); + }, + + updatePlayerInfoNoRecord: function(id, timestamp, key, value) { + if (!this.players_[id]) { + console.error('[updatePlayerInfo] Id ' + id + + ' does not exist'); + return; + } + + this.players_[id].addPropertyNoRecord(timestamp, key, value); + + // If we can potentially rename the player, do so. + if (key === 'name' || key === 'url') { + this.renderman_.redrawList(); + } + + this.renderman_.update(); + }, + + /** + * + * @param id The unique ID that identifies the player to be updated. + * @param timestamp The timestamp of when the change occured. This + * timestamp is *not* normalized. + * @param key The name of the property to be added/changed. + * @param value The value of the property. + */ + updatePlayerInfo: function(id, timestamp, key, value) { + if (!this.players_[id]) { + console.error('[updatePlayerInfo] Id ' + id + + ' does not exist'); + return; + } + + this.players_[id].addProperty(timestamp, key, value); + + // If we can potentially rename the player, do so. + if (key === 'name' || key === 'url') { + this.renderman_.redrawList(); + } + + this.renderman_.update(); + } + }; + + return PlayerManager; +}()); diff --git a/content/browser/resources/media/new/player_manager_test.html b/content/browser/resources/media/new/player_manager_test.html new file mode 100644 index 0000000..eff78b5 --- /dev/null +++ b/content/browser/resources/media/new/player_manager_test.html @@ -0,0 +1,155 @@ +<!-- +Copyright 2013 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. +--> +<!DOCTYPE html> +<html> + <head> + <script src="webui_resource_test.js"></script> + <script src="player_manager.js"></script> + <script src="player_info.js"></script> + </head> + <body> + <script> + var doNothing = function() { + }; + + var emptyRenderMan = { + redrawList: doNothing, + update: doNothing, + select: doNothing + }; + + window.setUp = function() { + window.pm = new PlayerManager(emptyRenderMan); + }; + + window.tearDown = function() { + window.pm = null; + }; + + // Test a normal case of .addPlayer + window.testAddPlayer = function() { + window.pm.addPlayer('someid'); + assertTrue(undefined !== window.pm.players_['someid']); + }; + + // Make sure that adding a player forces a redraw + // on the renderManager. + window.testAddPlayerForceRedraw = function() { + var redrew = false; + var mockRenderManager = { + redrawList: function() { + redrew = true; + } + }; + var pm = new PlayerManager(mockRenderManager); + + pm.addPlayer('someid'); + assertTrue(redrew); + }; + + // On occasion, the backend will add an existing ID multiple times. + // make sure this doesn't break anything. + window.testAddPlayerAlreadyExisting = function() { + window.pm.addPlayer('someid'); + window.pm.addPlayer('someid'); + assertTrue(undefined !== window.pm.players_['someid']); + }; + + // If the removal is set, make sure that a player + // gets removed from the PlayerManager. + window.testRemovePlayerShouldRemove = function() { + // Because we don't have the checkbox. + window.pm.shouldRemovePlayer_ = function() { + return true; + }; + window.pm.addPlayer('someid'); + assertTrue(undefined !== window.pm.players_['someid']); + window.pm.removePlayer('someid'); + assertTrue(undefined === window.pm.players_['someid']); + }; + + // On the removal of a player, the renderer should be forced + // to redraw the list. + window.testRemovePlayerRedraw = function() { + var redrew = false; + + var fakeObj = { + redrawList: function() { + redrew = true; + } + }; + + var pm = new PlayerManager(fakeObj); + // Because we don't have the checkbox; + pm.shouldRemovePlayer_ = function() { + return true; + }; + + + pm.addPlayer('someid'); + assertTrue(undefined !== pm.players_['someid']); + pm.removePlayer('someid'); + assertTrue(undefined === pm.players_['someid']); + + assertTrue(redrew); + }; + + // If you shouldn't remove the player, the player shouldn't be + // removed. + window.testRemovePlayerNoRemove = function() { + window.pm = new PlayerManager(emptyRenderMan); + // Because we don't have the checkbox; + window.pm.shouldRemovePlayer_ = function() { + return false; + }; + window.pm.addPlayer('someid'); + assertTrue(undefined !== window.pm.players_['someid']); + window.pm.removePlayer('someid'); + assertTrue(undefined !== window.pm.players_['someid']); + }; + + + // Removing a nonexistant player shouldn't break anything + // The backend also occasionally does this. + window.testRemovePlayerNonExistant = function() { + // Because we don't have the checkbox; + window.pm.shouldRemovePlayer_ = function() { + return false; + }; + window.pm.removePlayer('someid'); + assertTrue(undefined === window.pm.players_['someid']); + }; + + // Trying to select a non-existant player should throw + // an exception + window.testSelectNonExistant = function() { + assertThrows(function() { + window.pm.selectPlayer('someId'); + }); + }; + + // Selecting an existing player should trigger a redraw + window.testSelectExistingPlayer = function() { + var selected = false; + var redrew = false; + var pm = new PlayerManager({ + select: function() { + selected = true; + }, + redrawList: function() { + redrew = true; + } + }); + pm.addPlayer('someId'); + pm.selectPlayer('someId'); + + assertTrue(selected); + assertTrue(redrew); + }; + runTests(); + </script> + </body> +</html> diff --git a/content/browser/resources/media/new/webui_resource_test.js b/content/browser/resources/media/new/webui_resource_test.js new file mode 100644 index 0000000..6b05a30 --- /dev/null +++ b/content/browser/resources/media/new/webui_resource_test.js @@ -0,0 +1,210 @@ +// Copyright 2013 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. + +/** + * Tests that an observation matches the expected value. + * @param {Object} expected The expected value. + * @param {Object} observed The actual value. + * @param {string=} opt_message Optional message to include with a test + * failure. + */ +function assertEquals(expected, observed, opt_message) { + if (observed !== expected) { + var message = 'Assertion Failed\n Observed: ' + observed + + '\n Expected: ' + expected; + if (opt_message) + message = message + '\n ' + opt_message; + throw new Error(message); + } +} + +/** + * Verifies that a test result is true. + * @param {boolean} observed The observed value. + * @param {string=} opt_message Optional message to include with a test + * failure. + */ +function assertTrue(observed, opt_message) { + assertEquals(true, observed, opt_message); +} + +/** + * Verifies that a test result is false. + * @param {boolean} observed The observed value. + * @param {string=} opt_message Optional message to include with a test + * failure. + */ +function assertFalse(observed, opt_message) { + assertEquals(false, observed, opt_message); +} + +/** + * Verifies that the observed and reference values differ. + * @param {Object} reference The target value for comparison. + * @param {Object} observed The test result. + * @param {string=} opt_message Optional message to include with a test + * failure. + */ +function assertNotEqual(reference, observed, opt_message) { + if (observed === reference) { + var message = 'Assertion Failed\n Observed: ' + observed + + '\n Reference: ' + reference; + if (opt_message) + message = message + '\n ' + opt_message; + throw new Error(message); + } +} + +/** + * Verifies that a test evaluation results in an exception. + * @param {!Function} f The test function. + */ +function assertThrows(f) { + var triggeredError = false; + try { + f(); + } catch (err) { + triggeredError = true; + } + if (!triggeredError) + throw new Error('Assertion Failed: throw expected.'); +} + +/** + * Verifies that the contents of the expected and observed arrays match. + * @param {!Array} expected The expected result. + * @param {!Array} observed The actual result. + */ +function assertArrayEquals(expected, observed) { + var v1 = Array.prototype.slice.call(expected); + var v2 = Array.prototype.slice.call(observed); + var equal = v1.length == v2.length; + if (equal) { + for (var i = 0; i < v1.length; i++) { + if (v1[i] !== v2[i]) { + equal = false; + break; + } + } + } + if (!equal) { + var message = + ['Assertion Failed', 'Observed: ' + v2, 'Expected: ' + v1].join('\n '); + throw new Error(message); + } +} + +/** + * Verifies that the expected and observed result have the same content. + * @param {*} expected The expected result. + * @param {*} observed The actual result. + */ +function assertDeepEquals(expected, observed, opt_message) { + if (typeof expected == 'object' && expected != null) { + assertNotEqual(null, observed); + for (var key in expected) { + assertTrue(key in observed, opt_message); + assertDeepEquals(expected[key], observed[key], opt_message); + } + for (var key in observed) { + assertTrue(key in expected, opt_message); + } + } else { + assertEquals(expected, observed, opt_message); + } +} + +/** + * Defines runTests. + */ +(function(exports) { + /** + * List of test cases. + * @type {Array.<string>} List of function names for tests to run. + */ + var testCases = []; + + /** + * Indicates if all tests have run successfully. + * @type {boolean} + */ + var cleanTestRun = true; + + /** + * Armed during setup of a test to call the matching tear down code. + * @type {Function} + */ + var pendingTearDown = null; + + /** + * Runs all functions starting with test and reports success or + * failure of the test suite. + */ + function runTests() { + for (var name in window) { + if (typeof window[name] == 'function' && /^test/.test(name)) + testCases.push(name); + } + if (!testCases.length) { + console.error('Failed to find test cases.'); + cleanTestRun = false; + } + continueTesting(); + } + + function reportPass(name) { + 'use strict'; + var text = document.createTextNode(name + ': PASSED'); + var span = document.createElement('span'); + span.appendChild(text); + document.body.appendChild(span); + document.body.appendChild(document.createElement('br')); + } + + function reportFail(name) { + 'use strict'; + var text = document.createTextNode(name + ': =========FAILED======='); + var span = document.createElement('span'); + span.appendChild(text); + document.body.appendChild(span); + document.body.appendChild(document.createElement('br')); + } + + /** + * Runs the next test in the queue. Reports the test results if the queue is + * empty. + */ + function continueTesting() { + if (pendingTearDown) { + pendingTearDown(); + pendingTearDown = null; + } + if (testCases.length > 0) { + var fn = testCases.pop(); + var isAsyncTest = window[fn].length; + try { + if (window.setUp) + window.setUp(); + pendingTearDown = window.tearDown; + window[fn](continueTesting); + reportPass(fn); + } catch (err) { + reportFail(fn); + console.error('Failure in test ' + fn + '\n' + err); + console.log(err.stack); + cleanTestRun = false; + } + // Asynchronous tests must manually call continueTesting when complete. + if (!isAsyncTest) + continueTesting(); + } + if (testCases.length) { + domAutomationController.setAutomationId(1); + domAutomationController.send('PENDING'); + } + }; + + exports.runTests = runTests; +})(this); + |