Saturday, August 29, 2015

Creating a HelloWorld Android App for Amazon Fire Phone

I've tried creating a sample project for Amazon Fire Phone and found a few issues and workarounds. I thought it might be helpful to share them here. It might be useful to Android developers getting started with Amazon Fire Phone programming.

I've followed steps on setting up development environment provided by the Amazon site. Once I've got an Amazon Fire Phone SDK Addon installed, I've created a sample app in Android Studio v1.3.2. In this example, I am using buildToolsVersion "23.0.0"

Here are the steps for creating a new project:

Android Studio v1.3.2.
- File > New > New Project
   - Application Name: HelloWorld
   - Company Domain: practice.mdzyuba.com
   - Next
- Check on Phone and Tablet box
   - Select Minimum SDK: API 17: Android 4.2 (Jelly Bean)
   - Next
- Select a Blank Activity to be added
   - Next
- Finish

The projects has failed to compile and I've got a few errors:

/Users/mykola/Code/practice/android_practice/amazon/HelloWorld/app/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.0/res/values-v23/values-v23.xml
Error:(2) Error retrieving parent for item: No resource found that matches the given name 'android:TextAppearance.Material.Widget.Button.Inverse'.
Error:(2) Error retrieving parent for item: No resource found that matches the given name 'android:Widget.Material.Button.Colored'.

It looks like an Android Studio issue https://code.google.com/p/android/issues/detail?id=183478.

A workaround is to change or comment out the com.android.support:appcompat dependency in build.gradle:

app/build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0"

    defaultConfig {
        applicationId "com.mdzyuba.practice.sample1"
        minSdkVersion 17
        targetSdkVersion 17
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
//    compile 'com.android.support:appcompat-v7:23.0.0'
    compile "com.android.support:appcompat-v7:22.+"
}

After that, the project build is successful.

Next, I change the gradle version as suggested by the Amazon instructions:

build.gradle:
   dependencies {
//        classpath 'com.android.tools.build:gradle:1.3.0'
        classpath 'com.amazon.device.tools.build:gradle:1.1.+'

Now, I've got an error: 

Error:Gradle 2.4 requires Android Gradle plugin 1.2.0 (or newer)  but project is using version 1.1.3.
Please use Android Gradle plugin 1.2.0 or newer.
Fix plugin version and sync project

I've modified Project Structure > Project > Gradle version: 2.2.1. I've got this version from the Amazon examples. After that, I've got a few more errors

.../Sample1/app/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.2.1/res/values-v21/values-v21.xml
Error:(2) Error retrieving parent for item: No resource found that matches the given name 'android:TextAppearance.Material'.

So, I've decided to comment out com.android.support:appcompat dependency completely:

app/build.gradle:

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
//    compile 'com.android.support:appcompat-v7:23.0.0'
//    compile "com.android.support:appcompat-v7:22.+"
}

Now, I am getting a style error:

.../Sample1/app/src/main/res/values/styles.xml
Error:(2) Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light.DarkActionBar'.

Since there is no appcompat dependency, I've modified AppTheme in styles.xml to have a theme provided by the Android SDK:
   
Next error:
 
.../Sample1/app/src/main/res/menu/menu_main.xml
Error:(5) No resource identifier found for attribute 'showAsAction' in package 'com.mdzyuba.practice.sample1'

I just removed app:showAsAction="never"  attribute from the menu item element:


< item android:id="@+id/action_settings" android:title="@string/action_settings"

        android:orderInCategory="100" >


Next, I've got following errors:
.../Sample1/app/src/main/java/com/mdzyuba/practice/sample1/MainActivity.java
Error:(3, 30) error: package android.support.v7.app does not exist
Error:(8, 35) error: cannot find symbol class AppCompatActivity
Error:(10, 5) error: method does not override or implement a method from a supertype
Error:(12, 9) error: cannot find symbol variable super
Error:(13, 9) error: cannot find symbol method setContentView(int)
Error:(16, 5) error: method does not override or implement a method from a supertype
Error:(19, 9) error: cannot find symbol method getMenuInflater()

I've removed a dependency on the support lib in MainActivity class:

// import android.support.v7.app.AppCompatActivity;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends Activity {

Finally, good news: BUILD SUCCESSFUL :)

Running the app:


Hope it might be helpful and save time to anybody else who is creating apps for Amazon Phone with Android Studio.

Saturday, January 3, 2015

cucumber-jvm and Android Studio


During this Winter holidays, I've been exploring a Behavior Driven Development and experimented with a couple of testing frameworks: Calabash and cucumber-jvm.

In this post, I would like to describe setting up cucumber-jvm for Android Studio projects.

I've extended cucumber-jvm examples with a version for Android Studio. Here is a copy in my github branch: https://github.com/mdzyuba/cucumber-jvm/tree/add_android_studio_example/examples/android/android-studio/Cukeulator.

In addition to the example code, here is some details on how it was created.

Dependencies


The cucumber-jvm requires several libraries to be added to a project: https://github.com/cucumber/cucumber-jvm/tree/master/android

I've found it's easier to add those dependencies with the Android Studio project setup dialog because it takes care of the jar versions for you.

File > Project Structure > Modules > app > Dependencies > + > Library dependency
  > type "cucumber" in the search dialog and submit a search request
  > select
      cucumber-core.jar,
      cucumber-html.jar,
      cucumber-java.jar,
      cucumber-junit.jar,
      cucumber-jvm-deps.jar,
      gherkin.jar.

 Make sure to set the scope for the libs as Test compile:


The cucumber-android.jar contains an apklib. Turns out Android Studio does not support it yet. Please see this post for more details.

A workaround is to build the jar yourself or just append @jar to the dependency:

androidTestCompile 'info.cukes:cucumber-android:1.2.0@jar'

Note, the `@jar` suffix is required in order to use the embedded jar file.

If you like to build the jar yourself, make a copy of cucumber-jvm

git clone https://github.com/cucumber/cucumber-jvm.git

Build cucumber-android jar following the steps described here: https://github.com/cucumber/cucumber-jvm/tree/master/android

Copy the jar:

cp android/target/cucumber-android-1.2.1-SNAPSHOT.jar you_project/app/libs

Add a dependency on a jar file to your project

File > Project Structure > Modules > app > Dependencies > + > File dependency
> select the cucumber-android-1.2.1-SNAPSHOT.jar
> select Test compile scope.

Once this is done, the build.gradle should have something like this:

dependencies {
    androidTestCompile 'info.cukes:cucumber-core:1.2.0'
    androidTestCompile 'info.cukes:cucumber-html:0.2.3'
    androidTestCompile 'info.cukes:cucumber-java:1.2.0'
    androidTestCompile 'info.cukes:cucumber-junit:1.2.0'
    androidTestCompile 'info.cukes:cucumber-jvm-deps:1.0.3'
    androidTestCompile 'info.cukes:cucumber-picocontainer:1.2.0'
    androidTestCompile 'info.cukes:gherkin:2.12.2'
    androidTestCompile 'junit:junit:4.12'
    androidTestCompile files('libs/cucumber-android-1.2.1-SNAPSHOT.jar')
}


assets/features


The cucumber tests are stored in a assets/features folder. I placed them under androidTest folder:

Project/app/src/androidTest/assets

Then updated build.gradle with assets.srcDirs for androidTest:

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.1"

    defaultConfig {
        applicationId "cukeulator.android.example.cucumber.cukeulator"
        minSdkVersion 19
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"

        testApplicationId "cukeulator.android.example.cucumber.cukeulator.test"
        testInstrumentationRunner "cucumber.api.android.CucumberInstrumentation"
    }
    sourceSets {
        androidTest {
            assets.srcDirs = ['src/androidTest/assets']
        }
    }

At this point the project should build with no errors.

Test Code


The actual tests code is described here:
https://github.com/cucumber/cucumber-jvm/tree/master/android
and in the project examples.

There are two things required by cucumber-jvm: the tests code should be in the same package as the test packageId, and the test runner should be cucumber.api.android.CucumberInstrumentation. Therefore, I've added these two lines to the defaultConfig part of the build.gradle:

 testApplicationId "cukeulator.android.example.cucumber.cukeulator.test"
 testInstrumentationRunner "cucumber.api.android.CucumberInstrumentation"

Test Run Configuration


In order to create a new test run configuration for cucumber tests in Android Studio, select

Run > Edit Configurations
Click + and Select Android Tests
Specify:
- Test name: CalculatorTest
- Module: app
- Specific instrumentation runner: cucumber.api.android.CucumberInstrumentation
Click Ok



If you like, you can  run the tests from a command line:

cd your_project_folder
./gradlew connectedCheck

Test Run Results


The test results are displayed in the Android Studio:



Wednesday, November 25, 2009

Flash on Google Sites

This is a brief how-to add a Flash on a Google Site.

Google Sites allows adding Flash embedded into a Google Gadget. I uploaded a swf file to my Google Site - FlexWebTools and found a gadget that allows displaying any SWF on a Google Site page. It is called AnyFlash. It is pretty easy to use and it worked fine in my Firefox 3.5.5 on Mac OS X 10.5.8. Then I send a link to my site to my friends and they discovered that the SWF was not displayed properly on IE6 and in Firefox on Linux. I reviewed AnyFlash source code and eventually found that there is a bug 939 in the Google Gadget API. Therefore, I created a new gadget that does not use the gadgets.flash.embedFlash() method and works well with Firefox, Safari and IE6 browsers on Mac, Windows and Linux.

While working on the gadget, I found a few different ways of embedding Flash into a gadget. Some of them are pretty straightforward like simply pointing to a swf in the context tag. Some are more sophisticated. For instance, they would display images while SWF is loading, provide controls to start the application, etc. The source code for public Google Gadgets is available for preview. It is really fun to read and there are a lot of interesting stuff in the plugins code.

I compared a few SWF embedding options and decided to use SWFObject 2 API because it comes with lots of pretty helpful features that ensure a smooth SWF loading. By the way, it is used by Flash Builder too. So, I created a gadget called FlashWrapper. It is public and anyone is welcome to use it.

The setup and configuration is pretty easy. Once you add it to your page, the page will display a dialog where you can specify your SWF URL, the width and height of the SWF, the minimum required browser version (optional), the the background color (optional). The SWF could be stored on a Google Site, or on any other site.

Please let me know if you find any bugs or experience any problems with it. I hope FlashWrapper will be useful.

Thursday, May 22, 2008

Testing Remote Data Services with FlexUnit

This is a short example that describes an approach on testing a Flex remote data access code with FlexUnit. The access to the remote services in Flex is provided via HTTPService, WebService or RemoteObject classes. The requests to the data services are handled asynchronously. That adds a bit more complexity to the unit testing. In this example, I will share a design idea on how to test a client side of the remote data services. This is something I came up with working on my "pet" project.


The "pet" project is a Web application that is based on a three tier architecture: an SQL database, a middleware part implemented with Java, Hibernate, and BlazeDS, and a client tier implemented with Flex. The example shown here works on a client side and tests access to the server-side part of the application. The tests covered in this example are designed to test a user login operation.


The login operation is defined on the server-side class called UserService.


public class UserService {

/**
* User login operation.
*
* @param username
* the user name.
* @param password
* the password.
* @return a User object or null if username is not found or password is not
* valid.
*/
public User login(String username, String password) {
User user = null;
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
List users = session.createQuery(
"from User user where user.username = ?")
.setString(0, username).list();
if (users.size() == 1) {
user = (User) users.get(0);
}

if (user != null && user.isPasswordValid(password)) {
Hibernate.initialize(user);
// trying to fetch the lazy loaded items
user.getProjects().size();
for (Project p : user.getProjects()) {
p.getTasks().size();
}
// return user
} else {
// return null
user = null;
}

tx.commit();
session.close();

return user;
}
}

The UserService service is configured as a BlazeDS destination in remoting-confix.xml:


<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service"
class="flex.messaging.services.RemotingService">
<adapters>

<adapter-definition id="java-object"
class="flex.messaging.services.remoting.adapters.JavaAdapter"
default="true"/>
</adapters>
<default-channels>
<channel ref="my-amf"/>
</default-channels>
<destination id="user">
<properties>
<source>org.blazedspractice.model.UserService</source>
<scope>request</scope>
</properties>
</destination>
</service>



The FlexUnit test code shown below is invoking the UserService login method through a RemoteObject class and validates the response. We will review just a couple of test cases: a positive one, i.e. a valid user ID/password combination and a negative, that tests a login operation with an invalid password. The database is initialized with a test user account before the test run. Here is the code:


package org.blazedspractice.model
{
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;

import flexunit.framework.TestCase;
import flexunit.framework.TestSuite;

import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;

public class UserTest extends TestCase {
private static const LOGIN_NAME:String = "user1";
private static const LOGIN_PSW:String = "password";
private static const INVALID_LOGIN_PSW:String = "invalid_password";

private var user:User;
private var fault:Boolean = Boolean(false);
private var resultCheckTimer:Timer;

// The timer and result check timeouts
private static const TIMEOUT_MS:int = 3000;
private static const RESULT_CHECK_TIMEOUT_MS:int = 3500;

/**
* The test case constructor.
* @param The name of the test method to be called in the test run.
*/
public function UserTest(methodName:String) {
super( methodName );
}

/**
* Builds a test suite.
*/
public static function suite():TestSuite {
var ts:TestSuite = new TestSuite();
ts.addTest( new UserTest( "testLogin" ) );
ts.addTest( new UserTest( "testLoginNegative" ) );
return ts;
}

/**
* Setup the test. This method is executed before every testXXX() by the framework.
*/
override public function setUp() : void {
user = null;
fault = Boolean(false);
}

/**
* This test case validates login operation. The login name and password are valid.
*/
public function testLogin():void {
// Create a remote object
var userService:RemoteObject = new RemoteObject();
// This is a name of the destination configured in BlazeDS settings
userService.destination = "user";
// Add result and fault event listeners as asynchronous checkpoints
userService.login.addEventListener("result", handleLoginResponse);
userService.addEventListener("fault", faultHandler);
// Create a timer that will validate a result of the login operation.
resultCheckTimer = new Timer(1);
resultCheckTimer.delay = TIMEOUT_MS;
resultCheckTimer.addEventListener(TimerEvent.TIMER, addAsync(loginCheck, RESULT_CHECK_TIMEOUT_MS));
resultCheckTimer.start();
// Call the login method.
userService.login(LOGIN_NAME, LOGIN_PSW);
}

/**
* This method handles login response event. It is invoked by the
* Flex framework once the server side data service returns a response to
* the login request.
*/
private function handleLoginResponse(event:ResultEvent):void {
user = event.result as User;
trace("user: " + user);
}

private function faultHandler (event:FaultEvent):void {
fault = Boolean(true);
fail(event.fault.faultString);
}

/**
* Validate a positive test case.
*/
private function loginCheck(event:Event):void {
resultCheckTimer.reset();
trace("loginCheck: " + user);
if (fault == Boolean(true)) {
fail("login failed");
}
assertNotNull(user);
assertNotUndefined(user);
}

/**
* The negative test case. The login password is invalid.
*/
public function testLoginNegative():void {
var userService:RemoteObject = new RemoteObject();
userService.destination = "user";
userService.login.addEventListener("result", handleLoginResponse);
userService.addEventListener("fault", faultHandler);

resultCheckTimer = new Timer(1);
resultCheckTimer.delay = TIMEOUT_MS;
resultCheckTimer.addEventListener(TimerEvent.TIMER, addAsync(loginFailureCheck, RESULT_CHECK_TIMEOUT_MS));
resultCheckTimer.start();

userService.login(LOGIN_NAME, INVALID_LOGIN_PSW);
}

/**
* Validate a negative test case.
*/
private function loginFailureCheck(event:Event):void {
resultCheckTimer.reset();
trace("loginCheck: " + user);
if (fault == Boolean(true)) {
fail("login failed");
}
assertNull(user);
}
}
}

The comments in the code explain the low level details of the test. The FlexUnit and Flex SDK API documentation will cover the rest. I will explain why do we need a timer here and what is addAsync for. :) That is the key to the solution.


The call to userService.login(LOGIN_NAME, LOGIN_PSW); will submit a request to the remote data service and return immediately, since it is an asynchronous call. The response from the userService.login() call will be handled as an event. The call to the service could be successful or could fail due to a system error, for example the network connection is not available or a service is not configured properly, etc. These cases are handled as "result" and "fault:" event types:


userService.login.addEventListener("result", handleLoginResponse);
userService.addEventListener("fault", faultHandler);

The faultHandler() method will be invoked by the Flex SDK framework in case of a system error while calling the data service. The handleLoginResponse() method will be invoked when the response is received successfully.


The test should validate the response and assert expected values based on the test scenario. It should also fail in case of a system error.


The FlexUnit invokes just the methods starting with "test" prefix. Usually a test will be considered to be complete as soon as the testXXX() method is complete. However, we need to validate the results of the login operation that could be available in a few milliseconds after the testXXX() is done. To handle that case, FlexUnit provids a method that is called addAsync. The addAsync is defined in TestCase.as class of the FlexUnit framework:


/**
* Add an asynchronous check point to the test.
* This method will return an event handler function.
*
* @param func the Function to execute when things have been handled
* @param timeout if the function isn't called within this time the test is considered a failure
* @param passThroughData data that will be passed to your function (only if non-null) as the 2nd argument
*
@param failFunc a Function that will be called if the asynchronous
function fails to execute, useful if perhaps the failure to
* execute was intentional or if you want a specific failure message
* @return the Function that can be used as an event listener
*/

public function addAsync(func : Function, timeout : int,
passThroughData : Object = null, failFunc : Function = null) : Function
{
if (asyncTestHelper == null)
{
asyncTestHelper = new AsyncTestHelper(this, testResult);
}
asyncMethods.push({func: func, timeout: timeout, extraData: passThroughData, failFunc: failFunc});
return asyncTestHelper.handleEvent;
}

Basically addAsync adds a delayed check point to the test validation. Internally, the AsyncTestHelper class schedules a timer to do that.


Why does our test need a timer too? I guess, we could simply wrap our event handlers with the addAsync() like this:


 userService.login.addEventListener("result", addAsync(handleLoginResponse, RESULT_CHECK_TIMEOUT_MS));
userService.addEventListener("fault", addAsync(faultHandler, RESULT_CHECK_TIMEOUT_MS));

Well, that will work for the handleLoginResponse case, but will create a problem for faultHandler. In case of a successful response from the service, the timer scheduled internally by addAsync will time out, and the test will fail with an exception saying that method faultHandler() was never called. I suppose, I coudl cancel one of the timers if I get either one of the async points. However, I don't have an access to those timers and I don't want to make the tests to be too dependent on the internal implementation of the FlexUnit. Therefore, I introduced a local timer that invokes a method in a RESULT_CHECK_TIMEOUT_MS time and validates results set by data service result handlers. For example, testLogin() method schedules a call to loginCheck() method. The loginCheck() validates class variables called fault and user that are initialized in the data service result handlers handleLoginResponse() and faultHandler(). This is the basic idea.


The test runner is based on one I described in the earlier post. Here is its source code:


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
xmlns:flexunit="flexunit.flexui.*"
creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
import flexunit.framework.TestSuite;
import flexunit.flexui.TestRunnerBase;
import mx.collections.ArrayCollection;

import org.blazedspractice.model.UserTest;

[Bindable]
public var testClients:ArrayCollection;

public var NUMBER_OF_TESTS:int = 1;

private function onCreationComplete():void
{
var clients:Array = new Array();
var i:int;
for (i = 0; i < NUMBER_OF_TESTS; i++) {
clients.push("test"+i);
}
testClients = new ArrayCollection(clients);
startTests();
}

// Creates the test suite to run
private function createSuite():TestSuite {
var ts:TestSuite = new TestSuite();

ts.addTest( UserTest.suite() );

return ts;
}

private function startTests():void {
trace(" elements: " + testRepeater.numChildren);
var tests:Array = this.test as Array;
for each (var testRunner:TestRunnerBase in tests) {
testRunner.test = createSuite();
testRunner.startTest();
}
}

]]>
</mx:Script>

<mx:Button name="Run" label="Start Tests" click="startTests()" />

<mx:Panel layout="vertical" width="100%" height="100%">
<mx:Repeater id="testRepeater" dataProvider="{testClients}">
<flexunit:TestRunnerBase id="test" width="100%" height="100%" />
</mx:Repeater>
</mx:Panel>
</mx:Application>

Since the test runner is a Flex application, the build and deployment is the same as for the main application. I actually deply it together with the main application. Here is my ant target that creates html wrappers for the application itself and the test runner:


  <target name="compile.flex" depends="init, compile.flex.components, compile.flex.mxml, compile.flex.tests">
<html-wrapper title="${APP_TITLE}" file="index.html" application="app"
swf="${module}" version-major="9" version-minor="0" version-revision="0"
width="90%" height="100%" history="true" template="express-installation"
output="${build.dir}/${ant.project.name}/" />
<html-wrapper title="${APP_TITLE}" file="test.html" application="testapp"
swf="TestRunner" version-major="9" version-minor="0" version-revision="0"
width="90%" height="100%" history="true" template="express-installation"
output="${build.dir}/${ant.project.name}/" />
</target>

To run the tests, I simply navigate to the URL with the test.html, in my case it is
http://localhost:8400/blazeds_practice2/test.html. The tests are run automatically on the page creationComple event. The "Start Tests" button can be used to run them again. Here are the run results:



I hope this article will be helpful. I am sure this approach can be improved. The code I posted here does not cover all the conditions and most probably contains a few errors. I do not recommend using it as is in production. The intent was to share an idea and get some feedback. I am sure the approach described here is just one of the possible ways. I would be interested to learn more about testing asynchronous code and open to your comments and suggestions.

Tuesday, May 20, 2008

Building a Flex project with Ant

Here is a quick sample on how to build a simple Flex
"hello world" project with Ant.

The "hello world" project contains a src folder with one Flex application file and an Ant build.xml file:

./project_home/
./src/
app.mxml
build.xml

The app.mxml is the main module of the project. It simply has a label with a "Hello World!" text:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Label text="Hello World!"/>
</mx:Application>
Here is the build.xml source:
<?xml version="1.0"?>
<project name="fx_practice7" default="all">

<!-- Init the build process -->
<target name="init" unless="initialized">
<!-- Name of project and version -->
<property name="FLEX_HOME" location="/Users/mykola/java/flex"/>

<property name="proj.name" value="${ant.project.name}" />
<property name="proj.shortname" value="${ant.project.name}" />
<property name="version.major" value="0" />
<property name="version.minor" value="9" />
<property name="version.revision" value="0" />
<property name="APP_TITLE" value="Sample Application" />
<property name="APP_WIDTH" value="800" />
<property name="APP_HEIGHT" value="600" />

<!-- Global properties for this build -->
<property name="build.dir" location="${basedir}/build" />
<property name="flex_src" location="${basedir}/src" />

<path id="project.classpath">
<pathelement path="${java.class.path}" />
</path>

<taskdef resource="flexTasks.tasks"
classpath="${FLEX_HOME}/ant/lib/flexTasks.jar" />

<echoproperties/>

<property name="initialized" value="true" />

<mkdir dir="${build.dir}" />
</target>

<!-- Default target: clean and build the application -->
<target name="all" depends="init">
<antcall target="clean" />
<antcall target="build" />
</target>

<!-- Compile Flex files -->
<target name="compile.flex" depends="init">
<property name="module"
value="${ant.project.name}"
description="The name of the application module." />

<mxmlc file="${flex_src}/${module}.mxml"
keep-generated-actionscript="true"
output="${build.dir}/${ant.project.name}/${module}.swf"
actionscript-file-encoding="UTF-8"
incremental="true"
context-root="${ant.project.name}"
debug="true">
<load-config filename="${FLEX_HOME}/frameworks/flex-config.xml" />
<source-path path-element="${FLEX_HOME}/frameworks" />
<compiler.source-path path-element="${flex_src}" />
</mxmlc>

<html-wrapper title="${APP_TITLE}"
file="index.html"
application="app"
swf="${module}"
width="${APP_WIDTH}"
height="${APP_HEIGHT}"
version-major="${version.major}"
version-minor="${version.minor}"
version-revision="${version.revision}"
history="true"
template="express-installation"
output="${build.dir}/${ant.project.name}/" />

</target>

<!-- Build the application -->
<target name="build" depends="init">
<antcall target="compile.flex" />
</target>

<!-- Clean build files -->
<target name="clean" depends="init">
<delete dir="${basedir}/generated" />
<delete dir="${build.dir}" />
</target>

<target name="usage" description="Usage documentation">
<echo>
all - clean and build the project
</echo>
</target>
</project>
NOTE: Please update FLEX_HOME and other properties in the build.xml as required for your environment.

To do the build, simply run ant in the project folder. The build output will be stored in the project/build/ folder. To see the result, open index.html in a browser. The index.html is located in the project/build/project_home/ folder.

Monday, May 19, 2008

Using FlexUnit for Stress Testing

I saw quite a few questions in the forums on how to stress
test a Flex application. I thought about it and came up with an idea
that I want to share here.




I think FlexUnit can be used for stress testing. It is not
that difficult. I simply add multiple test runners for each client
application and run all of them asynchronously. Here is the example of
the FlexUnit runner:




<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
xmlns:flexunit="flexunit.flexui.*"
creationComplete="onCreationComplete()">

<mx:Script>
<![CDATA[
import flexunit.framework.TestSuite;
import test.TemperatureConverterTest;
import test.ArrayUtilTest;
import mx.collections.ArrayCollection;
import flexunit.flexui.TestRunnerBase;

[Bindable]
public var testClients:ArrayCollection;

public var NUMBER_OF_TESTS:int = 100;

private function onCreationComplete():void
{
var clients:Array = new Array();
var i:int;
for (i = 0; i < NUMBER_OF_TESTS; i++) {
clients.push("test"+i);
}
testClients = new ArrayCollection(clients);
}

// Creates the test suite to run
private function createSuite():TestSuite {
var ts:TestSuite = new TestSuite();

ts.addTest( TemperatureConverterTest.suite() );
ts.addTest( ArrayUtilTest.suite() );

return ts;
}

private function startTests():void {
trace(" elements: " + testRepeater.numChildren);
var tests:Array = this.test as Array;
for each (var testRunner:TestRunnerBase in tests) {
testRunner.test = createSuite();
testRunner.startTest();
}
}

]]>
</mx:Script>

<mx:Button name="Run" label="Start Tests" click="startTests()" />

<mx:Panel layout="vertical" width="100%">
<mx:Repeater id="testRepeater" dataProvider="{testClients}">
<flexunit:TestRunnerBase id="test" width="100%" height="100%" />
</mx:Repeater>

</mx:Panel>
</mx:Application>






Here is a screen shot of the test with 100 test runners:





So, I guess once you have a client application that runs
hundreds of tests in parallel, it is easy to launch it on several
instances of the browser, or even on several PCs. All you need to do is
to add startTests() call to onCreationComplete() as a last
line of the method, deploy the app and simply open the URL in the
browsers.


The other nice thing about it is that this test client can be
used for profiling purposes. FlexBuilder has a very nice profiler. I
found it very helpful in testing my BlazeDS application.

Friday, April 25, 2008

Java and Flex Builder 3


Flex Builder 3 is distributed as an Eclipse plugin and as a standalone IDE. The standalone version does not support Java development out of the box. These couple of blogs helped me to add Java to the standalone version.