package rhogenwizard.sdk.task;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import rhogenwizard.ConsoleHelper;
import rhogenwizard.OSHelper;
import rhogenwizard.OSValidator;
import rhogenwizard.ProcessListViewer;
import rhogenwizard.StringUtils;
import rhogenwizard.debugger.backend.DebugServer;
import rhogenwizard.debugger.backend.DebugState;
import rhogenwizard.debugger.backend.DebugVariableType;
import rhogenwizard.debugger.backend.IDebugCallback;
import rhogenwizard.sdk.task.generate.GenerateRhodesAppTask;
public class RunDebugRhodesAppTaskTest
{
private SynchronousQueue<String> m_eventQueue;
private Semaphore m_semaphore;
private static class DebugCallback implements IDebugCallback
{
private final SynchronousQueue<String> m_eventQueue;
private final Semaphore m_semaphore;
public DebugCallback(SynchronousQueue<String> eventQueue, Semaphore semaphore)
{
m_eventQueue = eventQueue;
m_semaphore = semaphore;
}
@Override
public void connected()
{
send("connected");
}
@Override
public void stopped(DebugState state, String file, int line, String className, String method)
{
send("stopped [" + DebugState.getName(state) + "] [" + file + "] [" + line + "] [" + className
+ "] [" + method + "]");
}
@Override
public void resumed()
{
send("resumed");
}
@Override
public void evaluation(boolean valid, String code, String value)
{
send("evaluation [" + valid + "] [" + code + "] [" + value + "]");
}
@Override
public void unknown(String cmd)
{
send("unknown [" + cmd + "]");
}
@Override
public void exited()
{
send("exited");
}
@Override
public void watch(DebugVariableType type, String variable, String value)
{
send("watch [" + DebugVariableType.getName(type) + "] [" + variable + "] [" + value + "]");
}
@Override
public void watchBOL(DebugVariableType type)
{
send("watchBOL [" + DebugVariableType.getName(type) + "]");
}
@Override
public void watchEOL(DebugVariableType type)
{
send("watchEOL [" + DebugVariableType.getName(type) + "]");
}
private void send(String event)
{
try
{
m_eventQueue.put(event);
m_semaphore.acquire();
}
catch (InterruptedException e)
{
throw new RuntimeException("Can not send event. Impossible!", e);
}
}
}
private static final String workspaceFolder = new File(System.getProperty("java.io.tmpdir"),
"junitworkfiles").getPath();
@BeforeClass
public static void setUpBeforeClass() throws Exception
{
ConsoleHelper.setupNullConsoles();
}
@AfterClass
public static void tearDownAfterClass() throws Exception
{
}
@Before
public void setUp() throws Exception
{
m_eventQueue = new SynchronousQueue<String>();
m_semaphore = new Semaphore(0);
OSHelper.deleteFolder(workspaceFolder);
File newWsFodler = new File(workspaceFolder);
newWsFodler.mkdir();
}
@After
public void tearDown() throws Exception
{
OSHelper.deleteFolder(workspaceFolder);
}
@Test
public void testRunDebugRhodesAppTask() throws Throwable
{
String appName = "app";
String projectLocation = OSHelper.concat(workspaceFolder, appName).getPath();
String signature1 = OSValidator.isWindows() ? "rhosimulator.exe -approot=\'"
+ unixSlashes(projectLocation) + "\'" : "RhoSimulator -approot=/private" + projectLocation;
String signature2 = OSValidator.isWindows() ? "cmd /c rhodes.bat app app"
: "rake run:android:rhosimulator_debug rho_debug_port=9000 rho_reload_app_changes=0";
ProcessListViewer rhosimViewer = new ProcessListViewer(signature1);
ProcessListViewer rakeViewer = new ProcessListViewer(signature2);
try
{
// create application
{
RunTask task = new GenerateRhodesAppTask(workspaceFolder, appName);
task.run();
assertTrue(task.isOk());
}
// write new application.rb
{
String text[] = {
/* 01 */"require 'rho/rhoapplication'",
/* 02 */"class AppApplication < Rho::RhoApplication",
/* 03 */" def initialize",
/* 04 */" super",
/* 05 */" x = 0",
/* 06 */" x = x + 1",
/* 07 */" $y = 11",
/* 08 */" m",
/* 09 */" $y = $y + 22",
/* 10 */" end",
/* 11 */" def m",
/* 12 */" z = 0",
/* 13 */" zz = 0",
/* 14 */" end",
/* 15 */"end",
/* 16 */"" };
String appRb = OSHelper.concat(projectLocation, "app", "application.rb").getPath();
writeTextFile(appRb, StringUtils.join("\n", text));
}
// start debug server
DebugCallback debugCallback = new DebugCallback(m_eventQueue, m_semaphore);
final DebugServer debugServer = new DebugServer(debugCallback);
final Throwable[] exception = new Throwable[1];
Thread debugServerThread = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
debugServer.run();
}
catch (Throwable t)
{
exception[0] = t;
}
}
});
debugServerThread.start();
// run debug Rhodes application [android] [rhosimulator]
{
// ILaunch launch = new Launch(null, ILaunchManager.DEBUG_MODE, null);
//
// RunTask task = new RunDebugRhodesAppTask(launch, projectLocation, appName,
// PlatformType.eAndroid, false, false, null, null, null);
// task.run();
// assertTrue(task.isOk());
}
suspend("connected");
debugServer.debugBreakpoint("application.rb", 5);
debugServer.debugBreakpoint("application.rb", 6);
debugServer.debugRemoveBreakpoint("application.rb", 5);
pass("unknown [HOST=127.0.0.1]", "unknown [PORT=9000]", "unknown [DEBUG PATH="
+ unixSlashes(prependPrivate(OSHelper.concat(projectLocation, "app").getPath())) + "/]",
"stopped [breakpoint] [application.rb] [6] [AppApplication] [initialize]");
debugServer.debugEvaluate("x");
pass("evaluation [true] [x] [0]");
debugServer.debugBreakpoint("application.rb", 7);
debugServer.debugResume();
pass("resumed", "stopped [breakpoint] [application.rb] [7] [AppApplication] [initialize]");
debugServer.debugEvaluate("x");
pass("evaluation [true] [x] [1]");
debugServer.debugStepOver();
pass("unknown [STEPOVER start]", "resumed",
"stopped [stopped (over)] [application.rb] [8] [AppApplication] [initialize]");
debugServer.debugEvaluate("$y");
pass("evaluation [true] [$y] [11]");
debugServer.debugBreakpoint("application.rb", 12);
debugServer.debugResume();
pass("resumed", "stopped [breakpoint] [application.rb] [12] [AppApplication] [m]");
debugServer.debugBreakpoint("application.rb", 13);
debugServer.debugRemoveAllBreakpoints();
debugServer.debugStepReturn();
pass("resumed", "stopped [stopped (return)] [application.rb] [9] [AppApplication] [initialize]");
debugServer.debugEvaluate("(1+2");
pass("evaluation [false] [(1+2] [\""
+ prependPrivate(unixSlashes(OSHelper.concat(projectLocation, "app", "application.rb")
.getPath())) + ":9: syntax error, unexpected $end, expecting ')'\"]");
debugServer.debugEvaluate("\"\\\\n\"");
pass("evaluation [true] [\"\\\\n\"] [\"\\\\n\"]");
debugServer.debugEvaluate("$y\n2+2 # comment");
pass("evaluation [true] [$y\n2+2 # comment] [4]");
debugServer.debugTerminate();
pass("exited");
debugServer.shutdown();
resume();
debugServerThread.join();
if (exception[0] != null)
{
throw exception[0];
}
}
finally
{
OSHelper.killProcesses(rhosimViewer.getNewProcesses());
OSHelper.killProcesses(rakeViewer.getNewProcesses());
}
}
private void suspend(String s) throws InterruptedException
{
String event = m_eventQueue.poll(10, TimeUnit.SECONDS);
if (event == null)
{
fail("timeout for \"" + s + "\"");
}
assertEquals(s, event);
}
private void resume()
{
assertEquals(0, m_semaphore.availablePermits());
m_semaphore.release();
}
private void pass(String... events) throws InterruptedException
{
for (String event : events)
{
resume();
suspend(event);
}
}
private static void writeTextFile(String filename, String text) throws IOException
{
FileWriter fw = new FileWriter(filename);
try
{
fw.write(text);
}
finally
{
fw.close();
}
}
private static String prependPrivate(String path)
{
return ((OSValidator.isWindows()) ? "" : "/private") + path;
}
private static String unixSlashes(String path)
{
return path.replace('\\', '/');
}
}