Merge "Add pre-upload hook to run Winscope tests" into udc-dev
diff --git a/tools/winscope/src/app/app_module.ts b/tools/winscope/src/app/app_module.ts
index 5954034..ddc8c74 100644
--- a/tools/winscope/src/app/app_module.ts
+++ b/tools/winscope/src/app/app_module.ts
@@ -18,7 +18,7 @@
 import {ScrollingModule} from '@angular/cdk/scrolling';
 import {CommonModule} from '@angular/common';
 import {HttpClientModule} from '@angular/common/http';
-import {NgModule} from '@angular/core';
+import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
 import {FormsModule, ReactiveFormsModule} from '@angular/forms';
 import {MatButtonModule} from '@angular/material/button';
 import {MatCardModule} from '@angular/material/card';
@@ -145,6 +145,7 @@
     DragDropModule,
     ReactiveFormsModule,
   ],
+  schemas: [CUSTOM_ELEMENTS_SCHEMA],
   bootstrap: [AppComponent],
 })
 export class AppModule {}
diff --git a/tools/winscope/src/app/components/app_component.ts b/tools/winscope/src/app/components/app_component.ts
index f5e62dc..512b198 100644
--- a/tools/winscope/src/app/components/app_component.ts
+++ b/tools/winscope/src/app/components/app_component.ts
@@ -32,6 +32,7 @@
 import {PersistentStore} from 'common/persistent_store';
 import {CrossToolProtocol} from 'cross_tool/cross_tool_protocol';
 import {TraceDataListener} from 'interfaces/trace_data_listener';
+import {LoadedTrace} from 'trace/loaded_trace';
 import {Timestamp} from 'trace/timestamp';
 import {TraceType} from 'trace/trace_type';
 import {proxyClient, ProxyState} from 'trace_collection/proxy_client';
@@ -59,6 +60,13 @@
       </a>
 
       <div class="spacer">
+        <mat-icon
+          *ngIf="activeTrace"
+          class="icon"
+          [matTooltip]="TRACE_INFO[activeTrace.type].name"
+          [style]="{color: TRACE_INFO[activeTrace.type].color, marginRight: '0.5rem'}">
+          {{ TRACE_INFO[activeTrace.type].icon }}
+        </mat-icon>
         <span *ngIf="dataLoaded" class="active-trace-file-info mat-body-2">
           {{ activeTraceFileInfo }}
         </span>
@@ -157,6 +165,9 @@
       .spacer {
         flex: 1;
         text-align: center;
+        display: flex;
+        align-items: center;
+        justify-content: center;
       }
       .viewers {
         height: 0;
@@ -202,11 +213,13 @@
   isDarkModeOn!: boolean;
   dataLoaded = false;
   activeView?: View;
+  activeTrace?: LoadedTrace;
   activeTraceFileInfo = '';
   collapsedTimelineHeight = 0;
   @ViewChild(UploadTracesComponent) uploadTracesComponent?: UploadTracesComponent;
   @ViewChild(CollectTracesComponent) collectTracesComponent?: UploadTracesComponent;
   @ViewChild(TimelineComponent) timelineComponent?: TimelineComponent;
+  TRACE_INFO = TRACE_INFO;
 
   constructor(
     @Inject(Injector) injector: Injector,
@@ -327,6 +340,7 @@
 
   onActiveViewChanged(view: View) {
     this.activeView = view;
+    this.activeTrace = this.getActiveTrace(view);
     this.activeTraceFileInfo = this.makeActiveTraceFileInfo(view);
     this.timelineData.setActiveViewTraceTypes(view.dependencies);
   }
@@ -336,15 +350,19 @@
   }
 
   private makeActiveTraceFileInfo(view: View): string {
-    const traceFile = this.tracePipeline
-      .getLoadedTraces()
-      .find((file) => file.type === view.dependencies[0]);
+    const trace = this.getActiveTrace(view);
 
-    if (!traceFile) {
+    if (!trace) {
       return '';
     }
 
-    return `${traceFile.type} (${traceFile.descriptors.join(', ')})`;
+    return `${TRACE_INFO[trace.type].name} (${trace.descriptors.join(', ')})`;
+  }
+
+  private getActiveTrace(view: View): LoadedTrace | undefined {
+    return this.tracePipeline
+      .getLoadedTraces()
+      .find((trace) => trace.type === view.dependencies[0]);
   }
 
   private async makeTraceFilesForDownload(): Promise<File[]> {
diff --git a/tools/winscope/src/app/components/timeline/expanded_timeline_component.ts b/tools/winscope/src/app/components/timeline/expanded_timeline_component.ts
index d1cb8f3..51a1d9b 100644
--- a/tools/winscope/src/app/components/timeline/expanded_timeline_component.ts
+++ b/tools/winscope/src/app/components/timeline/expanded_timeline_component.ts
@@ -40,7 +40,10 @@
         *ngFor="let trace of getTraces(); trackBy: trackTraceBySelectedTimestamp"
         class="timeline">
         <div class="icon-wrapper">
-          <mat-icon class="icon" [style]="{color: TRACE_INFO[trace.type].color}">
+          <mat-icon
+            class="icon"
+            [matTooltip]="TRACE_INFO[trace.type].name"
+            [style]="{color: TRACE_INFO[trace.type].color}">
             {{ TRACE_INFO[trace.type].icon }}
           </mat-icon>
         </div>
diff --git a/tools/winscope/src/app/components/timeline/timeline_component_test.ts b/tools/winscope/src/app/components/timeline/timeline_component_test.ts
index 1676e11..7720b09 100644
--- a/tools/winscope/src/app/components/timeline/timeline_component_test.ts
+++ b/tools/winscope/src/app/components/timeline/timeline_component_test.ts
@@ -20,9 +20,9 @@
 import {MatButtonModule} from '@angular/material/button';
 import {MatFormFieldModule} from '@angular/material/form-field';
 import {MatIconModule} from '@angular/material/icon';
-import {MatSelectModule} from '@angular/material/select';
-
 import {MatInputModule} from '@angular/material/input';
+import {MatSelectModule} from '@angular/material/select';
+import {MatTooltipModule} from '@angular/material/tooltip';
 import {By} from '@angular/platform-browser';
 import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
 import {
@@ -66,6 +66,7 @@
         MatInputModule,
         MatIconModule,
         MatSelectModule,
+        MatTooltipModule,
         ReactiveFormsModule,
         BrowserAnimationsModule,
       ],
diff --git a/tools/winscope/src/app/components/trace_view_component.ts b/tools/winscope/src/app/components/trace_view_component.ts
index ca8ca7c..7bd3bd8 100644
--- a/tools/winscope/src/app/components/trace_view_component.ts
+++ b/tools/winscope/src/app/components/trace_view_component.ts
@@ -15,6 +15,7 @@
  */
 
 import {Component, ElementRef, EventEmitter, Inject, Input, Output} from '@angular/core';
+import {TRACE_INFO} from 'app/trace_info';
 import {PersistentStore} from 'common/persistent_store';
 import {View, Viewer, ViewType} from 'viewers/viewer';
 
@@ -44,7 +45,15 @@
           [active]="isCurrentActiveTab(tab)"
           (click)="onTabClick(tab)"
           class="tab">
-          {{ tab.title }}
+          <mat-icon
+            class="icon"
+            [matTooltip]="TRACE_INFO[tab.traceType].name"
+            [style]="{color: TRACE_INFO[tab.traceType].color, marginRight: '0.5rem'}">
+            {{ TRACE_INFO[tab.traceType].icon }}
+          </mat-icon>
+          <p>
+            {{ tab.title }}
+          </p>
         </a>
       </nav>
       <button
@@ -99,6 +108,8 @@
   @Output() downloadTracesButtonClick = new EventEmitter<void>();
   @Output() activeViewChanged = new EventEmitter<View>();
 
+  TRACE_INFO = TRACE_INFO;
+
   private elementRef: ElementRef;
 
   tabs: Tab[] = [];
@@ -129,6 +140,7 @@
           title: view.title,
           addedToDom: false,
           dependencies: view.dependencies,
+          traceType: view.traceType,
         };
       });
 
diff --git a/tools/winscope/src/app/components/upload_traces_component.ts b/tools/winscope/src/app/components/upload_traces_component.ts
index 37dbd32..a52692e 100644
--- a/tools/winscope/src/app/components/upload_traces_component.ts
+++ b/tools/winscope/src/app/components/upload_traces_component.ts
@@ -46,6 +46,7 @@
           hidden
           type="file"
           multiple
+          onclick="this.value = null"
           #fileDropRef
           (change)="onInputFiles($event)" />
 
@@ -216,6 +217,7 @@
 
   onClearButtonClick() {
     this.tracePipeline.clear();
+    this.onOperationFinished();
   }
 
   onFileDragIn(e: DragEvent) {
@@ -240,6 +242,7 @@
     event.preventDefault();
     event.stopPropagation();
     this.tracePipeline.removeTraceFile(trace.type);
+    this.onOperationFinished();
   }
 
   private getInputFiles(event: Event): File[] {
diff --git a/tools/winscope/src/test/e2e/cross_tool_protocol_test.ts b/tools/winscope/src/test/e2e/cross_tool_protocol_test.ts
index 8fc15a2..86ea574 100644
--- a/tools/winscope/src/test/e2e/cross_tool_protocol_test.ts
+++ b/tools/winscope/src/test/e2e/cross_tool_protocol_test.ts
@@ -26,7 +26,8 @@
   const TIMESTAMP_FROM_WINSCOPE_TO_REMOTE_TOOL = '1670509913000000000';
 
   beforeAll(async () => {
-    await browser.manage().timeouts().implicitlyWait(5000);
+    jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000;
+    await browser.manage().timeouts().implicitlyWait(15000);
     await checkServerIsUp('Remote tool mock', REMOTE_TOOL_MOCK_URL);
     await checkServerIsUp('Winscope', WINSCOPE_URL);
   });
@@ -111,13 +112,13 @@
   };
 
   const checkWinscopeRenderedAllViewTabs = async () => {
-    const linkElements = await element.all(by.css('.tabs-navigation-bar a'));
+    const tabParagraphs = await element.all(by.css('.tabs-navigation-bar a p'));
 
-    const actualLinks = await Promise.all(
-      (linkElements as ElementFinder[]).map(async (linkElement) => await linkElement.getText())
+    const actualTabParagraphs = await Promise.all(
+      (tabParagraphs as ElementFinder[]).map(async (paragraph) => await paragraph.getText())
     );
 
-    const expectedLinks = [
+    const expectedTabParagraphs = [
       'Input Method Clients',
       'Input Method Manager Service',
       'Input Method Service',
@@ -128,7 +129,7 @@
       'Window Manager',
     ];
 
-    expect(actualLinks.sort()).toEqual(expectedLinks.sort());
+    expect(actualTabParagraphs.sort()).toEqual(expectedTabParagraphs.sort());
   };
 
   const checkWinscopeAppliedTimestampInBugreportMessage = async () => {
diff --git a/tools/winscope/src/test/e2e/upload_traces_test.ts b/tools/winscope/src/test/e2e/upload_traces_test.ts
index 8be4f78..72554a3 100644
--- a/tools/winscope/src/test/e2e/upload_traces_test.ts
+++ b/tools/winscope/src/test/e2e/upload_traces_test.ts
@@ -18,8 +18,11 @@
 import {E2eTestUtils} from './utils';
 
 describe('Upload traces', () => {
+  const DEFAULT_TIMEOUT_MS = 15000;
+
   beforeAll(async () => {
-    await browser.manage().timeouts().implicitlyWait(5000);
+    jasmine.DEFAULT_TIMEOUT_INTERVAL = DEFAULT_TIMEOUT_MS;
+    await browser.manage().timeouts().implicitlyWait(DEFAULT_TIMEOUT_MS);
   });
 
   beforeEach(async () => {
@@ -46,11 +49,11 @@
     const text = await element(by.css('.uploaded-files')).getText();
     expect(text).toContain('ProtoLog');
     expect(text).toContain('IME Service');
-    expect(text).toContain('IME Manager Service)');
-    expect(text).toContain('Window Manager)');
-    expect(text).toContain('Surface Flinger)');
-    expect(text).toContain('IME Clients)');
-    expect(text).toContain('Transactions)');
+    expect(text).toContain('IME Manager Service');
+    expect(text).toContain('Window Manager');
+    expect(text).toContain('Surface Flinger');
+    expect(text).toContain('IME Clients');
+    expect(text).toContain('Transactions');
     expect(text).toContain('Transitions');
 
     expect(text).toContain('wm_log.winscope');
@@ -75,7 +78,12 @@
   };
 
   const areMessagesEmitted = async (): Promise<boolean> => {
-    return element(by.css('snack-bar')).isPresent();
+    // Messages are emitted quickly. There is no Need to wait for the entire
+    // default timeout to understand whether the messages where emitted or not.
+    await browser.manage().timeouts().implicitlyWait(1000);
+    const emitted = await element(by.css('snack-bar')).isPresent();
+    await browser.manage().timeouts().implicitlyWait(DEFAULT_TIMEOUT_MS);
+    return emitted;
   };
 
   const checkRendersSurfaceFlingerView = async () => {
diff --git a/tools/winscope/src/viewers/viewer.ts b/tools/winscope/src/viewers/viewer.ts
index e2629e0..26560ee 100644
--- a/tools/winscope/src/viewers/viewer.ts
+++ b/tools/winscope/src/viewers/viewer.ts
@@ -27,7 +27,8 @@
     public type: ViewType,
     public dependencies: TraceType[],
     public htmlElement: HTMLElement,
-    public title: string
+    public title: string,
+    public traceType: TraceType
   ) {}
 }
 
diff --git a/tools/winscope/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts b/tools/winscope/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts
index 6d55511..e2ebb42 100644
--- a/tools/winscope/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts
+++ b/tools/winscope/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts
@@ -23,7 +23,13 @@
 class ViewerInputMethodClients extends ViewerInputMethod {
   override getViews(): View[] {
     return [
-      new View(ViewType.TAB, this.getDependencies(), this.htmlElement, 'Input Method Clients'),
+      new View(
+        ViewType.TAB,
+        this.getDependencies(),
+        this.htmlElement,
+        'Input Method Clients',
+        TraceType.INPUT_METHOD_CLIENTS
+      ),
     ];
   }
 
diff --git a/tools/winscope/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts b/tools/winscope/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts
index dd24e80..74a9ea6 100644
--- a/tools/winscope/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts
+++ b/tools/winscope/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts
@@ -27,7 +27,8 @@
         ViewType.TAB,
         this.getDependencies(),
         this.htmlElement,
-        'Input Method Manager Service'
+        'Input Method Manager Service',
+        TraceType.INPUT_METHOD_MANAGER_SERVICE
       ),
     ];
   }
diff --git a/tools/winscope/src/viewers/viewer_input_method_service/viewer_input_method_service.ts b/tools/winscope/src/viewers/viewer_input_method_service/viewer_input_method_service.ts
index fe205a7..8bc064f 100644
--- a/tools/winscope/src/viewers/viewer_input_method_service/viewer_input_method_service.ts
+++ b/tools/winscope/src/viewers/viewer_input_method_service/viewer_input_method_service.ts
@@ -23,7 +23,13 @@
 class ViewerInputMethodService extends ViewerInputMethod {
   override getViews(): View[] {
     return [
-      new View(ViewType.TAB, this.getDependencies(), this.htmlElement, 'Input Method Service'),
+      new View(
+        ViewType.TAB,
+        this.getDependencies(),
+        this.htmlElement,
+        'Input Method Service',
+        TraceType.INPUT_METHOD_SERVICE
+      ),
     ];
   }
 
diff --git a/tools/winscope/src/viewers/viewer_protolog/viewer_protolog.ts b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog.ts
index c71f390..f03f46a 100644
--- a/tools/winscope/src/viewers/viewer_protolog/viewer_protolog.ts
+++ b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog.ts
@@ -49,7 +49,15 @@
   }
 
   getViews(): View[] {
-    return [new View(ViewType.TAB, this.getDependencies(), this.htmlElement, 'ProtoLog')];
+    return [
+      new View(
+        ViewType.TAB,
+        this.getDependencies(),
+        this.htmlElement,
+        'ProtoLog',
+        TraceType.PROTO_LOG
+      ),
+    ];
   }
 
   getDependencies(): TraceType[] {
diff --git a/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording.ts b/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording.ts
index db26c90..5bc1952 100644
--- a/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording.ts
+++ b/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording.ts
@@ -42,7 +42,13 @@
 
   getViews(): View[] {
     return [
-      new View(ViewType.OVERLAY, this.getDependencies(), this.htmlElement, 'ScreenRecording'),
+      new View(
+        ViewType.OVERLAY,
+        this.getDependencies(),
+        this.htmlElement,
+        'ScreenRecording',
+        TraceType.SCREEN_RECORDING
+      ),
     ];
   }
 
diff --git a/tools/winscope/src/viewers/viewer_stub.ts b/tools/winscope/src/viewers/viewer_stub.ts
index 088c5f4..46a7f99 100644
--- a/tools/winscope/src/viewers/viewer_stub.ts
+++ b/tools/winscope/src/viewers/viewer_stub.ts
@@ -15,6 +15,7 @@
  */
 
 import {TracePosition} from 'trace/trace_position';
+import {TraceType} from 'trace/trace_type';
 import {View, Viewer, ViewType} from './viewer';
 
 class ViewerStub implements Viewer {
@@ -34,11 +35,19 @@
   }
 
   getViews(): View[] {
-    return [new View(ViewType.TAB, this.getDependencies(), this.htmlElement, this.title)];
+    return [
+      new View(
+        ViewType.TAB,
+        this.getDependencies(),
+        this.htmlElement,
+        this.title,
+        this.getDependencies()[0]
+      ),
+    ];
   }
 
-  getDependencies(): any {
-    return;
+  getDependencies(): TraceType[] {
+    return [TraceType.WINDOW_MANAGER];
   }
 
   private htmlElement: HTMLElement;
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts b/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts
index ff2b12e..17dc629 100644
--- a/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts
+++ b/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts
@@ -62,7 +62,15 @@
   }
 
   getViews(): View[] {
-    return [new View(ViewType.TAB, this.getDependencies(), this.htmlElement, 'Surface Flinger')];
+    return [
+      new View(
+        ViewType.TAB,
+        this.getDependencies(),
+        this.htmlElement,
+        'Surface Flinger',
+        TraceType.SURFACE_FLINGER
+      ),
+    ];
   }
 
   getDependencies(): TraceType[] {
diff --git a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts
index e1c31ac..2c83bc6 100644
--- a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions.ts
@@ -64,7 +64,15 @@
   }
 
   getViews(): View[] {
-    return [new View(ViewType.TAB, this.getDependencies(), this.htmlElement, 'Transactions')];
+    return [
+      new View(
+        ViewType.TAB,
+        this.getDependencies(),
+        this.htmlElement,
+        'Transactions',
+        TraceType.TRANSACTIONS
+      ),
+    ];
   }
 
   getDependencies(): TraceType[] {
diff --git a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions.ts b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions.ts
index 106c93d..6ba1e4e 100644
--- a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions.ts
@@ -39,7 +39,15 @@
   }
 
   getViews(): View[] {
-    return [new View(ViewType.TAB, this.getDependencies(), this.htmlElement, 'Transitions')];
+    return [
+      new View(
+        ViewType.TAB,
+        this.getDependencies(),
+        this.htmlElement,
+        'Transitions',
+        TraceType.TRANSITION
+      ),
+    ];
   }
 
   getDependencies(): TraceType[] {
diff --git a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
index 238862b..feb61c5 100644
--- a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
@@ -101,8 +101,8 @@
   const abortTime = null;
   const finishTime = CrossPlatform.timestamp.fromString(finishTimeNanos.toString(), null, null);
 
-  const startTransactionId = -1;
-  const finishTransactionId = -1;
+  const startTransactionId = '-1';
+  const finishTransactionId = '-1';
   const type = TransitionType.TO_FRONT;
   const changes: TransitionChange[] = [];
 
diff --git a/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager.ts b/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager.ts
index 7bfea19..29726f8 100644
--- a/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager.ts
+++ b/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager.ts
@@ -56,7 +56,15 @@
   }
 
   getViews(): View[] {
-    return [new View(ViewType.TAB, this.getDependencies(), this.htmlElement, 'Window Manager')];
+    return [
+      new View(
+        ViewType.TAB,
+        this.getDependencies(),
+        this.htmlElement,
+        'Window Manager',
+        TraceType.WINDOW_MANAGER
+      ),
+    ];
   }
 
   getDependencies(): TraceType[] {