[ct] Most of chromium-perf-runs-sk

Change-Id: Ic418fcbf7b8313d1a018c1003ca9764d1f308b4c
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/306066
Reviewed-by: Ravi Mistry <rmistry@google.com>
diff --git a/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk-demo.html b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk-demo.html
new file mode 100644
index 0000000..c067793
--- /dev/null
+++ b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk-demo.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Chromium Perf Runs</title>
+    <meta charset="utf-8"/>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  </head>
+  <body>
+    <h2>Chromium-perf-runs-sk Demo</h2><theme-chooser-sk></theme-chooser-sk>
+    <div id=container></div>
+  </body>
+</html>
diff --git a/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk-demo.js b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk-demo.js
new file mode 100644
index 0000000..af4b2c1
--- /dev/null
+++ b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk-demo.js
@@ -0,0 +1,15 @@
+import './index';
+import '../../../infra-sk/modules/theme-chooser-sk';
+import { $$ } from 'common-sk/modules/dom';
+import { fetchMock } from 'fetch-mock';
+import {
+  tasksResult0, tasksResult1, tasksResult2,
+} from './test_data';
+
+let i = 0;
+fetchMock.post('begin:/_/get_chromium_perf_tasks',
+  () => [tasksResult0, tasksResult1, tasksResult2][i++ % 3]);
+fetchMock.post('begin:/_/delete_chromium_perf_task', 200);
+fetchMock.post('begin:/_/redo_chromium_perf_task', 200);
+const cpr = document.createElement('chromium-perf-runs-sk');
+$$('#container').appendChild(cpr);
diff --git a/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk.js b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk.js
new file mode 100644
index 0000000..0c6f755
--- /dev/null
+++ b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk.js
@@ -0,0 +1,422 @@
+/**
+ * @fileoverview The bulk of the Chromium Perf Runs History page.
+ */
+
+import 'elements-sk/icon/delete-icon-sk';
+import 'elements-sk/icon/redo-icon-sk';
+import 'elements-sk/icon/cancel-icon-sk';
+import 'elements-sk/icon/check-circle-icon-sk';
+import 'elements-sk/icon/help-icon-sk';
+import 'elements-sk/toast-sk';
+import '../../../infra-sk/modules/confirm-dialog-sk';
+import '../pagination-sk';
+
+import { $$, DomReady } from 'common-sk/modules/dom';
+import { fromObject } from 'common-sk/modules/query';
+import { jsonOrThrow } from 'common-sk/modules/jsonOrThrow';
+import { define } from 'elements-sk/define';
+import { errorMessage } from 'elements-sk/errorMessage';
+import { html } from 'lit-html';
+
+import { ElementSk } from '../../../infra-sk/modules/ElementSk';
+import {
+  getFormattedTimestamp, getGSLink, isEmptyPatch, formatRepeatAfterDays,
+} from '../ctfe_utils';
+
+const template = (el) => html`
+<div>
+  <h2>${el._contrainByUser ? 'My ' : ''}Chromium Perf Runs</h2>
+  <pagination-sk @page-changed=${(e) => el._pageChanged(e)}></pagination-sk>
+  <br/>
+  <button id=userFilter @click=${() => el._constrainRunsByUser()}>
+    ${el._constrainByUser ? 'View Everyone\'s Runs' : 'View Only My Runs'}
+  </button>
+  <button id=testFilter @click=${() => el._constrainRunsByTest()}>
+    ${el._constrainByTest ? 'Include Test Run' : 'Exclude Test Runs'}
+  </button>
+
+  <br/>
+  <br/>
+  <table class="surface-themes-sk secondary-links runssummary" id=runssummary>
+    <tr class=primary-variant-container-themes-sk>
+      <th>Id</th>
+      <th>User</th>
+      <th>Timestamps</th>
+      <th>Task Config</th>
+      <th>Description</th>
+      <th>Results</th>
+      <th>Arguments</th>
+      <th>Patches</th>
+      <th>Task Repeats</th>
+    </tr>
+    ${el._tasks.map((task, index) => taskRowTemplate(el, task, index))}
+  </table>
+</div>
+
+${el._tasks.map((task, index) => taskDialogTemplate(task, index))}
+<confirm-dialog-sk id=confirm_dialog></confirm-dialog-sk>
+<toast-sk id=confirm_toast class=primary-variant-container-themes-sk duration=5000></toast-sk>
+`;
+
+const taskRowTemplate = (el, task, index) => html`
+<tr>
+  <!-- Id col -->
+  <td class=nowrap>
+    ${task.Results
+    ? html`<a href="${task.Results}" target=_blank rel="noopener noreferrer">${task.Id}</a>`
+    : html`<span>${task.Id}</span>`}
+    <delete-icon-sk title="Delete this task" alt=Delete ?hidden=${!task.canDelete}
+      @click=${() => el._confirmDeleteTask(index)}></delete-icon-sk>
+    <redo-icon-sk title="Redo this task" alt=Redo ?hidden=${!task.canRedo}
+      @click=${() => el._confirmRedoTask(index)}></redo-icon-sk>
+  </td>
+  <!-- User col -->
+  <td>${task.Username}</td>
+  <!-- Timestamps col -->
+  <td>
+    <table class=inner-table>
+      <tr>
+        <td>Added:</td>
+        <td class=nowrap>${getFormattedTimestamp(task.TsAdded)}</td>
+      </tr>
+      <tr>
+        <td>Started:</td>
+        <td class=nowrap>${getFormattedTimestamp(task.TsStarted)}</td>
+      </tr>
+      <tr>
+        <td>Completed:</td>
+        <td class=nowrap>${getFormattedTimestamp(task.TsCompleted)}</td>
+      </tr>
+    </table>
+  </td>
+  <!-- Task Config col -->
+  <td>
+    <table class=inner-table>
+      <tr>
+        <td>Benchmark:</td>
+        <td>${task.Benchmark}</td>
+      </tr>
+      <tr>
+        <td>Platform:</td>
+        <td>${task.Platform}</td>
+      </tr>
+      <tr>
+        <td>PageSet:</td>
+        <td>
+          ${!isEmptyPatch(task.CustomWebpagesGSPath)
+    ? html`<a href="${getGSLink(task.CustomWebpagesGSPath)}"
+              target=_blank rel="noopener noreferrer">$Custom Webpages</a>`
+    : task.PageSets}
+        </td>
+      </tr>
+      <tr>
+        <td>Repeats:</td>
+        <td>${task.RepeatRuns}</td>
+      </tr>
+      <tr>
+        <td>ParallelRun:</td>
+        <td>${task.RunInParallel}</td>
+      </tr>
+      ${task.ValueColumnName
+    ? html`<tr>
+          <td class=nowrap>Value Column:</td>
+          <td class=nowrap>${task.ValueColumnName}</td>
+          </tr>`
+    : ''}
+      ${task.TaskPriority
+    ? html`<tr>
+          <td>TaskPriority:</td>
+          <td>${task.TaskPriority}</td>
+        </tr>`
+    : ''}
+      ${task.CCList
+    ? html`<tr>
+          <td>CC List:</td>
+          <td>${task.CCList}</td>
+        </tr>`
+    : ''}
+      ${task.GroupName
+    ? html`<tr>
+          <td>GroupName:</td>
+          <td><a href="https://ct-perf.skia.org/e/?request_type=1">${task.GroupName}</a></td>
+        </tr>`
+    : ''}
+      ${task.ChromiumHash
+    ? html`<tr>
+          <td>ChromiumHash:</td>
+          <td><a href="https://chromium.googlesource.com/chromium/src/+show/${task.ChromiumHash}">${task.ChromiumHash}</a></td>
+        </tr>`
+    : ''}
+    </table>
+  </td>
+
+  <!-- Description col -->
+  <td>${task.Description}</td>
+
+  <!-- Results col -->
+  <td class=nowrap>
+    ${task.Failure ? '<div class=error>Failed</div>' : ''}
+    ${!task.TaskDone ? '<div class=green>Waiting</div>' : ''}
+    ${task.Results
+    ? html`<a href="${task.Results}" target=_blank rel="noopener noreferrer">
+        Overall Result
+      </a>
+      <br/>
+      <a href="${task.NoPatchRawOutput}" target=_blank rel="noopener noreferrer">
+        NoPatch Raw Output
+      </a>
+      <br/>
+      <a href="${task.WithPatchRawOutput}" target=_blank rel="noopener noreferrer">
+        WithPatch Raw Output
+      </a>`
+    : ''}
+    ${task.SwarmingLogs
+    ? html`<br/>
+      <a href="${task.SwarmingLogs}" target=_blank rel="noopener noreferrer">Swarming Logs</a>`
+    : ''}
+  </td>
+
+  <!-- Arguments -->
+  <td class=nowrap>
+    ${task.BenchmarkArgs
+    ? html`<a href="javascript:;" class=details
+        @click=${() => el._showDialog('benchmarkArgs', index)}>
+        Benchmark Args
+      </a>
+      <br/>`
+    : ''}
+    ${task.BrowserArgsNoPatch
+    ? html`<a href="javascript:;" class=details
+        @click=${() => el._showDialog('browserArgsNoPatch', index)}>
+        NoPatch Browser Args
+      </a>
+      <br/>`
+    : ''}
+    ${task.BrowserArgsWithPatch
+    ? html`<a href="javascript:;" class=details
+        @click=${() => el._showDialog('browserArgsWithPatch', index)}>
+        WithPatch Browser Args
+      </a>
+      <br/>`
+    : ''}
+  </td>
+
+  <!-- Patches -->
+  <td>
+    ${!isEmptyPatch(task.ChromiumPatchGSPath)
+    ? html`<a href="${getGSLink(task.ChromiumPatchGSPath)}"
+      target="_blank" rel="noopener noreferrer">Chromium</a>
+      <br/>
+      `
+    : ''}
+    ${!isEmptyPatch(task.BlinkPatchGSPath)
+    ? html`<a href="${getGSLink(task.BlinkPatchGSPath)}"
+      target="_blank" rel="noopener noreferrer">Blink</a>
+      <br/>
+      `
+    : ''}
+    ${!isEmptyPatch(task.SkiaPatchGSPath)
+    ? html`<a href="${getGSLink(task.SkiaPatchGSPath)}"
+      target="_blank" rel="noopener noreferrer">Skia</a>
+      <br/>
+      `
+    : ''}
+    ${!isEmptyPatch(task.V8PatchGSPath)
+    ? html`<a href="${getGSLink(task.V8PatchGSPath)}"
+      target="_blank" rel="noopener noreferrer">V8</a>
+      <br/>
+      `
+    : ''}
+    ${!isEmptyPatch(task.CatapultPatchGSPath)
+    ? html`<a href="${getGSLink(task.CatapultPatchGSPath)}"
+      target="_blank" rel="noopener noreferrer">Catapult</a>
+      <br/>
+      `
+    : ''}
+    ${!isEmptyPatch(task.BenchmarkPatchGSPath)
+    ? html`<a href="${getGSLink(task.BenchmarkPatchGSPath)}"
+      target="_blank" rel="noopener noreferrer">Telemetry</a>
+      <br/>
+      `
+    : ''}
+    ${!isEmptyPatch(task.ChromiumPatchBaseBuildGSPath)
+    ? html`<a href="${getGSLink(task.ChromiumPatchBaseBuildGSPath)}"
+      target="_blank" rel="noopener noreferrer">Chromium(base_build)</a>
+      <br/>
+      `
+    : ''}
+  </td>
+
+  <!-- Task Repeats -->
+  <td>${formatRepeatAfterDays(task.RepeatAfterDays)}</td>
+</tr>`;
+
+
+const taskDialogTemplate = (task, index) => html`
+<div id=${`benchmarkArgs${index}`} class="dialog-background hidden overlay-themes-sk"
+  @click=${hideDialog}>
+  <div class="dialog-content surface-themes-sk">
+    <pre>${task.BenchmarkArgs}</pre>
+  </div>
+</div>
+<div id=${`browserArgsNoPatch${index}`} class="dialog-background hidden overlay-themes-sk"
+  @click=${hideDialog}>
+  <div class="dialog-content surface-themes-sk">
+    <pre>${task.BrowserArgsNoPatch}</pre>
+  </div>
+</div>
+<div id=${`browserArgsWithPatch${index}`} class="dialog-background hidden overlay-themes-sk"
+  @click=${hideDialog}>
+  <div class="dialog-content surface-themes-sk">
+    <pre>${task.BrowserArgsWithPatch}</pre>
+  </div>
+</div>
+`;
+
+const hideDialog = (e) => {
+  if (e.target.classList.contains('dialog-background')) {
+    e.target.classList.add('hidden');
+  }
+};
+
+define('chromium-perf-runs-sk', class extends ElementSk {
+  constructor() {
+    super(template);
+    this._tasks = [];
+    this._constrainByUser = false;
+    this._constrainByTest = true;
+    this._resetPagination();
+    this._running = false;
+  }
+
+  connectedCallback() {
+    super.connectedCallback();
+    if (this._running) {
+      return;
+    }
+    this._running = true;
+    // We wait for everything to load so scaffolding event handlers are
+    // attached.
+    DomReady.then(() => {
+      this._render();
+      this._reload().then(() => {
+        this._running = false;
+      });
+    });
+  }
+
+  _showDialog(type, index) {
+    $$(`#${type}${index}`, this).classList.remove('hidden');
+  }
+
+  _pageChanged(e) {
+    this._pagination.offset = e.detail.offset;
+    this._reload();
+  }
+
+  _reload() {
+    this.dispatchEvent(new CustomEvent('begin-task', { bubbles: true }));
+    this._tasks = [];
+    const queryParams = {
+      offset: this._pagination.offset,
+      size: this._pagination.size,
+    };
+    if (this._constrainByUser) {
+      queryParams.filter_by_logged_in_user = true;
+    }
+    if (this._constrainByTest) {
+      queryParams.exclude_dummy_page_sets = true;
+    }
+    return fetch(`/_/get_chromium_perf_tasks?${fromObject(queryParams)}`,
+      { method: 'POST' })
+      .then(jsonOrThrow)
+      .then((json) => {
+        this._tasks = json.data;
+        this._pagination = json.pagination;
+        $$('pagination-sk', this).pagination = this._pagination;
+        for (let i = 0; i < this._tasks.length; i++) {
+          this._tasks[i].canDelete = json.permissions[i].DeleteAllowed;
+          this._tasks[i].canRedo = json.permissions[i].RedoAllowed;
+          this._tasks[i].Id = json.ids[i];
+        }
+      })
+      .catch(errorMessage)
+      .finally(() => {
+        this._render();
+        this.dispatchEvent(new CustomEvent('end-task', { bubbles: true }));
+      });
+  }
+
+  _confirmDeleteTask(index) {
+    document.getElementById('confirm_dialog')
+      .open('Proceed with deleting task?')
+      .then(() => {
+        this._deleteTask(index);
+      })
+      .catch(() => {});
+  }
+
+  _confirmRedoTask(index) {
+    document.getElementById('confirm_dialog')
+      .open('Reschedule this task?')
+      .then(() => {
+        this._redoTask(index);
+      })
+      .catch(() => {});
+  }
+
+  _deleteTask(index) {
+    const params = {};
+    params.id = this._tasks[index].Id;
+    fetch('/_/delete_chromium_perf_task', { method: 'POST', body: JSON.stringify(params) })
+      .then((res) => {
+        if (res.ok) {
+          $$('#confirm_toast').innerText = `Deleted task ${params.id}`;
+          $$('#confirm_toast').show();
+          return;
+        }
+        // Non-OK status. Read the response and punt it to the catch.
+        res.text().then((text) => { throw `Failed to delete the task: ${text}`; });
+      })
+      .then(() => {
+        this._reload();
+      })
+      .catch(errorMessage);
+  }
+
+  _redoTask(index) {
+    const params = {};
+    params.id = this._tasks[index].Id;
+    fetch('/_/redo_chromium_perf_task', { method: 'POST', body: JSON.stringify(params) })
+      .then((res) => {
+        if (res.ok) {
+          $$('#confirm_toast').innerText = `Resubmitted task ${params.id}`;
+          $$('#confirm_toast').show();
+          return;
+        }
+        // Non-OK status. Read the response and punt it to the catch.
+        res.text().then((text) => { throw `Failed to resubmit the task: ${text}`; });
+      })
+      .then(() => {
+        this._reload();
+      })
+      .catch(errorMessage);
+  }
+
+
+  _resetPagination() {
+    this._pagination = { offset: 0, size: 10 };
+  }
+
+  _constrainRunsByUser() {
+    this._constrainByUser = !this._constrainByUser;
+    this._resetPagination();
+    this._reload();
+  }
+
+  _constrainRunsByTest() {
+    this._constrainByTest = !this._constrainByTest;
+    this._resetPagination();
+    this._reload();
+  }
+});
diff --git a/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk.scss b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk.scss
new file mode 100644
index 0000000..975cd16
--- /dev/null
+++ b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk.scss
@@ -0,0 +1,55 @@
+@import '../colors.css';
+
+chromium-perf-runs-sk {
+  div.dialog-background {
+    width: 100%;
+    height: 100%;
+    z-index: 10;
+    position: fixed;
+    overflow: auto;
+    left: 0;
+    top: 0;
+  }
+  div.hidden {
+    display: none;
+  }
+  div.dialog-content {
+    min-width: 200px;
+    max-width: calc(100% - 10px);
+    position: fixed;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    padding: 2em;
+    border-radius: 1em;
+  }
+  table.runssummary > tbody > tr > td, table.runssummary>tbody>tr>th {
+      padding: 10px;
+      border: solid black 1px;
+    }
+  table.runssummary {
+    border-spacing: 0px;
+    padding-top: 2em;
+    padding-left: 2em;
+    th {
+      text-align: center;
+    }
+    td.nowrap {
+      white-space: nowrap;
+    }
+  }
+
+  table.inner-table {
+    td {
+      border: none;
+      .nowrap {
+        white-space: nowrap;
+      }
+    }
+  }
+}
+
+/* Outside chromium-perf-runs-sk because darkmode exists outside it. */
+.darkmode .queue a {
+  color: var(--secondary);
+}
diff --git a/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk_test.js b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk_test.js
new file mode 100644
index 0000000..5939239
--- /dev/null
+++ b/ct/modules/chromium-perf-runs-sk/chromium-perf-runs-sk_test.js
@@ -0,0 +1,91 @@
+import './index';
+
+import { $, $$ } from 'common-sk/modules/dom';
+import { fetchMock } from 'fetch-mock';
+
+import {
+  tasksResult0, tasksResult1,
+} from './test_data';
+import {
+  eventPromise,
+  setUpElementUnderTest,
+} from '../../../infra-sk/modules/test_util';
+
+describe('chromium-perf-runs-sk', () => {
+  const newInstance = setUpElementUnderTest('chromium-perf-runs-sk');
+  fetchMock.config.overwriteRoutes = false;
+
+  let perfRuns;
+  beforeEach(async () => {
+    await expectReload(() => perfRuns = newInstance());
+  });
+
+  afterEach(() => {
+    //  Check all mock fetches called at least once and reset.
+    expect(fetchMock.done()).to.be.true;
+    fetchMock.reset();
+  });
+
+  // Expect 'trigger' to cause a reload, and execute it.
+  // Optionally pass desired result from server.
+  const expectReload = async (trigger, result) => {
+    result = result || tasksResult0;
+    const event = eventPromise('end-task');
+    fetchMock.postOnce('begin:/_/get_chromium_perf_tasks', result);
+    trigger();
+    await event;
+  };
+
+  const confirmDialog = () => $$('dialog', perfRuns).querySelectorAll('button')[1].click();
+
+  it('shows table entries', async () => {
+    expect($('table.runssummary>tbody>tr', perfRuns)).to.have.length(11);
+    expect(fetchMock.lastUrl()).to.contain('exclude_dummy_page_sets=true');
+    expect(fetchMock.lastUrl()).to.contain('offset=0');
+    expect(fetchMock.lastUrl()).to.contain('size=10');
+    expect(fetchMock.lastUrl()).to.not.contain('filter_by_logged_in_user=true');
+  });
+
+  it('filters by user', async () => {
+    expect(fetchMock.lastUrl()).to.not.contain('filter_by_logged_in_user=true');
+    await expectReload(() => $$('#userFilter', perfRuns).click());
+    expect(fetchMock.lastUrl()).to.contain('filter_by_logged_in_user=true');
+  });
+
+  it('filters by tests', async () => {
+    expect(fetchMock.lastUrl()).to.contain('exclude_dummy_page_sets=true');
+    await expectReload(() => $$('#testFilter', perfRuns).click());
+    expect(fetchMock.lastUrl()).to.not.contain('exclude_dummy_page_sets=true');
+  });
+
+  it('navigates with pages', async () => {
+    expect(fetchMock.lastUrl()).to.contain('offset=0');
+    const result = tasksResult1;
+    result.pagination.offset = 10;
+    // 'Next page' button.
+    await expectReload(
+      () => $('pagination-sk button.action', perfRuns)[2].click(), result);
+    expect(fetchMock.lastUrl()).to.contain('offset=10');
+    expect($('table.runssummary>tbody>tr', perfRuns)).to.have.length(5);
+  });
+
+  it('deletes tasks', async () => {
+    $$('delete-icon-sk', perfRuns).click();
+    fetchMock.post('begin:/_/delete_chromium_perf_task', 200);
+    await expectReload(confirmDialog);
+    expect(fetchMock.lastOptions('begin:/_/delete').body).to.contain('"id":5094');
+  });
+
+  it('reschedules tasks', async () => {
+    $$('redo-icon-sk', perfRuns).click();
+    fetchMock.post('begin:/_/redo_chromium_perf_task', 200);
+    await expectReload(confirmDialog);
+    expect(fetchMock.lastOptions('begin:/_/redo').body).to.contain('"id":5094');
+  });
+
+  it('shows detail dialogs', async () => {
+    expect($$('.dialog-background', perfRuns)).to.have.class('hidden');
+    $$('.details', perfRuns).click();
+    expect($$('.dialog-background', perfRuns)).to.not.have.class('hidden');
+  });
+});
diff --git a/ct/modules/chromium-perf-runs-sk/index.js b/ct/modules/chromium-perf-runs-sk/index.js
new file mode 100644
index 0000000..2b83799
--- /dev/null
+++ b/ct/modules/chromium-perf-runs-sk/index.js
@@ -0,0 +1,2 @@
+import './chromium-perf-runs-sk';
+import './chromium-perf-runs-sk.scss';
diff --git a/ct/modules/chromium-perf-runs-sk/test_data.js b/ct/modules/chromium-perf-runs-sk/test_data.js
new file mode 100644
index 0000000..b697076
--- /dev/null
+++ b/ct/modules/chromium-perf-runs-sk/test_data.js
@@ -0,0 +1,50 @@
+export const tasksResult0 = {
+  data: [{
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADLEhYKEUNocm9taXVtUGVyZlRhc2tzEOYn', TsAdded: 20200723222929, TsStarted: 20200723222930, TsCompleted: 20200724050154, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:alexmt-ChromiumPerf-5094\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d969d43a4521410', Benchmark: 'ad_tagging.cluster_telemetry', Platform: 'Linux', RunOnGCE: false, PageSets: '10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2316326/2 ([WIP] Track dynamically added scripts with no src in AdTracker)', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/cf90a6c04958ad2ec0d41bda844e9008654b60e6.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/alexmt-ChromiumPerf-5094/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/alexmt-ChromiumPerf-5094-nopatch/consolidated_outputs/alexmt-ChromiumPerf-5094-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/alexmt-ChromiumPerf-5094-withpatch/consolidated_outputs/alexmt-ChromiumPerf-5094-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'sum',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADJ5EhYKEUNocm9taXVtUGVyZlRhc2tzEOMn', TsAdded: 20200723192204, TsStarted: 20200723192207, TsCompleted: 20200724003152, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:alexmt-ChromiumPerf-5091\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d95f1b4a3891710', Benchmark: 'ad_tagging.cluster_telemetry', Platform: 'Linux', RunOnGCE: false, PageSets: '10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format --max-pages-per-bot=10', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2316326/1 ([WIP] Track dynamically added scripts with no src in AdTracker)', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/8e2c009191f9ac50e59b8c896f475e2a4c555b59.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/alexmt-ChromiumPerf-5091/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/alexmt-ChromiumPerf-5091-nopatch/consolidated_outputs/alexmt-ChromiumPerf-5091-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/alexmt-ChromiumPerf-5091-withpatch/consolidated_outputs/alexmt-ChromiumPerf-5091-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'sum',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADJ5EhYKEUNocm9taXVtUGVyZlRhc2tzENwn', TsAdded: 20200723135735, TsStarted: 20200723135738, TsCompleted: 20200723203553, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:backer-ChromiumPerf-5084\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d94c8a1d384a910', Benchmark: 'rendering.cluster_telemetry', Platform: 'Android', RunOnGCE: false, PageSets: 'Mobile10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format', BrowserArgsNoPatch: '--enable-features=UseSkiaRenderer --use-gl=egl --disable-features=Vulkan', BrowserArgsWithPatch: '--enable-features=UseSkiaRenderer --use-gl=angle --use-cmd-decoder=passthrough --disable-features=Vulkan', Description: 'Testing ANGLE overhead', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/220e34417681c50b03443f1ae74d4d2f12b7b20e.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/220e34417681c50b03443f1ae74d4d2f12b7b20e.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/backer-ChromiumPerf-5084/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/backer-ChromiumPerf-5084-nopatch/consolidated_outputs/backer-ChromiumPerf-5084-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/backer-ChromiumPerf-5084-withpatch/consolidated_outputs/backer-ChromiumPerf-5084-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'sum',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADJ5EhYKEUNocm9taXVtUGVyZlRhc2tzENon', TsAdded: 20200723044620, TsStarted: 20200723044623, TsCompleted: 20200724005552, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:sauski-ChromiumPerf-5082\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d92cff197daa510', Benchmark: 'loading.cluster_telemetry', Platform: 'Linux', RunOnGCE: false, PageSets: '10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '--enable-features=ClientStorageAccessContextAuditing', Description: 'Client Storage API Access Context Auditing - Cookies', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/sauski-ChromiumPerf-5082/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/sauski-ChromiumPerf-5082-nopatch/consolidated_outputs/sauski-ChromiumPerf-5082-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/sauski-ChromiumPerf-5082-withpatch/consolidated_outputs/sauski-ChromiumPerf-5082-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'avg',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADHJ5EhYKEUNocm9taXVtUGVyZlRhc2tzENkn', TsAdded: 20200723002847, TsStarted: 20200723002848, TsCompleted: 20200723052634, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:backer-ChromiumPerf-5081\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d91e420c1ef9610', Benchmark: 'rendering.cluster_telemetry', Platform: 'Android', RunOnGCE: false, PageSets: 'Mobile10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format', BrowserArgsNoPatch: '--enable-features=UseSkiaRenderer', BrowserArgsWithPatch: '--enable-features=UseSkiaRenderer --use-gl=angle --use-cmd-decoder=passthrough', Description: 'Testing ANGLE overhead', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/8a9ada38ecc1413abc7180faf960b9c976dc139f.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/8a9ada38ecc1413abc7180faf960b9c976dc139f.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/backer-ChromiumPerf-5081/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/backer-ChromiumPerf-5081-nopatch/consolidated_outputs/backer-ChromiumPerf-5081-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/backer-ChromiumPerf-5081-withpatch/consolidated_outputs/backer-ChromiumPerf-5081-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'sum',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADHJ5EhYKEUNocm9taXVtUGVyZlRhc2tzENIn', TsAdded: 20200721083934, TsStarted: 20200721083937, TsCompleted: 20200721133734, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 7, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:sadrul-ChromiumPerf-5074\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d8958c2f8927b10', Benchmark: 'rendering.cluster_telemetry', Platform: 'Android', RunOnGCE: false, PageSets: 'Mobile10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --legacy-json-trace-format', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'https://bugs.chromium.org/p/chromium/issues/detail?id=968412', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/sadrul-ChromiumPerf-5074/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/sadrul-ChromiumPerf-5074-nopatch/consolidated_outputs/sadrul-ChromiumPerf-5074-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/sadrul-ChromiumPerf-5074-withpatch/consolidated_outputs/sadrul-ChromiumPerf-5074-withpatch.output', ChromiumHash: '', CCList: ['someone@google.com', 'someone@google.com'], TaskPriority: 100, GroupName: 'ClusteringTop10K', ValueColumnName: '',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADEhYKEUNocm9taXVtUGVyZlRhc2tzEMkn', TsAdded: 20200715215236, TsStarted: 20200715215238, TsCompleted: 20200716080950, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:teresakang-ChromiumPerf-5065\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d6d48a186847610', Benchmark: 'rendering.cluster_telemetry', Platform: 'Linux', RunOnGCE: false, PageSets: '10k', IsTestPageSet: false, RepeatRuns: 10, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=10 --skip-typ-expectations-tags-validation --legacy-json-trace-format', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing willReadFrequently', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/e4ed65d9ff42dc58ad00a776310890c76de94277.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/teresakang-ChromiumPerf-5065/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/teresakang-ChromiumPerf-5065-nopatch/consolidated_outputs/teresakang-ChromiumPerf-5065-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/teresakang-ChromiumPerf-5065-withpatch/consolidated_outputs/teresakang-ChromiumPerf-5065-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'avg',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADldHJ5EhYKEUNocm9taXVtUGVyZlRhc2tzEMcn', TsAdded: 20200715181424, TsStarted: 20200715181426, TsCompleted: 20200715224750, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:cammie-ChromiumPerf-5063\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d6c80dd15c79c10', Benchmark: 'ad_tagging.cluster_telemetry', Platform: 'Android', RunOnGCE: false, PageSets: 'Mobile10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format --additional-histograms=Blink.Paint.UpdateTime,PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame.CreativeOriginStatusWithThrottling', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2296293/4 (Perf: NotifyPaintTimingChanged for FEtP only 1 in N times to speed up.)', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/a69a1805423e629696d63976e71c64ffc5ddffea.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/cammie-ChromiumPerf-5063/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/cammie-ChromiumPerf-5063-nopatch/consolidated_outputs/cammie-ChromiumPerf-5063-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/cammie-ChromiumPerf-5063-withpatch/consolidated_outputs/cammie-ChromiumPerf-5063-withpatch.output', ChromiumHash: '', CCList: ['cammie@chromium.org'], TaskPriority: 100, GroupName: '1100404', ValueColumnName: 'sum',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHAD1ldHJ5EhYKEUNocm9taXVtUGVyZlRhc2tzEMYn', TsAdded: 20200714193806, TsStarted: 20200714193807, TsCompleted: 20200715000950, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:cammie-ChromiumPerf-5062\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d67a71f71ba7510', Benchmark: 'ad_tagging.cluster_telemetry', Platform: 'Android', RunOnGCE: false, PageSets: 'Mobile10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format --additional-histograms=Blink.Paint.UpdateTime,PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame.CreativeOriginStatusWithThrottling', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2296293/3 (Perf: NotifyPaintTimingChanged for FEtP only 1 in N times to speed up.)', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/6dc9bd43d71e9ff3d488861a6ca630a73cbc2d52.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/cammie-ChromiumPerf-5062/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/cammie-ChromiumPerf-5062-nopatch/consolidated_outputs/cammie-ChromiumPerf-5062-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/cammie-ChromiumPerf-5062-withpatch/consolidated_outputs/cammie-ChromiumPerf-5062-withpatch.output', ChromiumHash: '', CCList: ['cammie@chromium.org'], TaskPriority: 100, GroupName: '1100404', ValueColumnName: 'sum',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHAD5EhYKEUNocm9taXVtUGVyZlRhc2tzEMUn', TsAdded: 20200714184410, TsStarted: 20200714184412, TsCompleted: 20200714235950, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:wangxianzhu-ChromiumPerf-5061\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d6775c135325910', Benchmark: 'rasterize_and_record_micro_ct', Platform: 'Linux', RunOnGCE: false, PageSets: '10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2297698/1 (Revert "Use PhysicalOffset in legacy inline box painters")', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/107799931181b2f5b13d132af8112c098ca9aa88.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/wangxianzhu-ChromiumPerf-5061/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/wangxianzhu-ChromiumPerf-5061-nopatch/consolidated_outputs/wangxianzhu-ChromiumPerf-5061-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/wangxianzhu-ChromiumPerf-5061-withpatch/consolidated_outputs/wangxianzhu-ChromiumPerf-5061-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'avg',
+  }],
+  ids: [5094, 5091, 5084, 5082, 5081, 5074, 5065, 5063, 5062, 5061],
+  pagination: { offset: 0, size: 10, total: 1827 },
+  permissions: [{ DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }],
+};
+export const tasksResult1 = {
+  data: [{
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADEhYKEUNocm9taXVtUGVyZlRhc2tzEMkn', TsAdded: 20200715215236, TsStarted: 20200715215238, TsCompleted: 20200716080950, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:teresakang-ChromiumPerf-5065\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d6d48a186847610', Benchmark: 'rendering.cluster_telemetry', Platform: 'Linux', RunOnGCE: false, PageSets: '10k', IsTestPageSet: false, RepeatRuns: 10, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=10 --skip-typ-expectations-tags-validation --legacy-json-trace-format', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing willReadFrequently', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/e4ed65d9ff42dc58ad00a776310890c76de94277.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/teresakang-ChromiumPerf-5065/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/teresakang-ChromiumPerf-5065-nopatch/consolidated_outputs/teresakang-ChromiumPerf-5065-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/teresakang-ChromiumPerf-5065-withpatch/consolidated_outputs/teresakang-ChromiumPerf-5065-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'avg',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADldHJ5EhYKEUNocm9taXVtUGVyZlRhc2tzEMcn', TsAdded: 20200715181424, TsStarted: 20200715181426, TsCompleted: 20200715224750, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:cammie-ChromiumPerf-5063\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d6c80dd15c79c10', Benchmark: 'ad_tagging.cluster_telemetry', Platform: 'Android', RunOnGCE: false, PageSets: 'Mobile10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format --additional-histograms=Blink.Paint.UpdateTime,PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame.CreativeOriginStatusWithThrottling', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2296293/4 (Perf: NotifyPaintTimingChanged for FEtP only 1 in N times to speed up.)', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/a69a1805423e629696d63976e71c64ffc5ddffea.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/cammie-ChromiumPerf-5063/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/cammie-ChromiumPerf-5063-nopatch/consolidated_outputs/cammie-ChromiumPerf-5063-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/cammie-ChromiumPerf-5063-withpatch/consolidated_outputs/cammie-ChromiumPerf-5063-withpatch.output', ChromiumHash: '', CCList: ['cammie@chromium.org'], TaskPriority: 100, GroupName: '1100404', ValueColumnName: 'sum',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHAD1ldHJ5EhYKEUNocm9taXVtUGVyZlRhc2tzEMYn', TsAdded: 20200714193806, TsStarted: 20200714193807, TsCompleted: 20200715000950, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:cammie-ChromiumPerf-5062\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d67a71f71ba7510', Benchmark: 'ad_tagging.cluster_telemetry', Platform: 'Android', RunOnGCE: false, PageSets: 'Mobile10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format --additional-histograms=Blink.Paint.UpdateTime,PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame.CreativeOriginStatusWithThrottling', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2296293/3 (Perf: NotifyPaintTimingChanged for FEtP only 1 in N times to speed up.)', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/6dc9bd43d71e9ff3d488861a6ca630a73cbc2d52.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/cammie-ChromiumPerf-5062/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/cammie-ChromiumPerf-5062-nopatch/consolidated_outputs/cammie-ChromiumPerf-5062-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/cammie-ChromiumPerf-5062-withpatch/consolidated_outputs/cammie-ChromiumPerf-5062-withpatch.output', ChromiumHash: '', CCList: ['cammie@chromium.org'], TaskPriority: 100, GroupName: '1100404', ValueColumnName: 'sum',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHAD5EhYKEUNocm9taXVtUGVyZlRhc2tzEMUn', TsAdded: 20200714184410, TsStarted: 20200714184412, TsCompleted: 20200714235950, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:wangxianzhu-ChromiumPerf-5061\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d6775c135325910', Benchmark: 'rasterize_and_record_micro_ct', Platform: 'Linux', RunOnGCE: false, PageSets: '10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2297698/1 (Revert "Use PhysicalOffset in legacy inline box painters")', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/107799931181b2f5b13d132af8112c098ca9aa88.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/wangxianzhu-ChromiumPerf-5061/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/wangxianzhu-ChromiumPerf-5061-nopatch/consolidated_outputs/wangxianzhu-ChromiumPerf-5061-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/wangxianzhu-ChromiumPerf-5061-withpatch/consolidated_outputs/wangxianzhu-ChromiumPerf-5061-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'avg',
+  }],
+  ids: [5094, 5091, 5084, 5082],
+  pagination: { offset: 10, size: 10, total: 1827 },
+  permissions: [{ DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }],
+};
+export const tasksResult2 = {
+  data: [{
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADLEhYKEUNocm9taXVtUGVyZlRhc2tzEOYn', TsAdded: 20200723222929, TsStarted: 20200723222930, TsCompleted: 20200724050154, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:alexmt-ChromiumPerf-5094\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d969d43a4521410', Benchmark: 'ad_tagging.cluster_telemetry', Platform: 'Linux', RunOnGCE: false, PageSets: '10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2316326/2 ([WIP] Track dynamically added scripts with no src in AdTracker)', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/cf90a6c04958ad2ec0d41bda844e9008654b60e6.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/alexmt-ChromiumPerf-5094/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/alexmt-ChromiumPerf-5094-nopatch/consolidated_outputs/alexmt-ChromiumPerf-5094-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/alexmt-ChromiumPerf-5094-withpatch/consolidated_outputs/alexmt-ChromiumPerf-5094-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'sum',
+  }, {
+    DatastoreKey: 'ABDSHGASHHJAIJASFGHADSFGHADJ5EhYKEUNocm9taXVtUGVyZlRhc2tzEOMn', TsAdded: 20200723192204, TsStarted: 20200723192207, TsCompleted: 20200724003152, Username: 'someone@google.com', Failure: false, RepeatAfterDays: 0, SwarmingLogs: 'https://chrome-swarming.appspot.com/tasklist?l=500\u0026c=name\u0026c=created_ts\u0026c=bot\u0026c=duration\u0026c=state\u0026f=runid:alexmt-ChromiumPerf-5091\u0026st=1262304000000', TaskDone: true, SwarmingTaskID: '4d95f1b4a3891710', Benchmark: 'ad_tagging.cluster_telemetry', Platform: 'Linux', RunOnGCE: false, PageSets: '10k', IsTestPageSet: false, RepeatRuns: 1, RunInParallel: false, BenchmarkArgs: '--output-format=csv --pageset-repeat=1 --skip-typ-expectations-tags-validation --legacy-json-trace-format --max-pages-per-bot=10', BrowserArgsNoPatch: '', BrowserArgsWithPatch: '', Description: 'Testing https://chromium-review.googlesource.com/c/2316326/1 ([WIP] Track dynamically added scripts with no src in AdTracker)', CustomWebpagesGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchGSPath: 'patches/8e2c009191f9ac50e59b8c896f475e2a4c555b59.patch', BlinkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', SkiaPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', CatapultPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', BenchmarkPatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', ChromiumPatchBaseBuildGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', V8PatchGSPath: 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch', Results: 'https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/alexmt-ChromiumPerf-5091/html/index.html', NoPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/alexmt-ChromiumPerf-5091-nopatch/consolidated_outputs/alexmt-ChromiumPerf-5091-nopatch.output', WithPatchRawOutput: 'https://ct.skia.org/results/cluster-telemetry/tasks/benchmark_runs/alexmt-ChromiumPerf-5091-withpatch/consolidated_outputs/alexmt-ChromiumPerf-5091-withpatch.output', ChromiumHash: '', CCList: null, TaskPriority: 100, GroupName: '', ValueColumnName: 'sum',
+  }],
+  ids: [5094, 5091, 5084, 5082, 5081, 5074, 5065, 5063, 5062, 5061],
+  pagination: { offset: 20, size: 10, total: 1827 },
+  permissions: [{ DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }, { DeleteAllowed: true, RedoAllowed: true }],
+};
diff --git a/ct/modules/ctfe_utils.js b/ct/modules/ctfe_utils.js
index 99b33fd..68d7b3f 100644
--- a/ct/modules/ctfe_utils.js
+++ b/ct/modules/ctfe_utils.js
@@ -51,6 +51,36 @@
 }
 
 /**
+ * Append gsPath with appropriate url to fetch from ct.skia.org.
+ */
+export function getGSLink(gsPath) {
+  return `https://ct.skia.org/results/cluster-telemetry/${gsPath}`;
+}
+
+/**
+   * Returns true if gsPath is not set or if the patch's SHA1 digest in the specified
+   * google storage path is for an empty string.
+   */
+export function isEmptyPatch(gsPath) {
+  // Compare against empty string and against the SHA1 digest of an empty string.
+  return gsPath === '' || gsPath === 'patches/da39a3ee5e6b4b0d3255bfef95601890afd80709.patch';
+}
+
+/**
+ * Express numeric days in a readable format (e.g. 'Weekly'; 'Every 3 days')
+ */
+export function formatRepeatAfterDays(num) {
+  if (num === 0) {
+    return 'N/A';
+  } if (num === 1) {
+    return 'Daily';
+  } if (num === 7) {
+    return 'Weekly';
+  }
+  return `Every ${num} days`;
+}
+
+/**
  * Fetches benchmarks with doc links, and platforms with descriptions.
  *
  * @param {func<Object>} func - Function called with fetched benchmarks and