package com.simpligility.maven.plugins.android;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.isNull;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.fail;
import static org.powermock.api.easymock.PowerMock.createMock;
import static org.powermock.api.easymock.PowerMock.mockStatic;
import java.io.IOException;
import java.util.List;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.TimeoutException;
@RunWith( PowerMockRunner.class )
@PrepareForTest(
{ AndroidDebugBridge.class, CommandExecutor.Factory.class } )
@Ignore("Does not work anymore with new sdk")
public class AbstractEmulatorMojoTest
{
private static final String AVD_NAME = "emulator";
private static final long DEFAULT_TIMEOUT = 500;
private AbstractEmulatorMojoToTest abstractEmulatorMojo;
private CommandExecutor mockExecutor;
private AndroidDebugBridge mockAndroidDebugBridge;
@Before
public void setUp() throws Exception
{
mockExecutor = PowerMock.createNiceMock( CommandExecutor.class );
mockExecutor.executeCommand( anyObject( String.class ), isNull( List.class ) );
PowerMock.replay( mockExecutor );
mockStatic( CommandExecutor.Factory.class );
expect( CommandExecutor.Factory.createDefaultCommmandExecutor() ).andReturn( mockExecutor );
PowerMock.replay( CommandExecutor.Factory.class );
mockAndroidDebugBridge = createMock( AndroidDebugBridge.class );
abstractEmulatorMojo = new AbstractEmulatorMojoToTest();
}
@Test
public void testStartAndroidEmulatorWithTimeoutToConnect() throws MojoExecutionException, ExecutionException
{
boolean onlineAtSecondTry = false;
int extraBootStatusPollCycles = -1;//ignored
abstractEmulatorMojo.setWait( DEFAULT_TIMEOUT );
IDevice emulatorDevice = withEmulatorDevice( onlineAtSecondTry, extraBootStatusPollCycles );
withConnectedDebugBridge( emulatorDevice );
try
{
abstractEmulatorMojo.startAndroidEmulator();
fail();
}
catch ( MojoExecutionException e )
{
verify( mockExecutor );
}
}
@Test
public void testStartAndroidEmulatorAlreadyBooted() throws MojoExecutionException, ExecutionException
{
boolean onlineAtSecondTry = true;
int extraBootStatusPollCycles = 0;
abstractEmulatorMojo.setWait( DEFAULT_TIMEOUT );
IDevice emulatorDevice = withEmulatorDevice( onlineAtSecondTry, extraBootStatusPollCycles );
withConnectedDebugBridge( emulatorDevice );
abstractEmulatorMojo.startAndroidEmulator();
verify( mockExecutor );
}
@Test
public void testStartAndroidEmulatorWithOngoingBoot() throws MojoExecutionException, ExecutionException
{
boolean onlineAtSecondTry = true;
int extraBootStatusPollCycles = 1;
abstractEmulatorMojo.setWait( extraBootStatusPollCycles * 5000 + 500 );
IDevice emulatorDevice = withEmulatorDevice( onlineAtSecondTry, extraBootStatusPollCycles );
withConnectedDebugBridge( emulatorDevice );
abstractEmulatorMojo.startAndroidEmulator();
verify( mockExecutor );
}
@Test
public void testStartAndroidEmulatorWithBootTimeout() throws MojoExecutionException, ExecutionException
{
boolean onlineAtSecondTry = true;
int extraBootStatusPollCycles = -1;
abstractEmulatorMojo.setWait( DEFAULT_TIMEOUT );
IDevice emulatorDevice = withEmulatorDevice( onlineAtSecondTry, extraBootStatusPollCycles );
withConnectedDebugBridge( emulatorDevice );
try
{
abstractEmulatorMojo.startAndroidEmulator();
fail();
}
catch ( MojoExecutionException e )
{
verify( mockExecutor );
}
}
/**
* @param onlineAtSecondTry
* @param extraBootStatusPollCycles < 0 to simulate 'stuck in boot animation'
* @return
*/
private IDevice withEmulatorDevice( boolean onlineAtSecondTry, int extraBootStatusPollCycles )
{
IDevice emulatorDevice = createMock( IDevice.class );
expect( emulatorDevice.getAvdName() ).andReturn( AVD_NAME ).atLeastOnce();
expect( emulatorDevice.isEmulator() ).andReturn( true ).atLeastOnce();
if ( onlineAtSecondTry )
{
try
{
expect( emulatorDevice.isOnline() ).andReturn( false ).andReturn( true );
if ( extraBootStatusPollCycles < 0 )
{
//Simulate 'stuck in boot animation'
expect( emulatorDevice.getPropertySync( "dev.bootcomplete" ) )
.andReturn( null ).atLeastOnce();
expect( emulatorDevice.getPropertySync( "sys.boot_completed" ) )
.andReturn( null ).atLeastOnce();
expect( emulatorDevice.getPropertySync( "init.svc.bootanim" ) )
.andReturn( null ).once()
.andReturn( "running" ).atLeastOnce(); //never changes to "stopped"
}
else if ( extraBootStatusPollCycles == 0 )
{
//Simulate 'already booted'
expect( emulatorDevice.getPropertySync( "dev.bootcomplete" ) )
.andReturn( "1" ).once(); //to be cached
expect( emulatorDevice.getPropertySync( "sys.boot_completed" ) )
.andReturn( "1" ).once(); //to be cached
expect( emulatorDevice.getPropertySync( "init.svc.bootanim" ) )
.andReturn( "stopped" ).once(); //to be cached
}
else if ( extraBootStatusPollCycles == 1 )
{
//Simulate 'almost booted (1 extra poll)'
expect( emulatorDevice.getPropertySync( "dev.bootcomplete" ) )
.andReturn( null ).once()
.andReturn( "1" ).once(); //to be cached
expect( emulatorDevice.getPropertySync( "sys.boot_completed" ) )
.andReturn( null ).once()
.andReturn( "1" ).once(); //to be cached
expect( emulatorDevice.getPropertySync( "init.svc.bootanim" ) )
.andReturn( "running" ).once()
.andReturn( "stopped" ).once(); //to be cached
}
else if( extraBootStatusPollCycles >=3 )
{
//Simulate 'almost booted (>=3 extra polls)'
expect( emulatorDevice.getPropertySync( "dev.bootcomplete" ) )
.andReturn( null ).times( extraBootStatusPollCycles - 1 )
.andReturn( "1" ).once(); //to be cached
expect( emulatorDevice.getPropertySync( "sys.boot_completed" ) )
.andReturn( null ).times( extraBootStatusPollCycles - 1 )
.andReturn( "1" ).once(); //to be cached
expect( emulatorDevice.getPropertySync( "init.svc.bootanim" ) )
.andReturn( null ).times( extraBootStatusPollCycles / 2 )
.andReturn( "running" ).times( extraBootStatusPollCycles / 2 + extraBootStatusPollCycles % 2 )
.andReturn( "stopped" ).once(); //to be cached
}
else if( extraBootStatusPollCycles >=2 )
{
//Simulate 'almost booted (>=2 extra polls)'
expect( emulatorDevice.getPropertySync( "dev.bootcomplete" ) )
.andReturn( null ).times( extraBootStatusPollCycles - 1 )
.andReturn( "1" ).once(); //to be cached
expect( emulatorDevice.getPropertySync( "sys.boot_completed" ) )
.andReturn( null ).times( extraBootStatusPollCycles - 1 )
.andReturn( "1" ).once(); //to be cached
expect( emulatorDevice.getPropertySync( "init.svc.bootanim" ) )
.andReturn( "running" ).times( extraBootStatusPollCycles -1 )
.andReturn( "stopped" ).once(); //to be cached
}
}
catch ( TimeoutException e)
{
throw new RuntimeException( "Unexpected checked exception during mock setup", e );
}
catch ( AdbCommandRejectedException e)
{
throw new RuntimeException( "Unexpected checked exception during mock setup", e );
}
catch ( ShellCommandUnresponsiveException e)
{
throw new RuntimeException( "Unexpected checked exception during mock setup", e );
}
catch ( IOException e )
{
throw new RuntimeException( "Unexpected checked exception during mock setup", e );
}
}
else
{
expect( emulatorDevice.isOnline() ).andReturn( false ).atLeastOnce();
}
replay( emulatorDevice );
return emulatorDevice;
}
private void withConnectedDebugBridge( IDevice emulatorDevice )
{
expect( mockAndroidDebugBridge.isConnected() ).andReturn( true );
expect( mockAndroidDebugBridge.hasInitialDeviceList() ).andReturn( true );
expect( mockAndroidDebugBridge.getDevices() ).andReturn( new IDevice[ 0 ] ).andReturn( new IDevice[]
{ emulatorDevice } ).atLeastOnce();
replay( mockAndroidDebugBridge );
}
private class AbstractEmulatorMojoToTest extends AbstractEmulatorMojo
{
private long wait = DEFAULT_TIMEOUT;
public long getWait()
{
return wait;
}
public void setWait( long wait )
{
this.wait = wait;
}
@Override
protected AndroidSdk getAndroidSdk()
{
return new SdkTestSupport().getSdk_with_platform_default();
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException
{
}
@Override
protected AndroidDebugBridge initAndroidDebugBridge() throws MojoExecutionException
{
return mockAndroidDebugBridge;
}
@Override
String determineAvd()
{
return AVD_NAME;
}
@Override
String determineWait()
{
return String.valueOf( wait );
}
}
}