blob: fb0004d21911d4310cc85b6e690e4b47f4a238ae [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.profilers;
import static com.android.tools.idea.transport.faketransport.FakeTransportService.FAKE_DEVICE;
import static com.android.tools.idea.transport.faketransport.FakeTransportService.FAKE_PROCESS;
import static com.android.tools.profilers.StudioProfilers.AGENT_STATUS_MAX_RETRY_COUNT;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.sdklib.AndroidVersion;
import com.android.tools.adtui.model.AspectObserver;
import com.android.tools.adtui.model.FakeTimer;
import com.android.tools.idea.transport.faketransport.FakeGrpcServer;
import com.android.tools.idea.transport.faketransport.FakeTransportService;
import com.android.tools.profiler.proto.Common;
import com.android.tools.profiler.proto.Common.AgentData;
import com.android.tools.profilers.cpu.CpuProfilerStage;
import com.android.tools.profilers.energy.EnergyProfilerStage;
import com.android.tools.profilers.memory.MemoryProfilerStage;
import com.android.tools.profilers.network.NetworkProfilerStage;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import org.jetbrains.annotations.NotNull;
import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public final class StudioProfilersTest {
@Parameterized.Parameters
public static Collection<Boolean> useNewEventPipelineParameter() {
return Arrays.asList(false, true);
}
private final FakeTimer myTimer = new FakeTimer();
private final FakeTransportService myTransportService = new FakeTransportService(myTimer, false);
private final FakeProfilerService myProfilerService = new FakeProfilerService(myTimer);
@Rule public FakeGrpcServer myGrpcServer =
FakeGrpcServer.createFakeGrpcServer("StudioProfilerTestChannel", myTransportService, myProfilerService);
private final ProfilerClient myProfilerClient = new ProfilerClient(myGrpcServer.getName());
private final FakeGrpcServer.CpuService myCpuService = myGrpcServer.getCpuService();
private final FakeIdeProfilerServices myIdeProfilerServices;
private final boolean myNewEventPipeline;
public StudioProfilersTest(boolean useNewEventPipeline) {
myIdeProfilerServices = new FakeIdeProfilerServices();
myIdeProfilerServices.enableEventsPipeline(useNewEventPipeline);
myNewEventPipeline = useNewEventPipeline;
}
@Test
public void testSleepBeforeAppLaunched() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
//Validate we start in the null stage.
assertThat(profilers.getStageClass()).isSameAs(NullMonitorStage.class);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
myTransportService.addDevice(device);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS); // One second must be enough for new devices to be picked up
profilers.setProcess(device, null);
// Validate that just because we add a device, we still have not left the null monitor stage.
assertThat(profilers.getDevice().getSerial()).isEqualTo("FakeDevice");
assertThat(profilers.getStageClass()).isSameAs(NullMonitorStage.class);
// Pick a time to set the device to. Note that this value is arbitrary but the bug this tests
// is exposed if this value is larger than nanoTime.
long timeOnDevice = System.nanoTime() + 1000;
myTimer.setCurrentTimeNs(timeOnDevice);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
// Add a process and validate the stage goes to the monitor stage.
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
// Test that the process was attached correctly
assertThat(profilers.getTimeline().isStreaming()).isTrue();
// Test that the data range has not been inverted
assertThat(profilers.getTimeline().getDataRange().isEmpty()).isFalse();
}
@Test
public void testProfilerStageChange() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
//Validate we start in the null stage.
assertThat(profilers.getStageClass()).isSameAs(NullMonitorStage.class);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
myTransportService.addDevice(device);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS); // One second must be enough for new devices to be picked up
profilers.setProcess(device, null);
// Validate that just because we add a device, we still have not left the null monitor stage.
assertThat(profilers.getDevice().getSerial()).isEqualTo("FakeDevice");
assertThat(profilers.getStageClass()).isSameAs(NullMonitorStage.class);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
// Add a process and validate the stage goes to the monitor stage.
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getStageClass()).isSameAs(StudioMonitorStage.class);
}
@Test
public void testLateConnectionOfPreferredProcess() {
final String PREFERRED_PROCESS = "Preferred";
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
profilers.setPreferredProcess(null, PREFERRED_PROCESS, null);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isNull();
assertThat(profilers.getProcess()).isNull();
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS); // One second must be enough for new devices to be picked up
profilers.setProcess(device, null);
// We are waiting for the preferred process so the process should not be selected.
assertThat(profilers.getDevice().getSerial()).isEqualTo("FakeDevice");
assertThat(profilers.getProcess()).isNull();
Common.Process preferred = createProcess(device.getDeviceId(), 21, PREFERRED_PROCESS, Common.Process.State.ALIVE);
myTransportService.addProcess(device, preferred);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS); // One second must be enough for new devices to be picked up
assertThat(profilers.getDevice()).isEqualTo(device);
assertThat(profilers.getProcess()).isEqualTo(preferred);
assertThat(profilers.getProcesses()).hasSize(2);
assertThat(profilers.getProcesses()).containsAllIn(ImmutableList.of(process, preferred));
}
@Test
public void testSetPreferredProcessDoesNotProfileEarlierProcess() {
final String PREFERRED_PROCESS = "Preferred";
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
profilers.setPreferredProcess(null, PREFERRED_PROCESS, p -> p.getStartTimestampNs() > 5);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isNull();
assertThat(profilers.getProcess()).isNull();
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process earlierProcess = Common.Process.newBuilder()
.setDeviceId(device.getDeviceId())
.setPid(20)
.setName(PREFERRED_PROCESS)
.setState(Common.Process.State.ALIVE)
.setStartTimestampNs(5)
.build();
myTransportService.addDevice(device);
myTransportService.addProcess(device, earlierProcess);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, null);
// The process start time is before the time we start looking for preferred process, so profiler should not have started.
assertThat(profilers.getDevice()).isEqualTo(device);
assertThat(profilers.getProcess()).isNull();
Common.Process afterProcess = Common.Process.newBuilder()
.setDeviceId(device.getDeviceId())
.setPid(21)
.setName(PREFERRED_PROCESS)
.setState(Common.Process.State.ALIVE)
.setStartTimestampNs(10)
.build();
myTransportService.addProcess(device, afterProcess);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isEqualTo(device);
assertThat(profilers.getProcess()).isEqualTo(afterProcess);
assertThat(profilers.getProcesses()).hasSize(2);
assertThat(profilers.getProcesses()).containsAllIn(ImmutableList.of(earlierProcess, afterProcess));
}
@Test
public void testSetNullPreferredProcessDoesNotStartAutoProfiling() {
final String PREFERRED_PROCESS = "Preferred";
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
profilers.setPreferredProcess(null, null, p -> p.getStartTimestampNs() > 5);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isNull();
assertThat(profilers.getProcess()).isNull();
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = Common.Process.newBuilder()
.setDeviceId(device.getDeviceId())
.setPid(21)
.setName(PREFERRED_PROCESS)
.setState(Common.Process.State.ALIVE)
.setStartTimestampNs(10)
.build();
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, null);
assertThat(profilers.getDevice()).isEqualTo(device);
assertThat(profilers.getProcess()).isNull();
assertThat(profilers.getSession()).isEqualTo(Common.Session.getDefaultInstance());
}
@Test
public void testConnectionError() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE)
.toBuilder().setModel("FakeDevice").build();
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
profilers.setPreferredProcess("FakeDevice", "FakeProcess", null);
// This should fail and not find any devices
myTransportService.setThrowErrorOnGetDevices(true);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isNull();
assertThat(profilers.getProcess()).isNull();
// Server "is back up", try again
myTransportService.setThrowErrorOnGetDevices(false);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice().getSerial()).isEqualTo("FakeDevice");
assertThat(profilers.getProcess().getName()).isEqualTo("FakeProcess");
}
@Test
public void testAlreadyConnected() {
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getStage()).isInstanceOf(StudioMonitorStage.class);
}
@Test
public void testTimeResetOnConnectedDevice() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
int nowInSeconds = 42;
myTimer.setCurrentTimeNs(TimeUnit.SECONDS.toNanos(nowInSeconds));
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getTimeline().getDataRange().getMin()).isWithin(0.001).of(TimeUnit.SECONDS.toMicros(nowInSeconds));
assertThat(profilers.getTimeline().getDataRange().getMax()).isWithin(0.001).of(TimeUnit.SECONDS.toMicros(nowInSeconds));
// The timeline has reset in the previous tick, so we need to advance the current time to make sure the next tick advances data range.
myTimer.setCurrentTimeNs(myTimer.getCurrentTimeNs() + FakeTimer.ONE_SECOND_IN_NS * 5);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS * 5);
assertThat(profilers.getTimeline().getDataRange().getMin()).isWithin(0.001).of(TimeUnit.SECONDS.toMicros(nowInSeconds));
assertThat(profilers.getTimeline().getDataRange().getMax()).isWithin(0.001).of(TimeUnit.SECONDS.toMicros(nowInSeconds + 5));
}
@Test
public void testAgentUnattachableAfterMaxRetries() {
Assume.assumeFalse(myNewEventPipeline);
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
AgentData attachedResponse = AgentData.newBuilder().setStatus(AgentData.Status.UNSPECIFIED).build();
myTransportService.setAgentStatus(attachedResponse);
myTransportService.addDevice(FAKE_DEVICE);
myTransportService.addProcess(FAKE_DEVICE, FAKE_PROCESS);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(FAKE_DEVICE, FAKE_PROCESS);
for (int i = 0; i < AGENT_STATUS_MAX_RETRY_COUNT; i++) {
assertThat(profilers.getAgentData().getStatus()).isEqualTo(AgentData.Status.UNSPECIFIED);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
}
assertThat(profilers.getAgentData().getStatus()).isEqualTo(AgentData.Status.UNATTACHABLE);
// Ensures that if the agent becomes attached at a later point, the status will be correct.
attachedResponse = AgentData.newBuilder().setStatus(AgentData.Status.ATTACHED).build();
myTransportService.setAgentStatus(attachedResponse);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getAgentData().getStatus()).isEqualTo(AgentData.Status.ATTACHED);
}
@Test
public void testAgentStatusRetryCachedForSession() {
Assume.assumeFalse(myNewEventPipeline);
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
AgentData attachedResponse = AgentData.newBuilder().setStatus(AgentData.Status.UNSPECIFIED).build();
myTransportService.setAgentStatus(attachedResponse);
myTransportService.addDevice(FAKE_DEVICE);
myTransportService.addProcess(FAKE_DEVICE, FAKE_PROCESS);
Common.Process process2 = FAKE_PROCESS.toBuilder().setPid(2).build();
myTransportService.addProcess(FAKE_DEVICE, process2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(FAKE_DEVICE, FAKE_PROCESS);
profilers.getSessionsManager().endCurrentSession();
Common.Session session1 = profilers.getSession();
// Switch to a different process.
profilers.setProcess(FAKE_DEVICE, process2);
Common.Session session2 = profilers.getSession();
int session2RetryCount;
for (session2RetryCount = 0; session2RetryCount < AGENT_STATUS_MAX_RETRY_COUNT / 2; session2RetryCount++) {
assertThat(profilers.getAgentData().getStatus()).isEqualTo(AgentData.Status.UNSPECIFIED);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
}
// Switch back to the first session, agent status should remain unspecified because it is ended.
profilers.getSessionsManager().setSession(session1);
for (int j = 0; j < AGENT_STATUS_MAX_RETRY_COUNT * 2; j++) {
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
}
assertThat(profilers.getAgentData().getStatus()).isEqualTo(AgentData.Status.UNSPECIFIED);
// Switch back to the second session, we should only need another 5 ticks to reach UNATTACHABLE.
profilers.getSessionsManager().setSession(session2);
for (; session2RetryCount < AGENT_STATUS_MAX_RETRY_COUNT; session2RetryCount++) {
assertThat(profilers.getAgentData().getStatus()).isEqualTo(AgentData.Status.UNSPECIFIED);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
}
assertThat(profilers.getAgentData().getStatus()).isEqualTo(AgentData.Status.UNATTACHABLE);
// Switch to the first session and back should give UNATTACHABLE immediately.
profilers.getSessionsManager().setSession(session1);
profilers.getSessionsManager().setSession(session2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getAgentData().getStatus()).isEqualTo(AgentData.Status.UNATTACHABLE);
}
@Test
public void testAgentAspectFiring() {
AgentStatusAspectObserver observer = new AgentStatusAspectObserver();
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
profilers.addDependency(observer).onChange(ProfilerAspect.AGENT, observer::AgentStatusChanged);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getAgentData()).isEqualTo(AgentData.getDefaultInstance());
assertThat(observer.getAgentStatusChangedCount()).isEqualTo(0);
// Test that status changes if no process is selected does nothing
AgentData attachedResponse = AgentData.newBuilder().setStatus(AgentData.Status.ATTACHED).build();
myTransportService.setAgentStatus(attachedResponse);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess()).isNull();
assertThat(profilers.getAgentData()).isEqualTo(AgentData.getDefaultInstance());
assertThat(observer.getAgentStatusChangedCount()).isEqualTo(0);
// Test that agent status change fires after a process is selected.
Common.Device device = createDevice(AndroidVersion.VersionCodes.O, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process1 = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
Common.Process process2 = createProcess(device.getDeviceId(), 21, "FakeProcess2", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process1);
myTransportService.addProcess(device, process2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess()).isEqualTo(process1);
assertThat(profilers.getAgentData()).isEqualTo(attachedResponse);
assertThat(observer.getAgentStatusChangedCount()).isEqualTo(1);
// Test that manually setting a process fires an agent status change
profilers.setProcess(device, process2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess()).isSameAs(process2);
assertThat(profilers.getAgentData()).isEqualTo(attachedResponse);
assertThat(observer.getAgentStatusChangedCount()).isEqualTo(2);
// Setting the same agent status should not trigger an aspect change.
attachedResponse = AgentData.newBuilder().setStatus(AgentData.Status.ATTACHED).build();
myTransportService.setAgentStatus(attachedResponse);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getAgentData()).isEqualTo(attachedResponse);
assertThat(observer.getAgentStatusChangedCount()).isEqualTo(2);
}
@Test
public void testAgentAspectNotFiredWhenSettingSameDeviceProcess() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
AgentStatusAspectObserver observer = new AgentStatusAspectObserver();
profilers.addDependency(observer).onChange(ProfilerAspect.AGENT, observer::AgentStatusChanged);
// Test that the status changed is fired when the process first gets selected.
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isSameAs(device);
assertThat(profilers.getProcess()).isEqualTo(process);
assertThat(profilers.isAgentAttached()).isFalse();
assertThat(observer.getAgentStatusChangedCount()).isEqualTo(1);
// Test that resetting the same device/process would not trigger the status changed event.
profilers.setProcess(device, process);
assertThat(profilers.getDevice()).isSameAs(device);
assertThat(profilers.getProcess()).isEqualTo(process);
assertThat(profilers.isAgentAttached()).isFalse();
assertThat(observer.getAgentStatusChangedCount()).isEqualTo(1);
}
@Test
public void shouldSelectAlivePreferredProcessWhenRestarted() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
//int nowInSeconds = 42;
//myTransportService.setTimestampNs(TimeUnit.SECONDS.toNanos(nowInSeconds));
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
Common.Device device =
createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE).toBuilder().setModel("FakeDevice").build();
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
profilers.setPreferredProcess("FakeDevice", process.getName(), null);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess().getPid()).isEqualTo(20);
assertThat(profilers.getProcess().getState()).isEqualTo(Common.Process.State.ALIVE);
// Change the alive (active) process to DEAD, and create a new ALIVE process simulating a debugger restart.
myTransportService.removeProcess(device, process);
process = process.toBuilder()
.setState(Common.Process.State.DEAD)
.build();
myTransportService.addProcess(device, process);
// Verify the process is in the dead state.
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess().getPid()).isEqualTo(20);
assertThat(profilers.getProcess().getState()).isEqualTo(Common.Process.State.DEAD);
process = process.toBuilder()
.setPid(21)
.setState(Common.Process.State.ALIVE)
.build();
myTransportService.addProcess(device, process);
// The profiler should select the alive preferred process.
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess().getPid()).isEqualTo(21);
assertThat(profilers.getProcess().getState()).isEqualTo(Common.Process.State.ALIVE);
}
@Test
public void shouldNotSelectPreferredAfterUserSelectsOtherProcess() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE)
.toBuilder().setModel("FakeDevice").build();
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
profilers.setPreferredProcess("FakeDevice", process.getName(), null);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess().getPid()).isEqualTo(20);
assertThat(profilers.getProcess().getState()).isEqualTo(Common.Process.State.ALIVE);
Common.Process otherProcess = createProcess(device.getDeviceId(), 21, "OtherProcess", Common.Process.State.ALIVE);
myTransportService.addProcess(device, otherProcess);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, otherProcess);
// The user selected the other process explicitly
assertThat(profilers.getProcess().getPid()).isEqualTo(21);
assertThat(profilers.getProcess().getState()).isEqualTo(Common.Process.State.ALIVE);
// Should select the other process again
profilers.setProcess(device, null);
assertThat(profilers.getProcess().getPid()).isEqualTo(21);
assertThat(profilers.getProcess().getState()).isEqualTo(Common.Process.State.ALIVE);
}
@Test
public void shouldOpenCpuProfileStageIfStartupProfilingStarted() {
myIdeProfilerServices.enableStartupCpuProfiling(true);
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myCpuService.setStartupProfiling(true);
// To make sure that StudioProfilers#update is called, which in a consequence polls devices and processes,
// and starts a new session with the preferred process name.
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess().getPid()).isEqualTo(20);
assertThat(profilers.getProcess().getState()).isEqualTo(Common.Process.State.ALIVE);
assertThat(profilers.getStage()).isInstanceOf(CpuProfilerStage.class);
}
@Test
public void testProcessStateChangesShouldNotTriggerStageChange() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess().getPid()).isEqualTo(20);
assertThat(Common.Process.State.ALIVE).isEqualTo(profilers.getProcess().getState());
AspectObserver observer = new AspectObserver();
profilers.addDependency(observer).onChange(ProfilerAspect.STAGE, () -> {
assert false;
});
// Change the alive (active) process to DEAD
myTransportService.removeProcess(device, process);
process = process.toBuilder()
.setState(Common.Process.State.DEAD)
.build();
myTransportService.addProcess(device, process);
//Verify the process is in the dead state.
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess().getPid()).isEqualTo(20);
assertThat(Common.Process.State.DEAD).isEqualTo(profilers.getProcess().getState());
}
@Test
public void timelineShouldBeStreamingWhenProcessIsSelected() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getTimeline().isStreaming()).isTrue();
}
@Test
public void timelineShouldStopStreamingWhenRangeIsSelected() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
ProfilerTimeline timeline = profilers.getTimeline();
assertTrue(timeline.isStreaming());
timeline.getDataRange().set(0, FakeTimer.ONE_SECOND_IN_NS);
timeline.getSelectionRange().set(0, 0);
assertFalse(timeline.isStreaming());
timeline.setStreaming(true);
assertTrue(timeline.isStreaming());
timeline.getSelectionRange().set(0, FakeTimer.ONE_SECOND_IN_NS);
assertFalse(timeline.isStreaming());
}
@Test
public void onlineDeviceShouldNotOverrideSelectedOfflineDevice() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
assertThat(profilers.getDevice()).isEqualTo(device);
assertThat(profilers.getProcess()).isEqualTo(process);
// Connect a new device, and marks the old one as disconnected
Common.Device dead_device = device.toBuilder().setState(Common.Device.State.DISCONNECTED).build();
Common.Device device2 = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice2", Common.Device.State.ONLINE);
Common.Process process2 = createProcess(device2.getDeviceId(), 21, "FakeProcess2", Common.Process.State.ALIVE);
myTransportService.updateDevice(device, dead_device);
myTransportService.addDevice(device2);
myTransportService.addProcess(device2, process2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
// Connecting an online device should not override previously selection automatically
assertThat(profilers.getDevice()).isEqualTo(dead_device);
assertThat(profilers.getProcess()).isEqualTo(process);
}
@Test
public void preferredDeviceShouldNotOverrideSelectedDevice() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
profilers.setPreferredProcess("Manufacturer Model", "ProcessName", null);
// A device with a process that can be profiled
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
// The preferred device
Common.Device preferredDevice = createDevice(AndroidVersion.VersionCodes.BASE, "PreferredDevice", Common.Device.State.ONLINE);
preferredDevice = preferredDevice.toBuilder().setManufacturer("Manufacturer").setModel("Model").build();
Common.Process preferredProcess =
createProcess(preferredDevice.getDeviceId(), 21, "PreferredProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(preferredDevice);
myTransportService.addProcess(preferredDevice, preferredProcess);
// Preferred should be selected.
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isEqualTo(preferredDevice);
// Selecting device manually should keep it selected
profilers.setProcess(device, null);
assertThat(profilers.getDevice()).isEqualTo(device);
// Changing states on the preferred device should have no effects.
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isEqualTo(device);
Common.Device offlinePreferredDevice = preferredDevice.toBuilder().setState(Common.Device.State.DISCONNECTED).build();
myTransportService.updateDevice(preferredDevice, offlinePreferredDevice);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isEqualTo(device);
myTransportService.updateDevice(offlinePreferredDevice, preferredDevice);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isEqualTo(device);
}
@Test
public void preferredDeviceHasPriority() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
profilers.setPreferredProcess("Manufacturer Model", "PreferredProcess", null);
// A device with a process that can be profiled
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
// Create the preferred device but have it offline to start with.
Common.Device offlinePreferredDevice = createDevice(AndroidVersion.VersionCodes.BASE, "PreferredDevice", Common.Device.State.OFFLINE);
offlinePreferredDevice = offlinePreferredDevice.toBuilder().setManufacturer("Manufacturer").setModel("Model").build();
Common.Process preferredProcess =
createProcess(offlinePreferredDevice.getDeviceId(), 21, "PreferredProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(offlinePreferredDevice);
myTransportService.addProcess(offlinePreferredDevice, preferredProcess);
// No device should be selected given no online preferred device is found.
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isNull();
assertThat(profilers.getProcess()).isNull();
// Turn the preferred device online and it should be selected.
Common.Device preferredDevice = offlinePreferredDevice.toBuilder().setState(Common.Device.State.ONLINE).build();
myTransportService.updateDevice(offlinePreferredDevice, preferredDevice);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isEqualTo(preferredDevice);
assertThat(profilers.getProcess()).isEqualTo(preferredProcess);
Common.Device preferredDevice2 = createDevice(AndroidVersion.VersionCodes.BASE, "PreferredDevice2", Common.Device.State.ONLINE);
preferredDevice2 = preferredDevice2.toBuilder().setManufacturer("Manufacturer2").setModel("Model2").build();
Common.Process preferredProcess2 = createProcess(preferredDevice2.getDeviceId(), 22, "PreferredProcess2", Common.Process.State.ALIVE);
myTransportService.addDevice(preferredDevice2);
myTransportService.addProcess(preferredDevice2, preferredProcess2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isEqualTo(preferredDevice);
assertThat(profilers.getProcess()).isEqualTo(preferredProcess);
// Updating the preferred device should immediately switch over.
profilers.setPreferredProcess("Manufacturer2 Model2", "PreferredProcess", null);
assertThat(profilers.getDevice()).isEqualTo(preferredDevice2);
assertThat(profilers.getProcess()).isNull();
}
@Test
public void keepSelectedDeviceAfterDisconnectingAllDevices() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device1 = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process1 = createProcess(device1.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device1);
myTransportService.addProcess(device1, process1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
// Connect a new device
Common.Device device2 = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice2", Common.Device.State.ONLINE);
Common.Process process2 = createProcess(device2.getDeviceId(), 21, "FakeProcess2", Common.Process.State.ALIVE);
myTransportService.addDevice(device2);
myTransportService.addProcess(device2, process2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device2, null);
assertThat(profilers.getDevice()).isEqualTo(device2);
// Update device1 state to disconnect
Common.Device disconnectedDevice = Common.Device.newBuilder()
.setDeviceId(device1.getDeviceId())
.setSerial(device1.getSerial())
.setState(Common.Device.State.DISCONNECTED)
.build();
myTransportService.updateDevice(device1, disconnectedDevice);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
// Update device2 state to disconnect
Common.Device disconnectedDevice2 = Common.Device.newBuilder()
.setDeviceId(device2.getDeviceId())
.setSerial(device2.getSerial())
.setState(Common.Device.State.DISCONNECTED)
.build();
myTransportService.updateDevice(device2, disconnectedDevice2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
// Selected device should be FakeDevice2, which was selected before disconnecting all devices
assertThat(profilers.getDevice().getSerial()).isEqualTo("FakeDevice2");
// Make sure the device is disconnected
assertThat(profilers.getDevice().getState()).isEqualTo(Common.Device.State.DISCONNECTED);
}
@Test
public void testProfileOneProcessAtATime() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device1 = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process1 = createProcess(device1.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
Common.Process process2 = createProcess(device1.getDeviceId(), 21, "FakeProcess2", Common.Process.State.ALIVE);
myTransportService.addDevice(device1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(0);
myTransportService.addProcess(device1, process1);
myTransportService.addProcess(device1, process2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device1, process1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess()).isEqualTo(process1);
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(1);
// Switch to another process.
profilers.setProcess(device1, process2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(1);
assertThat(profilers.getProcess()).isEqualTo(process2);
// Connect a new device with a process.
Common.Device device2 = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice2", Common.Device.State.ONLINE);
Common.Process process3 = createProcess(device2.getDeviceId(), 22, "FakeProcess3", Common.Process.State.ALIVE);
myTransportService.addDevice(device2);
myTransportService.addProcess(device2, process3);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
// Switch to the new device + process
profilers.setProcess(device2, process3);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(1);
assertThat(profilers.getProcess()).isEqualTo(process3);
// Update device2 state to disconnect
Common.Device disconnectedDevice2 = device2.toBuilder()
.setState(Common.Device.State.DISCONNECTED)
.build();
myTransportService.updateDevice(device2, disconnectedDevice2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(0);
// Switch back to the first device.
profilers.setProcess(device1, process1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(1);
// Update device1 state to disconnect
Common.Device disconnectedDevice = device1.toBuilder()
.setState(Common.Device.State.DISCONNECTED)
.build();
myTransportService.updateDevice(device1, disconnectedDevice);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(0);
}
@Test
public void testAttachAgentCalledPostO() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.O, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process1 = createProcess(device.getDeviceId(), 1, "FakeProcess1", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isEqualTo(device);
assertThat(profilers.getProcess()).isEqualTo(process1);
assertThat(myNewEventPipeline ? myTransportService.getAgentAttachCalled() : myProfilerService.getAgentAttachCalled()).isTrue();
}
@Test
public void testAttachAgentNotCalledPreO() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.N, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process1 = createProcess(device.getDeviceId(), 1, "FakeProcess1", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process1);
assertThat(profilers.getDevice()).isEqualTo(device);
assertThat(profilers.getProcess()).isEqualTo(process1);
assertThat(myNewEventPipeline ? myTransportService.getAgentAttachCalled() : myProfilerService.getAgentAttachCalled()).isFalse();
}
/**
* We need to account for an scenario where perfd reinstantiates and needs to pass a new client socket to the app. Hence we make the
* same attach agent call from Studio side and let perfd handles the rest.
*/
@Test
public void testAttachAgentEvenIfAlreadyAttached() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
AgentData attachedResponse = AgentData.newBuilder().setStatus(AgentData.Status.ATTACHED).build();
myTransportService.setAgentStatus(attachedResponse);
Common.Device device = createDevice(AndroidVersion.VersionCodes.O, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process1 = createProcess(device.getDeviceId(), 1, "FakeProcess1", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process1);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getDevice()).isEqualTo(device);
assertThat(profilers.getProcess()).isEqualTo(process1);
assertThat(myNewEventPipeline ? myTransportService.getAgentAttachCalled() : myProfilerService.getAgentAttachCalled()).isTrue();
}
@Test
public void testProfilingStops() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE)
.toBuilder().setModel("FakeDevice").build();
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(0);
myTransportService.addProcess(device, process);
profilers.setPreferredProcess("FakeDevice", "FakeProcess", null);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getProcess()).isEqualTo(process);
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(1);
assertThat(profilers.getProcess()).isEqualTo(process);
assertThat(myTimer.isRunning()).isTrue();
// Stop the profiler
profilers.stop();
assertThat(myGrpcServer.getProfiledProcessCount()).isEqualTo(0);
assertThat(profilers.getProcess()).isNull();
assertThat(profilers.getDevice()).isNull();
assertThat(myTimer.isRunning()).isFalse();
}
@Test
public void testNullDeviceKeepsPreviousSession() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device1 = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice1", Common.Device.State.ONLINE);
Common.Process process = createProcess(device1.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
Common.Device device2 = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice2", Common.Device.State.ONLINE);
myTransportService.addDevice(device1);
myTransportService.addProcess(device1, process);
myTransportService.addDevice(device2);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device1, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
Common.Session session = profilers.getSession();
assertThat(profilers.getDevice()).isEqualTo(device1);
assertThat(profilers.getProcess()).isEqualTo(process);
assertThat(session).isNotEqualTo(Common.Session.getDefaultInstance());
assertThat(profilers.getStageClass()).isEqualTo(StudioMonitorStage.class);
// Setting device and process to null should maintain the current session.
profilers.setProcess(null, null);
assertThat(profilers.getDevice()).isEqualTo(null);
assertThat(profilers.getProcess()).isEqualTo(null);
assertThat(profilers.getSession().getSessionId()).isEqualTo(session.getSessionId());
assertThat(profilers.getStageClass()).isEqualTo(StudioMonitorStage.class);
}
@Test
public void testProfilingStopsWithLiveAllocationEnabled() {
// Enable live allocation tracker
myIdeProfilerServices.enableLiveAllocationTracking(true);
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.O, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
assertThat(myTimer.isRunning()).isTrue();
assertThat(profilers.getDevice()).isEqualTo(device);
assertThat(profilers.getProcess()).isEqualTo(process);
// Stop the profiler
profilers.stop();
assertThat(myTimer.isRunning()).isFalse();
assertThat(profilers.getProcess()).isEqualTo(null);
assertThat(profilers.getDevice()).isEqualTo(null);
}
@Test
public void testStoppingTwice() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
// Should be modified when STAGE aspect is fired.
boolean[] stageAspectTriggered = {false};
profilers.addDependency(new AspectObserver())
.onChange(ProfilerAspect.STAGE, () -> stageAspectTriggered[0] = true);
// Check profiler is not stopped.
assertThat(profilers.isStopped()).isFalse();
assertThat(myTimer.isRunning()).isTrue();
// Stop the profiler
profilers.stop();
// Profiler should have stopped and STAGE is supposed to have been fired.
assertThat(stageAspectTriggered[0]).isTrue();
// Check profiler is stopped.
assertThat(profilers.isStopped()).isTrue();
assertThat(myTimer.isRunning()).isFalse();
stageAspectTriggered[0] = false;
// Try to stop the profiler again.
profilers.stop();
// Profiler was already stopped and STAGE is not supposed to have been fired.
assertThat(stageAspectTriggered[0]).isFalse();
}
@Test
public void testBeginAndEndSessionOnProcessChange() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
assertThat(profilers.getSession()).isEqualTo(Common.Session.getDefaultInstance());
// Adds a device without processes. Session should be null.
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
myTransportService.addDevice(device);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS); // One second must be enough for new devices to be picked up
assertThat(profilers.getSession()).isEqualTo(Common.Session.getDefaultInstance());
// Adds a process, which should trigger the session to start.
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession().getStreamId()).isEqualTo(device.getDeviceId());
assertThat(profilers.getSession().getPid()).isEqualTo(process.getPid());
assertThat(profilers.getSession().getEndTimestamp()).isEqualTo(Long.MAX_VALUE);
// Mark the process as dead, which should ends the session.
myTransportService.removeProcess(device, process);
process = process.toBuilder().setState(Common.Process.State.DEAD).build();
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession().getStreamId()).isEqualTo(device.getDeviceId());
assertThat(profilers.getSession().getPid()).isEqualTo(process.getPid());
assertThat(profilers.getSession().getEndTimestamp()).isNotEqualTo(Long.MAX_VALUE);
}
@Test
public void testBeginAndEndSessionOnDeviceChange() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
assertThat(profilers.getSession()).isEqualTo(Common.Session.getDefaultInstance());
// Adds a device with process. Session should start immediately
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS); // One second must be enough for new devices to be picked up
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession().getStreamId()).isEqualTo(device.getDeviceId());
assertThat(profilers.getSession().getPid()).isEqualTo(process.getPid());
assertThat(profilers.getSession().getEndTimestamp()).isEqualTo(Long.MAX_VALUE);
// Killing the device should stop the session
myTransportService.removeProcess(device, process);
Common.Device deadDevice = device.toBuilder().setState(Common.Device.State.DISCONNECTED).build();
Common.Process deadProcess = process.toBuilder().setState(Common.Process.State.DEAD).build();
myTransportService.addDevice(deadDevice);
myTransportService.addProcess(deadDevice, deadProcess);
myTimer.setCurrentTimeNs(FakeTimer.ONE_SECOND_IN_NS);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession().getStreamId()).isEqualTo(device.getDeviceId());
assertThat(profilers.getSession().getPid()).isEqualTo(process.getPid());
assertThat(profilers.getSession().getEndTimestamp()).isNotEqualTo(Long.MAX_VALUE);
}
@Test
public void testSessionDoesNotAutoStartOnSameProcess() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
assertThat(profilers.getSession()).isEqualTo(Common.Session.getDefaultInstance());
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS); // One second must be enough for new devices to be picked up
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
// End the session.
myTransportService.removeProcess(device, process);
process = process.toBuilder().setState(Common.Process.State.DEAD).build();
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
Common.Session session = profilers.getSession();
assertThat(session).isNotNull();
assertThat(session.getStreamId()).isEqualTo(device.getDeviceId());
assertThat(session.getPid()).isEqualTo(process.getPid());
// The same process coming alive should not start a new session automatically.
myTransportService.removeProcess(device, process);
process = process.toBuilder().setState(Common.Process.State.ALIVE).build();
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession()).isEqualTo(session);
}
@Test
public void testNewSessionResetsStage() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
assertThat(profilers.getSession()).isEqualTo(Common.Session.getDefaultInstance());
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS); // One second must be enough for new devices to be picked up
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession()).isNotNull();
assertThat(profilers.getSession().getStreamId()).isEqualTo(device.getDeviceId());
assertThat(profilers.getSession().getPid()).isEqualTo(process.getPid());
assertThat(profilers.getSession().getEndTimestamp()).isEqualTo(Long.MAX_VALUE);
assertThat(profilers.getStage()).isInstanceOf(StudioMonitorStage.class);
// Goes into a different stage
profilers.setStage(new FakeStage(profilers));
assertThat(profilers.getStage()).isInstanceOf(FakeStage.class);
// Ending a session should not leave the stage automatically.
myTransportService.removeProcess(device, process);
process = process.toBuilder().setState(Common.Process.State.DEAD).build();
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession()).isNotNull();
assertThat(profilers.getSession().getStreamId()).isEqualTo(device.getDeviceId());
assertThat(profilers.getSession().getPid()).isEqualTo(process.getPid());
assertThat(profilers.getSession().getEndTimestamp()).isNotEqualTo(Long.MAX_VALUE);
assertThat(profilers.getStage()).isInstanceOf(FakeStage.class);
// Restarting a session on the same process should re-enter the StudioMonitorStage
myTransportService.removeProcess(device, process);
process = process.toBuilder().setState(Common.Process.State.ALIVE).build();
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession()).isNotNull();
assertThat(profilers.getSession().getStreamId()).isEqualTo(device.getDeviceId());
assertThat(profilers.getSession().getPid()).isEqualTo(process.getPid());
assertThat(profilers.getSession().getEndTimestamp()).isEqualTo(Long.MAX_VALUE);
assertThat(profilers.getStage()).isInstanceOf(StudioMonitorStage.class);
}
@Test
public void testGetDirectStagesReturnsOnlyExpectedStages() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
// When energy flag is enabled and device is pre-O, GetDirectStages does not return Energy stage.
myIdeProfilerServices.enableEnergyProfiler(true);
Common.Device deviceNougat = createDevice(AndroidVersion.VersionCodes.N_MR1, "FakeDeviceN", Common.Device.State.ONLINE);
myTransportService.addDevice(deviceNougat);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(deviceNougat, null);
assertThat(profilers.getDevice().getSerial()).isEqualTo("FakeDeviceN");
assertThat(profilers.getDirectStages()).containsExactly(
CpuProfilerStage.class,
MemoryProfilerStage.class,
NetworkProfilerStage.class).inOrder();
// When energy flag is enabled and device is O, GetDirectStages returns Energy stage.
Common.Device deviceOreo = createDevice(AndroidVersion.VersionCodes.O, "FakeDeviceO", Common.Device.State.ONLINE);
myTransportService.addDevice(deviceOreo);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(deviceOreo, null);
assertThat(profilers.getDevice().getSerial()).isEqualTo("FakeDeviceO");
assertThat(profilers.getDirectStages()).containsExactly(
CpuProfilerStage.class,
MemoryProfilerStage.class,
NetworkProfilerStage.class,
EnergyProfilerStage.class).inOrder();
// When energy flag is disabled and device is O, GetDirectStages does not return Energy stage.
myIdeProfilerServices.enableEnergyProfiler(false);
assertThat(profilers.getDevice().getSerial()).isEqualTo("FakeDeviceO");
assertThat(profilers.getDirectStages()).containsExactly(
CpuProfilerStage.class,
MemoryProfilerStage.class,
NetworkProfilerStage.class).inOrder();
}
@Test
public void testGetDirectStageReturnsEnergyOnlyForPostOSession() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
// When energy flag is enabled and the session is pre-O, GetDirectStages does not return Energy stage.
myIdeProfilerServices.enableEnergyProfiler(true);
Common.Session sessionPreO = Common.Session.newBuilder()
.setSessionId(1).setStartTimestamp(FakeTimer.ONE_SECOND_IN_NS).setEndTimestamp(FakeTimer.ONE_SECOND_IN_NS * 2).build();
Common.SessionMetaData sessionPreOMetadata = Common.SessionMetaData.newBuilder()
.setSessionId(1).setType(Common.SessionMetaData.SessionType.FULL).setJvmtiEnabled(false).setStartTimestampEpochMs(1).build();
if (myNewEventPipeline) {
myTransportService.addSession(sessionPreO, sessionPreOMetadata);
}
else {
myProfilerService.addSession(sessionPreO, sessionPreOMetadata);
}
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.getSessionsManager().setSession(profilers.getSessionsManager().getSessionArtifacts().get(0).getSession());
assertThat(profilers.getSessionsManager().getSelectedSessionMetaData().getJvmtiEnabled()).isFalse();
assertThat(profilers.getDirectStages()).containsExactly(
CpuProfilerStage.class,
MemoryProfilerStage.class,
NetworkProfilerStage.class).inOrder();
// When energy flag is enabled and the session is O, GetDirectStages returns Energy stage.
Common.Session sessionO = Common.Session.newBuilder()
.setSessionId(2).setStartTimestamp(FakeTimer.ONE_SECOND_IN_NS).setEndTimestamp(FakeTimer.ONE_SECOND_IN_NS * 2).build();
Common.SessionMetaData sessionOMetadata = Common.SessionMetaData.newBuilder()
.setSessionId(2).setType(Common.SessionMetaData.SessionType.FULL).setJvmtiEnabled(true).setStartTimestampEpochMs(1).build();
if (myNewEventPipeline) {
myTransportService.addSession(sessionO, sessionOMetadata);
}
else {
myProfilerService.addSession(sessionO, sessionOMetadata);
}
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.getSessionsManager().setSession(profilers.getSessionsManager().getSessionArtifacts().get(0).getSession());
assertThat(profilers.getSessionsManager().getSelectedSessionMetaData().getJvmtiEnabled()).isTrue();
assertThat(profilers.getDirectStages()).containsExactly(
CpuProfilerStage.class,
MemoryProfilerStage.class,
NetworkProfilerStage.class,
EnergyProfilerStage.class).inOrder();
// When energy flag is disabled and the session is pre-O, GetDirectStages does not return Energy stage.
myIdeProfilerServices.enableEnergyProfiler(false);
assertThat(profilers.getSessionsManager().getSelectedSessionMetaData().getJvmtiEnabled()).isTrue();
assertThat(profilers.getDirectStages()).containsExactly(
CpuProfilerStage.class,
MemoryProfilerStage.class,
NetworkProfilerStage.class).inOrder();
}
@Test
public void testBuildSessionName() {
Common.Device device1 = Common.Device.newBuilder()
.setManufacturer("Manufacturer")
.setModel("Model")
.setSerial("Serial")
.build();
Common.Device device2 = Common.Device.newBuilder()
.setModel("Model-Serial")
.setSerial("Serial")
.build();
Common.Process process1 = Common.Process.newBuilder()
.setPid(10)
.setAbiCpuArch("x86")
.setName("Process1")
.build();
Common.Process process2 = Common.Process.newBuilder()
.setPid(20)
.setAbiCpuArch("arm")
.setName("Process2")
.build();
assertThat(StudioProfilers.buildSessionName(device1, process1)).isEqualTo("Process1 (Manufacturer Model)");
assertThat(StudioProfilers.buildSessionName(device2, process2)).isEqualTo("Process2 (Model)");
}
@Test
public void testBuildDeviceName() {
Common.Device device = Common.Device.newBuilder()
.setManufacturer("Manufacturer")
.setModel("Model")
.setSerial("Serial")
.build();
assertThat(StudioProfilers.buildDeviceName(device)).isEqualTo("Manufacturer Model");
Common.Device deviceWithEmptyManufacturer = Common.Device.newBuilder()
.setModel("Model")
.setSerial("Serial")
.build();
assertThat(StudioProfilers.buildDeviceName(deviceWithEmptyManufacturer)).isEqualTo("Model");
Common.Device deviceWithSerialInModel = Common.Device.newBuilder()
.setManufacturer("Manufacturer")
.setModel("Model-Serial")
.setSerial("Serial")
.build();
assertThat(StudioProfilers.buildDeviceName(deviceWithSerialInModel)).isEqualTo("Manufacturer Model");
}
@Test
public void testSelectedAppNameFromSession() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess (phone)", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession()).isNotEqualTo(Common.Session.getDefaultInstance());
assertThat(profilers.getSelectedAppName()).isEqualTo("FakeProcess");
}
@Test
public void testSelectedAppNameFromProcessWhenNoSession() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
Common.Process process = createProcess(device.getDeviceId(), 20, "FakeProcess (phone)", Common.Process.State.ALIVE);
myTransportService.addDevice(device);
myTransportService.addProcess(device, process);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.setProcess(device, process);
profilers.getSessionsManager().setSession(Common.Session.getDefaultInstance());
assertThat(profilers.getSelectedAppName()).isEqualTo("FakeProcess");
}
@Test
public void testSelectedAppNameWhenNoProcessAndNoSession() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Device device = createDevice(AndroidVersion.VersionCodes.BASE, "FakeDevice", Common.Device.State.ONLINE);
myTransportService.addDevice(device);
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSession()).isEqualTo(Common.Session.getDefaultInstance());
assertThat(profilers.getProcess()).isNull();
assertThat(profilers.getSelectedAppName()).isEmpty();
}
@Test
public void testSessionViewRangeCaches() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Session finishedSession = Common.Session.newBuilder()
.setSessionId(1).setStartTimestamp(FakeTimer.ONE_SECOND_IN_NS)
.setEndTimestamp(FakeTimer.ONE_SECOND_IN_NS * 2).build();
Common.SessionMetaData finishedSessionMetadata = Common.SessionMetaData.newBuilder()
.setSessionId(1)
.setType(Common.SessionMetaData.SessionType.FULL)
.setStartTimestampEpochMs(1).build();
Common.Session ongoingSession = Common.Session.newBuilder()
.setSessionId(2).setStartTimestamp(0).setEndTimestamp(Long.MAX_VALUE).build();
Common.SessionMetaData ongoingSessionMetadata = Common.SessionMetaData.newBuilder()
.setSessionId(2).setType(Common.SessionMetaData.SessionType.FULL)
.setStartTimestampEpochMs(2).build();
if (myNewEventPipeline) {
myTransportService.addSession(finishedSession, finishedSessionMetadata);
}
else {
myProfilerService.addSession(finishedSession, finishedSessionMetadata);
}
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
if (myNewEventPipeline) {
myTransportService.addSession(ongoingSession, ongoingSessionMetadata);
}
else {
myProfilerService.addSession(ongoingSession, ongoingSessionMetadata);
}
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
// Arbitrary view range min/max to be set for each session
long viewRangeMin = TimeUnit.MILLISECONDS.toMicros(1200);
long viewRangeMax = TimeUnit.MILLISECONDS.toMicros(1600);
// selecting an ongoing session should use the default zoom with streaming enabled
profilers.getSessionsManager().setSession(profilers.getSessionsManager().getSessionArtifacts().get(0).getSession());
assertThat(profilers.getTimeline().getViewRange().getMin()).isWithin(0).of(-ProfilerTimeline.DEFAULT_VIEW_LENGTH_US);
assertThat(profilers.getTimeline().getViewRange().getMax()).isWithin(0).of(0);
assertThat(profilers.getTimeline().isStreaming()).isTrue();
assertThat(profilers.getTimeline().isPaused()).isFalse();
profilers.getTimeline().getViewRange().set(viewRangeMin, viewRangeMax);
// selecting a finished session without a view range cache should use the entire data range
profilers.getSessionsManager().setSession(profilers.getSessionsManager().getSessionArtifacts().get(1).getSession());
assertThat(profilers.getTimeline().getViewRange().getMin()).isWithin(0).of(TimeUnit.SECONDS.toMicros(1));
assertThat(profilers.getTimeline().getViewRange().getMax()).isWithin(0).of(TimeUnit.SECONDS.toMicros(2));
assertThat(profilers.getTimeline().isStreaming()).isFalse();
assertThat(profilers.getTimeline().isPaused()).isTrue();
profilers.getTimeline().getViewRange().set(viewRangeMin, viewRangeMax);
// Navigate back to the ongoing session should still use the default zoom
profilers.getSessionsManager().setSession(profilers.getSessionsManager().getSessionArtifacts().get(0).getSession());
assertThat(profilers.getTimeline().getViewRange().getMin()).isWithin(0).of(-ProfilerTimeline.DEFAULT_VIEW_LENGTH_US);
assertThat(profilers.getTimeline().getViewRange().getMax()).isWithin(0).of(0);
assertThat(profilers.getTimeline().isStreaming()).isTrue();
assertThat(profilers.getTimeline().isPaused()).isFalse();
// Navigate again to the finished session should use the last view range
profilers.getSessionsManager().setSession(profilers.getSessionsManager().getSessionArtifacts().get(1).getSession());
assertThat(profilers.getTimeline().getViewRange().getMin()).isWithin(0).of(viewRangeMin);
assertThat(profilers.getTimeline().getViewRange().getMax()).isWithin(0).of(viewRangeMax);
assertThat(profilers.getTimeline().isStreaming()).isFalse();
assertThat(profilers.getTimeline().isPaused()).isTrue();
// Arbitrarily setting the view range to something beyond the data range should force the timeline to clamp to data range.
profilers.getTimeline().getViewRange().set(TimeUnit.SECONDS.toMicros(-10), TimeUnit.SECONDS.toMicros(10));
profilers.getSessionsManager().setSession(Common.Session.getDefaultInstance());
profilers.getSessionsManager().setSession(profilers.getSessionsManager().getSessionArtifacts().get(1).getSession());
assertThat(profilers.getTimeline().getViewRange().getMin()).isWithin(0).of(TimeUnit.SECONDS.toMicros(1));
assertThat(profilers.getTimeline().getViewRange().getMax()).isWithin(0).of(TimeUnit.SECONDS.toMicros(2));
assertThat(profilers.getTimeline().isStreaming()).isFalse();
assertThat(profilers.getTimeline().isPaused()).isTrue();
}
@Test
public void testMultipleUpdateTicksShouldNotChangeSession() {
StudioProfilers profilers = new StudioProfilers(myProfilerClient, myIdeProfilerServices, myTimer);
Common.Session finishedSession = Common.Session.newBuilder()
.setSessionId(1).setStartTimestamp(FakeTimer.ONE_SECOND_IN_NS)
.setEndTimestamp(FakeTimer.ONE_SECOND_IN_NS * 2).build();
Common.SessionMetaData finishedSessionMetadata = Common.SessionMetaData.newBuilder()
.setSessionId(1)
.setType(Common.SessionMetaData.SessionType.FULL)
.setStartTimestampEpochMs(1).build();
Common.Session ongoingSession = Common.Session.newBuilder()
.setSessionId(2).setStartTimestamp(0).setEndTimestamp(Long.MAX_VALUE).build();
Common.SessionMetaData ongoingSessionMetadata = Common.SessionMetaData.newBuilder()
.setSessionId(2).setType(Common.SessionMetaData.SessionType.FULL)
.setStartTimestampEpochMs(2).build();
if (myNewEventPipeline) {
myTransportService.addSession(finishedSession, finishedSessionMetadata);
}
else {
myProfilerService.addSession(finishedSession, finishedSessionMetadata);
}
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
if (myNewEventPipeline) {
myTransportService.addSession(ongoingSession, ongoingSessionMetadata);
}
else {
myProfilerService.addSession(ongoingSession, ongoingSessionMetadata);
}
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
profilers.getSessionsManager().setSession(profilers.getSessionsManager().getSessionArtifacts().get(0).getSession());
assertThat(profilers.getSessionsManager().getSelectedSession().getSessionId()).isEqualTo(ongoingSession.getSessionId());
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSessionsManager().getSelectedSession().getSessionId()).isEqualTo(ongoingSession.getSessionId());
profilers.getSessionsManager().setSession(profilers.getSessionsManager().getSessionArtifacts().get(1).getSession());
assertThat(profilers.getSessionsManager().getSelectedSession().getSessionId()).isEqualTo(finishedSession.getSessionId());
myTimer.tick(FakeTimer.ONE_SECOND_IN_NS);
assertThat(profilers.getSessionsManager().getSelectedSession().getSessionId()).isEqualTo(finishedSession.getSessionId());
}
private static Common.Device createDevice(int featureLevel, @NotNull String serial, @NotNull Common.Device.State state) {
return Common.Device.newBuilder()
.setDeviceId(serial.hashCode())
.setFeatureLevel(featureLevel)
.setSerial(serial)
.setState(state)
.build();
}
private Common.Process createProcess(long deviceId, int pid, @NotNull String name, Common.Process.State state) {
return Common.Process.newBuilder()
.setDeviceId(deviceId)
.setPid(pid)
.setName(name)
.setState(state)
.build();
}
private static class AgentStatusAspectObserver extends AspectObserver {
private int myAgentStatusChangedCount;
void AgentStatusChanged() {
myAgentStatusChangedCount++;
}
int getAgentStatusChangedCount() {
return myAgentStatusChangedCount;
}
}
private static class FakeStage extends Stage {
private FakeStage(@NotNull StudioProfilers profilers) {
super(profilers);
}
@Override
public void enter() {
}
@Override
public void exit() {
}
}
}