1/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {browser, by, element, ElementFinder} from 'protractor';
18import {E2eTestUtils} from './utils';
19
20describe('Cross-Tool Protocol', () => {
21  beforeEach(async () => {
22    await browser.restart();
23
24    jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;
25    await browser.manage().timeouts().implicitlyWait(20000);
26    await E2eTestUtils.checkServerIsUp(
27      'Remote tool mock',
28      E2eTestUtils.REMOTE_TOOL_MOCK_URL,
29    );
30    await E2eTestUtils.checkServerIsUp('Winscope', E2eTestUtils.WINSCOPE_URL);
31
32    await browser.get(E2eTestUtils.REMOTE_TOOL_MOCK_URL);
33  });
34
35  it('allows communication with ABT', async () => {
36    const TIMESTAMP_IN_BUGREPORT_MESSAGE = '1670509911000000000';
37    const TIMESTAMP_FROM_ABT_TO_WINSCOPE = '1670509912000000000';
38    const TIMESTAMP_FROM_WINSCOPE = '1670509913000000000';
39
40    await openWinscopeTabFromRemoteTool();
41    await waitWinscopeTabIsOpen();
42
43    await sendBugreportToWinscope();
44    await checkWinscopeRendersUploadView();
45    await closeWinscopeSnackBar();
46
47    await clickWinscopeViewTracesButton();
48    await checkWinscopeRenderedSurfaceFlingerView();
49    await checkWinscopeRenderedAllViewTabs();
50    await checkWinscopeTimestamp(TIMESTAMP_IN_BUGREPORT_MESSAGE);
51
52    await sendRealtimeTimestampToWinscope(TIMESTAMP_FROM_ABT_TO_WINSCOPE);
53    await checkWinscopeTimestamp(TIMESTAMP_FROM_ABT_TO_WINSCOPE);
54
55    await sendTimestampToRemoteTool(TIMESTAMP_FROM_WINSCOPE);
56    await checkRemoteToolRealtimeTimestamp(TIMESTAMP_FROM_WINSCOPE);
57  });
58
59  it('allows communication with Perfetto', async () => {
60    // real-to-boottime offset = 1659107074601779989
61    const TIMESTAMP_IN_FILES_MESSAGE_REALTIME = '1659107090327674405';
62    const TIMESTAMP_FROM_PERFETTO_BOOTTIME = '15795654466';
63    const TIMESTAMP_FROM_PERFETTO_REALTIME = '1659107090397434455';
64    const TIMESTAMP_FROM_WINSCOPE_BOOTTIME = '15970486213';
65    const TIMESTAMP_FROM_WINSCOPE_REALTIME = '1659107090572266202';
66
67    await openWinscopeTabFromRemoteTool();
68    await waitWinscopeTabIsOpen();
69
70    await sendFilesToWinscope();
71    await checkWinscopeRendersUploadView();
72
73    await clickWinscopeViewTracesButton();
74    await checkWinscopeRenderedSurfaceFlingerView();
75    await checkWinscopeTimestamp(TIMESTAMP_IN_FILES_MESSAGE_REALTIME);
76
77    await sendBoottimeTimestampToWinscope(TIMESTAMP_FROM_PERFETTO_BOOTTIME);
78    await checkWinscopeTimestamp(TIMESTAMP_FROM_PERFETTO_REALTIME);
79
80    await sendTimestampToRemoteTool(TIMESTAMP_FROM_WINSCOPE_REALTIME);
81    await checkRemoteToolBoottimeTimestamp(TIMESTAMP_FROM_WINSCOPE_BOOTTIME);
82  });
83
84  it('can turn timestamp sync off/on', async () => {
85    // real-to-boottime offset = 1659107074601779989
86    const TIMESTAMP_IN_FILES_MESSAGE_REALTIME = '1659107090327674405';
87    const TIMESTAMP_FROM_PERFETTO_BOOTTIME = '15725894416';
88
89    await openWinscopeTabFromRemoteTool();
90    await waitWinscopeTabIsOpen();
91
92    await sendFilesToWinscope();
93    await checkWinscopeRendersUploadView();
94
95    await clickWinscopeViewTracesButton();
96    await checkWinscopeRenderedSurfaceFlingerView();
97
98    await clickCrossToolSyncButton();
99
100    await checkWinscopeTimestamp(TIMESTAMP_IN_FILES_MESSAGE_REALTIME);
101    await sendBoottimeTimestampToWinscope(TIMESTAMP_FROM_PERFETTO_BOOTTIME);
102    await checkWinscopeTimestamp(TIMESTAMP_IN_FILES_MESSAGE_REALTIME);
103
104    await checkRemoteToolBoottimeTimestamp('');
105    await sendTimestampToRemoteTool(TIMESTAMP_IN_FILES_MESSAGE_REALTIME);
106    await checkRemoteToolBoottimeTimestamp('');
107  });
108
109  async function openWinscopeTabFromRemoteTool() {
110    await browser.switchTo().window(await getWindowHandleRemoteToolMock());
111    const buttonElement = element(by.css('.button-open-winscope'));
112    await buttonElement.click();
113  }
114
115  async function sendBugreportToWinscope() {
116    await browser.switchTo().window(await getWindowHandleRemoteToolMock());
117    const inputFileElement = element(by.css('.button-send-bugreport'));
118    await inputFileElement.sendKeys(
119      E2eTestUtils.getFixturePath('bugreports/bugreport_stripped.zip'),
120    );
121  }
122
123  async function sendFilesToWinscope() {
124    await browser.switchTo().window(await getWindowHandleRemoteToolMock());
125    const inputFileElement = element(by.css('.button-send-files'));
126    await inputFileElement.sendKeys(
127      E2eTestUtils.getFixturePath(
128        'traces/perfetto/layers_trace.perfetto-trace',
129      ),
130    );
131  }
132
133  async function checkWinscopeRendersUploadView() {
134    await browser.switchTo().window(await getWindowHandleWinscope());
135    const isPresent = await element(by.css('.uploaded-files')).isPresent();
136    expect(isPresent).toBeTruthy();
137  }
138
139  async function clickWinscopeViewTracesButton() {
140    await browser.switchTo().window(await getWindowHandleWinscope());
141    await E2eTestUtils.clickViewTracesButton();
142  }
143
144  async function closeWinscopeSnackBar() {
145    await browser.switchTo().window(await getWindowHandleWinscope());
146    await E2eTestUtils.closeSnackBar();
147  }
148
149  async function waitWinscopeTabIsOpen() {
150    await browser.wait(
151      async () => {
152        const handles = await browser.getAllWindowHandles();
153        return handles.length >= 2;
154      },
155      20000,
156      'The Winscope tab did not open',
157    );
158  }
159
160  async function checkWinscopeRenderedSurfaceFlingerView() {
161    await browser.switchTo().window(await getWindowHandleWinscope());
162    const viewerPresent = await element(
163      by.css('viewer-surface-flinger'),
164    ).isPresent();
165    expect(viewerPresent).toBeTruthy();
166  }
167
168  async function checkWinscopeRenderedAllViewTabs() {
169    const tabParagraphs = await element.all(
170      by.css('.tabs-navigation-bar a span'),
171    );
172
173    const actualTabParagraphs = await Promise.all(
174      (tabParagraphs as ElementFinder[]).map(
175        async (paragraph) => await paragraph.getText(),
176      ),
177    );
178
179    const expectedTabParagraphs = [
180      'Surface Flinger',
181      'Transactions',
182      'Transitions',
183      'Window Manager',
184    ];
185
186    expect(actualTabParagraphs.sort()).toEqual(expectedTabParagraphs.sort());
187  }
188
189  async function sendRealtimeTimestampToWinscope(value: string) {
190    await browser.switchTo().window(await getWindowHandleRemoteToolMock());
191    const inputElement = element(by.css('.input-timestamp'));
192    await inputElement.sendKeys(value);
193    const buttonElement = element(by.css('.button-send-realtime-timestamp'));
194    await buttonElement.click();
195  }
196
197  async function sendBoottimeTimestampToWinscope(value: string) {
198    await browser.switchTo().window(await getWindowHandleRemoteToolMock());
199    const inputElement = element(by.css('.input-timestamp'));
200    await inputElement.sendKeys(value);
201    const buttonElement = element(by.css('.button-send-boottime-timestamp'));
202    await buttonElement.click();
203  }
204
205  async function sendTimestampToRemoteTool(value: string) {
206    browser.switchTo().window(await getWindowHandleWinscope());
207    await E2eTestUtils.changeNsTimestampInWinscope(value);
208  }
209
210  async function checkWinscopeTimestamp(expectedValue: string) {
211    await browser.switchTo().window(await getWindowHandleWinscope());
212    await E2eTestUtils.checkWinscopeNsTimestamp(expectedValue);
213  }
214
215  async function checkRemoteToolRealtimeTimestamp(expectedValue: string) {
216    await browser.switchTo().window(await getWindowHandleRemoteToolMock());
217    const paragraphElement = element(
218      by.css('.paragraph-received-realtime-timestamp'),
219    );
220    const actualValue = await paragraphElement.getText();
221    expect(actualValue).toEqual(expectedValue);
222  }
223
224  async function checkRemoteToolBoottimeTimestamp(expectedValue: string) {
225    await browser.switchTo().window(await getWindowHandleRemoteToolMock());
226    const paragraphElement = element(
227      by.css('.paragraph-received-boottime-timestamp'),
228    );
229    const actualValue = await paragraphElement.getText();
230    expect(actualValue).toEqual(expectedValue);
231  }
232
233  async function getWindowHandleRemoteToolMock(): Promise<string> {
234    const handles = await browser.getAllWindowHandles();
235    expect(handles.length).toBeGreaterThan(0);
236    return handles[0];
237  }
238
239  async function getWindowHandleWinscope(): Promise<string> {
240    const handles = await browser.getAllWindowHandles();
241    expect(handles.length).toEqual(2);
242    return handles[1];
243  }
244
245  async function clickCrossToolSyncButton() {
246    const button = element(by.css('.cross-tool-sync-button'));
247    await button.click();
248  }
249});
250