Thursday, August 05, 2010
Three Ways to Run Multiple Versions of Internet Explorer and Firefox
Recently I worked on a project that had to support six different browsers; three versions of Firefox (1 through 3) and three versions of Internet Explorer (6, 7, 8). Right away this raised a few questions, and one of the more interesting ones was about testing:
What is the optimal environment for testing two browsers at three versions each?
This is an interesting issue because browsers are not designed to allow more than one version to be installed at once. This makes sense for users — it would be confusing and insecure for them to use an older version by accident — but it's traditionally a big hassle for web developers. It's important for us to be able to test our changes on multiple browsers because different versions of the same browser may not render the same code the same way, and it's important to catch and fix these bugs.
To answer this question, our team of myself (a developer) and two quality assurance experts did some brainstorming and each of us actually came up with our own solution. Here are the three options we considered:
Multiple VMs
This was our senior QA's suggestion. Set up three virtual machines, and on each machine, install a different version of Internet Explorer and Firefox.
Advantages:
- Tried, tested and true. This is a standard QA practice and has been in use for years.
- Can run all six browsers simultaneously (good if we want to test in parallel)
Disadvantages:
- There is some overhead involved in setting up and keeping track of three VMs.
- Three VMs will need to run on their own machine, so an extra box or some server space is needed.
VM Snapshots
This was our other QA's suggestion, which I thought was quite novel. The process goes like this: create a virtual machine and install IE6 and FF1, then take a snapshot (something the VM can revert to upon request). Now, upgrade the browsers to IE7/FF2 and take another snapshot, and perform the final upgrade to IE8/FF3 and take a third snapshot. By toggling between the three snapshots, all six browsers can be tested using only one VM.
Advantages:
- Only one VM is required, so there is less overhead.
- Since there is only one VM, it's possible to run the VM on a developer or QA's machine in a pinch.
Disadvantages:
- Can only test one version of each browser at a time.
- There is probably still a need for some shared machine space to host the VM.
Hack it Together
Predictably, this was the developer's solution (mine). I've used a slightly-unstable beta product called IETester in the past, which allows testing multiple versions of Internet Explorer simultaneously. This meant all I needed was a solution for Firefox, and after some searching I found an outline of how to install multiple versions of FF.
Advantages:
- No virtual machine overhead.
- Everything can be run as needed on the developer's machine.
Disadvantages:
- Highly technical: Setting up and maintaining this environment is not for the faint of heart.
- Can only run one version of FF at a time (though IE can be done in parallel).
Given these options, we decided that the best match for our project's needs was to use VM Snapshots on the quality assurance side, and the hacked-together solution on the development side. The low overhead was a big plus, especially for development, and it was important that QA was able to reliably test real versions of each browser. This worked out well for us, and I would definitely use such methods again.
Have you used these or similar solutions in the past? Are there other solutions out there that we don't know about? Let us know in the comments.
Labels: Browsers, Firefox, Internet Explorer, QA
Tuesday, March 02, 2010
RIATest: Automating Custom Components
When you specify actions that must be performed on your application the first thing you specify is what component you want to be located by RIATest so that an action can be performed on it. RIATest has few ways of locating components. One of the ways is locating component by label e.g. button with "Save" label can be located following way:
FlexButton("Save")Component can be located in a same manner using components automatationName. You can also locate component using its automationIndex:
FlexButton("index:0")In most of the cases you are getting every component which you need in the inspector, so you can easily locate those components. In case you derive a custom class from built-in container-type classes you usually do not need to do anything special to make children of your custom component available for automation since this is done by the automation delegate of base class.
However sometimes you implement a custom component that contains other components but your custom component is not derived from Container built-in class. If that is the case you need to expose components contained in your custom class. You will need to perform some additional steps to access them.
Let's discuss one exact example. Here we have a CustomPanel with a button added in its title bar. You will notice that the button which you just added is not visible in the inspector:
So what we can do? We need to implement automation delegate class for our custom component. Exposing child components is done by implementing numAutomationChildren/getAutomationChildAt pair of functions. numAutomationChildren function must return the number of children component for your custom class, getAutomationChildAt must return the child at the specified index. This means we need to implement automatation delegate class for our custom component to make Switch button accessible from RIATest.
numAutomationChildren
In our case we will need to return the number of children which are implementing IAutomationObject plus Switch button:
override public function get numAutomationChildren() : int {
var count:int = 0;
for(var i:int=0; i < comp.numChildren; i++) {
if (comp.getChildAt(i) is IAutomationObject)
count++;
}
return ++count;
}getAutomationChildAt
getAutomationChildAt will need to return the child with a given index in case of index < numChildren and return Switch button in case of index == numChildren
override public function getAutomationChildAt( index : int ) : IAutomationObject {
if (index >= 0 && index < comp.numChildren )
return comp.getChildAt(index) as IAutomationObject;
if(index == comp.numChildren)
return comp.button;
return null;
}We will also need to implement init method in order to register our delegate class:
public static function init( root : DisplayObject ) : void {
Automation.registerDelegateClass( CustomPanel, CustomPanelAutomationImpl);
}The only thing left to do is to include our delegate into build, include your delegate class using additional compiler arguments:
-includes CustomPanelAutomationImpl
As you can see from the screenshot now Switch button is getting displayed in the inspector, so you can easily locate it:
FlexPanel("index:0")->FlexButton("Switch")=>click();You can download source code of this project from here.
Friday, September 18, 2009
Maven Compiler Tips and Tricks
Apache’s Maven is a great tool for managing a build environment: it keeps track of all project dependencies and provides a number of configurable build phases which can add depth to the build process. Building a project from the ground-up with Maven is a sure-fire way to keep it well organized and easy to maintain – but what about adding Maven on to an existing project, or worse, merging a non-Maven project into a project that already relies on Maven?
What follows is a look at some of the lessons I’ve learned from tweaking compiler plug-ins and digging through search results to debug various Maven-related issues. Hopefully it will be useful to the next developer who happens to hit similar issues, and if you have tips of your own be sure to leave a comment.
The maven-compiler-plugin <include> property
When overriding the default maven-compiler-plugin, the <include> tag may be used to force the compiler to include extra files into the build. There are a couple of interesting points to note here:
It is a filter. Many things in Maven expect a path to a directory, but not the <include> tag. If you have some extra java classes in src/main/java and you pass that to <include> it will fail silently – what you actually want is src/main/java/**/*.java.
It is for the compile-phase only. By default, in addition to using the maven-compiler-plugin during the compile phase, Maven will also use it during the test-compile phase. Most properties will apply to both, but <include> is not one of them; the test-compile phase requires a separate property, <testIncludes>, for any includables it requires[1].
The generate-sources and generate-resources phases
These phases are great for adding source (.java) and resource (.class) files to the compiler before the compile phase occurs. The snippet below shows how to use mojo's build-helper-plugin to add some obscure .class files to the classpath:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>../obscure/classes</directory>
</resource>
</configuration>
</execution>
</executions>
</plugin>
Now any .class files in ../obscure/classes (or any subdirectories) will be added to the classpath.
The same plug-in is used for adding .java files, simply tweak the generate-resources and add-resource values to generate-sources and add-source, and the <resources> and <resource> properties to <sources> and <source>.
The maven-compiler-plugin <compilerArgument> property
The <complierArgument> tag may be used to pass command-line arguments directly to the java compiler. Two examples:
<classpath> seems like it would let you add resources to the classpath, but near as I can tell, these aren’t actually used. Maven seems to prefer managing its own classpath, though we can still add/remove entries by overriding the default generate-resources phase (as explained above).
<sourcepath> is a great way to specify multiple source directories for compilation. It takes a semicolon-delimited list of top-level directories containing java files to compile. Alternately, an override of the generate-sources phase may be used here as well.
One gotcha with regards to sourcepath: to get this to work, I had to manually set <fork> to true on the maven-compiler-plugin. In fact, this gets even worse: when <fork> is true, Maven will use the %PATH% environment variable to determine which JRE to use, and this will fail with a totally non-descript error if the path to your JRE contains any spaces – very annoying to track down. This is actually a bug in Maven, logged here (http://jira.codehaus.org/browse/MCOMPILER-30).
The <sourceDirectory> property
A minor but important tag when playing around with various sources and resources, the <sourceDirectory> tag may be used to set the base directory for including java source files. It defaults to src/main/java, but I found when playing around with a lot of sources spread around various directories, it was easiest to set it to the current directory as follows:
<build>
<sourceDirectory>.</sourceDirectory>
{...}
</build>
Other useful debugging hints
When running into problems, it’s always good to have a few debug flags around to get a little more information out of Maven, which is generally not great at telling you what might be wrong.
Specifying the -e flag while running Maven will print out any exceptions Maven encounters, with the corresponding stack trace.
The <verbose> tag may be added inside any plug-in’s <configuration> property and when toggled to true (default is false) it will print some extra information, including the sourcepath and classpath being used by Maven’s compiler.
Specifying the -X flag while running Maven will document all kinds of intermediate steps Maven takes during the build – much more than -e and <verbose>.
[1] This makes perfect sense, of course: it’s unlikely that you’ll want the same includables for both the normal compiler and the testing compiler. It’s just counter-intuitive compared to the rest of the <configuration> properties.
Friday, July 17, 2009
Here I am!
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);Now that we have location manager we need to request location updates from it. The location is requested using requestLocationUpdates method, it registers the current activity to be notified periodically by the named provider. In our case provider is GPS. For tracking location changes we are using LocationListener which is for receiving notifications from the LocationManager when the location has changed.
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, new LocationListener() { public void onLocationChanged(Location location) { double lat = location.getLatitude(); double lng = location.getLongitude(); latitude.setText( Double.toString( lat ) ); longitude.setText( Double.toString( lng ) ); } public void onProviderDisabled(String provider){} public void onProviderEnabled(String provider){} public void onStatusChanged(String provider, int status,Bundle extras){} });
Just getting last known location...
Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);Good! We got the location. Now we just need to generate the URL and send SMS. Android offers full access to SMS functionality from within your applications with the SMSManager. We are geeting a reference to the SMS Manager using the static method SmsManger.getDefault. For sending SMS message use sendTextMessage, which allows you to send SMS message by specifying text message and receiver's phone number.
final SmsManager sm = SmsManager.getDefault(); String phoneNumber = number.getText().toString(); String URL = "http://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q="; URL += latitude.getText() + "," + longitude.getText(); sm.sendTextMessage(phoneNumber, null, URL, null, null);That's it! Just one note - in order to send SMS message and request GPS location application require following two permissions:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.SEND_SMS"/>
Thanks to my friend Aaron Olson from Macadamian for the help in testing this code.
Source
Thursday, June 04, 2009
Using Mate to Dispatch Events with a Callback in ActionScript
I ran into a situation not too long ago where I had to dispatch an event with a callback from a service-like class. Since this wasn't a UI component, there was no corresponding MXML. I needed a pure-ActionScript solution, but still wanted to use Mate. In a matter of minutes, I had no problem dispatching my event*:
public class TestClass
{
public function generateEvent():void
{
var event:TestEvent= new TestEvent();
event.type = TestEvent.TYPE;
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.generator = TestEvent;
dispatcher.dispatchEvent( event );
}
}
This was a good start, but I wanted to specify a method within the same service-like class to be called if the event returned successfully. Mate's dispatcher tag makes this very easy, but without any MXML, it was not an option. All the handler for the event does is generate a result or fault using Mate's ServiceResponseAnnouncer, so I needed a way to specify the callback from my dispatcher: just like the MXML tag allows, but using only ActionScript.
Looking through the docs didn't get me very far. The section on the Dispatcher provides information on using a dispatcher in ActionScript, but without a callback, and there is a section on using callbacks, but specific to the MXML implementation. I checked the ResponseHandler classes as well, and poked around a bit on Google without much luck.
Knowing that these properties exist, I attempted to piece it together myself (and eventually succeeded). In case anyone else ever runs into the same issue, and because I'd rather not go through all that searching/trial-and-error again, here is a working solution:
public class TestClass
{
public function generateEvent():void
{
var event:TestEvent= new TestEvent();
event.type = TestEvent.TYPE;
var handler:ResponseHandler = new ResponseHandler();
handler.type = ResponseEvent.RESULT;
handler.method = myCallback;
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.generator = TestEvent;
dispatcher.responseHandlers = [handler];
dispatcher.dispatchEvent( event );
}
private function myCallback(event:ResponseEvent):void
{
trace( "callback reached!" );
}
}
One of the hardest parts to figure out was what method signature was required for the callback function. I found my answer in a comment in the ResponseHandler class - one more reason why open source frameworks and well commented code are the way to go!
*Aside: if a callback is not necessary, there's no need to use Mate. In fact, Mate's best practices specifically encourage using Flex's built-in dispatchEvent() method. Had I not needed a callback here, I could have called dispatchEvent() on the parent application (or any other DisplayObject within the scope of my service class).
Monday, May 11, 2009
XBAP - Using the Popup Control as a Dialog Box
How many people have experienced the modal pop up in desktop applications? They are commonly used across a wide variety of applications, one could say, too commonly used. While not appropriate for all situations, sometimes you need to use a pop up.
In XBAP applications the ability to open a pop-up is very limited; in many cases you will use navigation and multiple pages instead of separate windows. In most cases this is sufficient, but sometimes you really need to use a pop-up, and when you do a simple work around is to use the Popup control offered by WPF.
First, you define the Popup in the markup, making sure to set its StaysOpen property to true so it will remain open until you close it. (There’s no point in using the PopupAnimation or AllowsTransparency properties, because they won’t have any effect in a web page.) Include suitable buttons, such as OK and Cancel, and set the Placement property to Center so the popup will appear in the middle of the browser window.
Code sample:
<Popup Name="dialogPopUp" StaysOpen="True" Placement="Center" MaxWidth="200">
<Border>
<Border.Background>
<LinearGradientBrush>
<GradientStop Color="AliceBlue" Offset="1"></GradientStop>
<GradientStop Color="LightBlue" Offset="0"></GradientStop>
</LinearGradientBrush>
</Border.Background>
<StackPanel Margin="5" Background="White">
<TextBlock Margin="10" TextWrapping="Wrap">Please enter your name.
</TextBlock>
<TextBox Name="txtName" Margin="10"></TextBox>
<StackPanel Orientation="Horizontal" Margin="10">
<Button Click="dialog_boxOK_Click" Padding="3" Margin="0,0,5,0">OK</Button>
<Button Click="dialog_boxCancel_Click" Padding="3">Cancel</Button>
</StackPanel>
</StackPanel>
</Border>
</Popup>
At the appropriate time (for example, when a button is clicked), disable the rest of your user interface and show the Popup. To disable your user interface, you can set the IsEnabled property of some top-level container, such as a StackPanel or a Grid, to false. (You can also set the Background property of the page to gray, which will draw the user’s attention to Popup.) To show the Popup, simply set its IsVisible property to true.
Here’s an event handler that shows the previously defined Popup:
private void popupTriggerButton_Click(object sender, RoutedEventArgs e)
{DisableMainPage();
}
private void DisableMainPage()
{ mainPage.IsEnabled = false; this.Background = Brushes.LightGray; dialogPopUp.IsOpen = true;}
When the user clicks the OK or Cancel button, close the Popup by setting its IsVisible property to false, and re-enable the rest of the user interface:
private void dialog_boxOK_Click(object sender, RoutedEventArgs e)
{ // Copy name from the Popup into the main page. lblName.Content = "You entered: " + txtName.Text;EnableMainPage();
}
private void dialog_boxCancel_Click(object sender, RoutedEventArgs e)
{EnableMainPage();
}
private void EnableMainPage()
{ mainPage.IsEnabled = true;this.Background = null;
dialogPopUp.IsOpen = false;}
Using the Popup control to create this workaround has one significant limitation. To ensure that the Popup control can’t be used to spoof legitimate system dialog boxes, the Popup window is constrained to the size of the browser window. If you have a large Popup window and a small browser window, this could chop off some of your content. One solution is to wrap the full content of the Popup control in a ScrollViewer with the VerticalScrollBarVisibility property set to Auto.
If a Popup isn’t suitable for you, you can try another more “interesting” approach for showing a dialog box. Using the Windows Form library from .NET 2.0 you can safely create and host any WinForm control in your WPF application. In this case, you can create and show an instance of the System.Windows.Forms.Form class (or any custom form that derives from Form), because it doesn’t require unmanaged code permission. In fact, you can even show the form modelessly, so the page remains responsive. The only drawback is that a security balloon automatically appears superimposed over the form and remains until the user clicks the warning message. You’re also limited in what you can show in the form. Windows Forms controls are acceptable, but WPF content isn’t allowed.
Labels: .NET, c#, code sample, popup, WPF, XBAP
Monday, April 27, 2009
FlexDock
The component which is actually our "box" is called CanvasBox. For expanding the boxes I prefer to use Caurina Tweener, it is also possible to use built-in mx.effects.Resize, but I prefer Caurina Tweener's one:
private var _rollOverTween:Object = {width:200, height:200, time:0.45, transition:"easeOutBack", onComplete:onRollOver};
private var _rollOutTween:Object = {width:90, height:90, time:0.2, transition:"linear", onComplete:onRollOut};
When the user clicks on the button the Tweener.addTween( this, _rollOverTween); method is called, it is possible to change the content of the box in the click handler, but if you want your UI not to be broken it is better to do those changes in the onComplete handler of the tween.
Besides the CanvasBox component we also need to have a component which will hold the boxes. It is called BoxHolder and it is template component. It has _content field which is storing the boxes, pay attention at [ArrayElementType("CanvasBox")]:
// An array which stores the UI components
// of our content
[ArrayElementType("CanvasBox")]
private var _content:Array;
Every time you click on a box, the closeOthers method is called which is shrinking previously expanded box.
private function closeOthers(event:MouseEvent):void
{
for each(var component:CanvasBox in _content)
{
if(component != event.currentTarget)
{
component.roolOut();
}
}
}
DEMO | SOURCE