power_LoadTest: Add parameter for custom tasks

Added a parameter, 'tasks', to power_LoadTest's control file that can
override the default tasks in extension/urls.js with a custom set of
tasks.

Also fixed the way URL cycling delays are scaled in background.html.
Previously, they were not scaled along with start and duration, based on
time_ratio.

Also created functions to convert time into milliseconds, to clean
up code.

BUG=chromium-os:13437
TEST=Run the example in the control file.  Set loop_time to something
low, like 120 seconds, and loop_count to 1, so the test doesn't run for
an hour.

Change-Id: I8187341934ceb1944f5c5cdb2ad9ddc64b9169fc
Signed-off-by: Simon Que <sque@chromium.org>

Review URL: http://codereview.chromium.org/6675052
diff --git a/client/site_tests/power_LoadTest/control b/client/site_tests/power_LoadTest/control
index 4b05c1e..3ef1245 100755
--- a/client/site_tests/power_LoadTest/control
+++ b/client/site_tests/power_LoadTest/control
@@ -32,5 +32,29 @@
 loop_time = 3600
 loop_count = 11
 
+# To specify a list of custom tasks, pass in the task parameter:
+#    tasks='<task0> + <task1> + <task2> + ...'
+#
+# Task format:
+# - Open a window with some tabs, keep them open for some time:
+#       window;<comma-delimited urls>;<duration>
+# - Cycle through a list of sites one at a time, for a total duration, and a
+#   given delay on each site.
+#       cycle;<comma-delimited urls>;<duration>;<delay>
+#
+# Duration and delay are given in seconds.  The tasks are sequential.  So if
+# task0.duration=1800 and task1.duration=1200, task0 will run for 30 minutes,
+# followed by task1 for 20 minutes, for a total of 50 minutes.
+#
+# Example: Use the 'tasks' string below.  Pass 'tasks=tasks' as an argument into
+#          job.run_test().
+
+tasks='cycle;http://www.facebook.com,http://www.google.com,http://www.yahoo' + \
+      '.com;900;60 +' + \
+      'window;http://www.bing.com,http://www.gmail.com,http://www.amazon.co' + \
+      'm,http://www.ask.com;600 +' + \
+      'cycle;http://www.youtube.com,http://www.microsoft.com;900;240 +' + \
+      'window;http://www.wikipedia.org;1200'
+
 job.run_test('power_LoadTest', loop_time=loop_time, loop_count=loop_count,
               low_battery_threshold=3)
diff --git a/client/site_tests/power_LoadTest/extension.crx b/client/site_tests/power_LoadTest/extension.crx
index 61b7a6e..1c56257 100644
--- a/client/site_tests/power_LoadTest/extension.crx
+++ b/client/site_tests/power_LoadTest/extension.crx
Binary files differ
diff --git a/client/site_tests/power_LoadTest/extension/background.html b/client/site_tests/power_LoadTest/extension/background.html
index e1316f9..9a89b40 100755
--- a/client/site_tests/power_LoadTest/extension/background.html
+++ b/client/site_tests/power_LoadTest/extension/background.html
@@ -5,6 +5,19 @@
 --->
 
 <html>
+
+<script>
+// Convert seconds to milliseconds
+function seconds(s) {
+    return s * 1000;
+}
+
+// Convert minutes to milliseconds
+function minutes(m) {
+    return seconds(m * 60);
+}
+</script>
+
 <script src='/urls.js'>
 </script>
 
@@ -48,6 +61,46 @@
   }
 }
 
+function parseTaskList(tasks_string) {
+  if (tasks_string == '')
+    return [];
+  var task_strings = tasks_string.split('+');
+  var task_list = [];
+  var time = 0;
+
+  // Parse each task.
+  for (var i in task_strings) {
+    // Extract task parameters.
+    var params = task_strings[i].split(';');
+    var cmd = params[0];
+    var urls = params[1].split(',');
+    var duration = seconds(parseInt(params[2]));
+    if (params.length > 3)
+      var delay = seconds(parseInt(params[3]));
+
+    if (cmd == 'window') {
+      task_list.push( { type: 'window',
+                        start: time,
+                        duration: duration,
+                        focus: true,
+                        tabs: urls } );
+    }
+    else if (cmd == 'cycle') {
+      task_list.push( { type: 'cycle',
+                        start: time,
+                        duration: duration,
+                        delay: delay,
+                        timeout: seconds(10),
+                        focus: true,
+                        urls: urls } );
+    }
+    // Increment the time to determine the start time of the next task.
+    time += duration;
+  }
+  return task_list;
+}
+
+var task_list = [];
 
 chrome.extension.onRequest.addListener(
   function paramsSetupListener(request, sender) {
@@ -56,7 +109,8 @@
         undefined != request._should_scroll_up &&
         undefined != request._scroll_loop &&
         undefined != request._scroll_interval_ms &&
-        undefined != request._scroll_by_pixels) {
+        undefined != request._scroll_by_pixels &&
+        undefined != request._tasks) {
       // Update test parameters from content script.
       test_time_ms = request._test_time_ms;
       should_scroll = request._should_scroll;
@@ -64,6 +118,9 @@
       scroll_loop = request._scroll_loop;
       scroll_interval_ms = request._scroll_interval_ms;
       scroll_by_pixels = request._scroll_by_pixels;
+      task_list = parseTaskList(request._tasks);
+      if (task_list.length != 0)
+        tasks = task_list;
       time_ratio = 3600 * 1000 / test_time_ms; // default test time is 1 hour
       chrome.extension.onRequest.removeListener(paramsSetupListener);
       chrome.extension.onRequest.addListener(testListener);
@@ -88,10 +145,10 @@
   var url = cycle.urls[cycle.idx];
   chrome.tabs.update(cycle.id, {'url': url, 'selected': true});
   cycle.idx = (cycle.idx + 1) % cycle.urls.length;
-  if (cycle.timeout < cycle.delay && cycle.timeout > 0) {
+  if (cycle.timeout < cycle.delay / time_ratio && cycle.timeout > 0) {
     cycle.timer = setTimeout(cycle_check_timeout, cycle.timeout, cycle);
   } else {
-    cycle.timer = setTimeout(cycle_navigate, cycle.delay, cycle);
+    cycle.timer = setTimeout(cycle_navigate, cycle.delay / time_ratio, cycle);
   }
 }
 
@@ -100,7 +157,8 @@
     cycle.failed_loads++;
     cycle_navigate(cycle);
   } else {
-    cycle.timer = setTimeout(cycle_navigate, cycle.delay - cycle.timeout,
+    cycle.timer = setTimeout(cycle_navigate,
+                             cycle.delay / time_ratio - cycle.timeout,
                              cycle);
   }
 }
diff --git a/client/site_tests/power_LoadTest/extension/params.js b/client/site_tests/power_LoadTest/extension/params.js
index dca9487..9df4d07 100644
--- a/client/site_tests/power_LoadTest/extension/params.js
+++ b/client/site_tests/power_LoadTest/extension/params.js
@@ -2,8 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-var test_time_ms = 60 * 60 * 1000;
-var test_startup_delay = 5 * 1000;
+// Convert seconds to milliseconds
+function seconds(s) {
+    return s * 1000;
+}
+
+// Convert minutes to milliseconds
+function minutes(m) {
+    return seconds(m * 60);
+}
+
+var test_time_ms = minutes(60);
+var test_startup_delay = seconds(5);
 var should_scroll = true;
 var should_scroll_up = true;
 var scroll_loop = false;
diff --git a/client/site_tests/power_LoadTest/extension/testparams.js b/client/site_tests/power_LoadTest/extension/testparams.js
index 65ee8c5..c472e9e 100644
--- a/client/site_tests/power_LoadTest/extension/testparams.js
+++ b/client/site_tests/power_LoadTest/extension/testparams.js
@@ -11,6 +11,7 @@
 var scroll_loop = false;
 var scroll_interval_ms = 0;
 var scroll_by_pixels = 0;
+var tasks = "";
 
 document.getElementById('myCustomEventDiv').addEventListener('myCustomEvent',
   function() {
@@ -22,6 +23,7 @@
     scroll_loop = document.getElementById('scroll_loop').innerText;
     scroll_interval_ms = document.getElementById('scroll_interval_ms').innerText;
     scroll_by_pixels = document.getElementById('scroll_by_pixels').innerText;
+    tasks = document.getElementById('tasks').innerText;
 
     // pass to background page via sendRequest.
     var request = { _test_time_ms : test_time_ms,
@@ -29,7 +31,8 @@
                     _should_scroll_up : should_scroll_up,
                     _scroll_loop : scroll_loop,
                     _scroll_interval_ms : scroll_interval_ms,
-                    _scroll_by_pixels : scroll_by_pixels
+                    _scroll_by_pixels : scroll_by_pixels,
+                    _tasks : tasks
                   }
     chrome.extension.sendRequest(request);
   }
diff --git a/client/site_tests/power_LoadTest/extension/urls.js b/client/site_tests/power_LoadTest/extension/urls.js
index 53868bf..4d1f5f6 100755
--- a/client/site_tests/power_LoadTest/extension/urls.js
+++ b/client/site_tests/power_LoadTest/extension/urls.js
@@ -11,9 +11,10 @@
 var tasks = [
   {
     // Chrome browser window 1. This window remains open for the entire test.
-    'type': 'window',
+    type: 'window',
+    name: 'background',
     start: 0,
-    duration: 60 * 60 * 1000,
+    duration: minutes(2),
     tabs: [
      'http://www.cnn.com',
      'http://news.google.com',
@@ -27,9 +28,9 @@
     type: 'cycle',
     name: 'web',
     start: 0,
-    duration: 36 * 60 * 1000,
-    delay: 60 * 1000, // A minute on each page
-    timeout: 10 * 1000,
+    duration: minutes(36),
+    delay: seconds(60), // A minute on each page
+    timeout: seconds(10),
     focus: true,
     urls: URLS,
   },
@@ -37,10 +38,10 @@
     // After 36 minutes, actively read e-mail for 12 minutes
     type: 'cycle',
     name: 'email',
-    start: 36 * 60 * 1000 + 1 * 1000,
-    duration: 12 * 60 * 1000 - 1 * 1000,
-    delay: 5 * 60 * 1000, // 5 minutes between full gmail refresh
-    timeout: 10 * 1000,
+    start: minutes(36) + seconds(1),
+    duration: minutes(12) - seconds(1),
+    delay: minutes(5), // 5 minutes between full gmail refresh
+    timeout: seconds(10),
     focus: true,
     urls: [
        'http://gmail.com',
@@ -52,10 +53,10 @@
     // 12 minutes
     type: 'cycle',
     name: 'audio',
-    start: 36 * 60 * 1000,
-    duration: 12 * 60 * 1000,
-    delay: 12 * 60 * 1000,
-    timeout: 10 * 1000,
+    start: minutes(36),
+    duration: minutes(12),
+    delay: minutes(12),
+    timeout: seconds(10),
     focus: false,
     urls: [
       'http://www.bbc.co.uk/worldservice/audioconsole/?stream=live',
@@ -67,10 +68,10 @@
     // After 48 minutes, play with Google Docs for 6 minutes
     type: 'cycle',
     name: 'docs',
-    start: 48 * 60 * 1000,
-    duration: 6 * 60 * 1000,
-    delay: 60 * 1000, // A minute on each page
-    timeout: 10 * 1000,
+    start: minutes(48),
+    duration: minutes(6),
+    delay: minutes(1), // A minute on each page
+    timeout: seconds(10),
     focus: true,
     urls: [
        ViewGDoc + '0AaLGACl774zLZGRuYzlibWtfMXJzbmdoamcy',
@@ -81,8 +82,8 @@
     // After 54 minutes, watch Google IO for 6 minutes
     type: 'window',
     name: 'video',
-    start: 54 * 60 * 1000,
-    duration: 6 * 60 * 1000,
+    start: minutes(54),
+    duration: minutes(6),
     focus: true,
     tabs: [
         'http://www.youtube.com/v/ecI_hCBGEIM&autoplay=1'
diff --git a/client/site_tests/power_LoadTest/params.js b/client/site_tests/power_LoadTest/params.js
index 14e1ee0..40cedfc 100644
--- a/client/site_tests/power_LoadTest/params.js
+++ b/client/site_tests/power_LoadTest/params.js
@@ -8,3 +8,4 @@
 var scroll_loop = false;
 var scroll_interval_ms = 1000;
 var scroll_by_pixels = 600;
+var tasks = "";
diff --git a/client/site_tests/power_LoadTest/power_LoadTest.py b/client/site_tests/power_LoadTest/power_LoadTest.py
index 3694f1f..02226e4 100755
--- a/client/site_tests/power_LoadTest/power_LoadTest.py
+++ b/client/site_tests/power_LoadTest/power_LoadTest.py
@@ -17,6 +17,7 @@
     'scroll_loop': '_scroll_loop',
     'scroll_interval_ms': '_scroll_interval_ms',
     'scroll_by_pixels': '_scroll_by_pixels',
+    'tasks': '_tasks',
 }
 
 
@@ -49,7 +50,7 @@
                  scroll_loop='false', scroll_interval_ms='10000',
                  scroll_by_pixels='600', low_battery_threshold=3,
                  verbose=True, force_wifi=False, wifi_ap='', wifi_sec='none',
-                 wifi_pw=''):
+                 wifi_pw='', tasks=""):
 
         """
         percent_initial_charge_min: min battery charge at start of test
@@ -77,6 +78,7 @@
         self._json_path = None
         self._force_wifi = force_wifi
         self._testServer = None
+        self._tasks = '\'' + tasks + '\''
 
         # verify that initial conditions are met:
         if self._power_status.linepower[0].online:
diff --git a/client/site_tests/power_LoadTest/testparams.html b/client/site_tests/power_LoadTest/testparams.html
index 6f89d8d..4b736d7 100644
--- a/client/site_tests/power_LoadTest/testparams.html
+++ b/client/site_tests/power_LoadTest/testparams.html
@@ -35,6 +35,7 @@
 createDiv('scroll_loop', scroll_loop, false);
 createDiv('scroll_interval_ms', scroll_interval_ms, false);
 createDiv('scroll_by_pixels', scroll_by_pixels, false);
+createDiv('tasks', tasks, false);
 createDiv('myCustomEventDiv', data, true);