Wednesday, January 23, 2008

Test Driven Development and GUI Testing on the Android platform: Temperature Converter sample

Google Docs: This article can be viewed at Test Driven Development and GUI Testing on Android platform: Temperature Converter sample

Unit tests

This article will introduce Test Driven Development on the Google Android platform. I took me some time to figure it all out and it's not always clear where to find the correct documentation, answers or resources, so I'm writing this hoping it be helpful for someone who is trying to take this approach.

This is an open topic, an we are all doing the first steps and trying to discover some functionality which is still not documented in the Android SDK, and up to now there's no sources to check what's the intention of some API code or tools.

We will be using a fairly simple example that was used in previous articles about similar subjects. That's a Temperature Converter application. Requirements are defined by our Use Case: Convert Temperature.

Using Test Driven Development we will implement this use case, trying to keep it as simple as possible not to get confused with unnecessary details.


Comments, corrections, improvements, critics are gladly welcome.

Project

  1. Create a Android project project in Eclipse (File->New Project...->Android->Android Project)
  2. Name the project something like TDD Android
  3. Package name: com.example.tdd
  4. Activity name: TemperatureConverterActivity
  5. Application name: Temperature Converter
  6. Finish


Add testing infrastructure using JUnit

We are using a different folder to keep our tests in order to permit a clear separation in case the test should be removed at production.

  1. Select TDD Android project node in Packages
  2. Right click and select New Source Folder
  3. Folder name:test
  4. Finish

Then

  1. Select the newly created test folder
  2. Select New Java Package
  3. Name: com.example.tdd
  4. Finish


Generate test classes (JUnit version)


  1. Right click in the TemperatureConverter class
  2. Select New JUnit Test Case
  3. Select JUnit 3.x
  4. Source folder: TDD Android/test
  5. Finish

Now we will add our tests, even though our TemperatureConverter class is not clearly defined. Adding the tests will help us define our TemperatureConverter class.

Define the tests

Accordingly with our use case in Temperature Converter we can define 2 tests

  • testCelsiusToFahrenheit
  • testInvalidCelsiusTemperature

to verify our tests' resluts we will use some data obtained from an external converter and we will put these figures in a conversion table. Some temperature measure background can be obtained from http://en.wikipedia.org/wiki/Temperature and an online converter can be found at http://www.onlineconversion.com/temperature.htm. We will add a conversionTable to verify our tests shortly.

Edit the source file and:

  1. add public void testCelsiusToFahrenheit()
  2. add public void testInvalidCelsiusTemperature()


And use this code snippet

 public void testCelsiusToFahrenheit() {
fail("Not implemented yet");
}

public void testInvalidCelsiusTemperature() {
fail("Not implemented yet");

Adding the conversion table

Also in the TemperatureConversionTest class

  1. Add an attribute private static final Map<Integer, Integer> conversionTable
  2. Set its default value to new HashMap<Integer, Integer>()
  3. Shift + Ctrl + O to resolve the imports

Initialization code

  1. Initialize the conversion table to
static { // initialize (c, f) pairs conversionTable.put(0, 32); conversionTable.put(100, 212); conversionTable.put(-1, 30); conversionTable.put(-100, -148); conversionTable.put(32, 90); conversionTable.put(-40, -40); conversionTable.put(-273, -459); }

just after its definition

Run the tests

Right click on the TDD Android project node and select Run as JUnit Test We will se how, as expected. our tests fail.

Notice that these tests will run outside the Android emulator. Later we will include these tests to be run by the emulator itself.

Complete the tests


Now, once our test infrastructure is setup, let's proceed to define our tests.

  1. Add
public void testCelsiusToFahrenheit () 
for (int c: conversionTable.keySet()) {
int f = conversionTable.get(c);
String msg = "" + c + "C -> " + f + "F";
assertEquals(msg, f, TemperatureConverter.celsiusToFahrenheit(c));
}
}
  1. Add
   public void invalidCelsiusTemperature () {
try {
int f = TemperatureConverter.celsiusToFahrenheit(-274);
} catch (RuntimeException ex) {
if (ex.getMessage().contains("below absolute zero")) {
return;
}
else {
fail("Undetected temperature below absolute zero: " + ex.getMessage());
}

}

fail("Undetected temperature below absolute zero: no exception generated");

}
  1. If some imports are missing just press Shift + Ctrl + O

Creating the real code

We have now the tests for code that still doesn't exist. If everything was fine, you should see a light bulb in the line where TemperatureConverter.celsiusToFahrenheit is invoked and giving you the alternative to

Create Method celsiusToFarenheit(int) in tdd.TemperatureConverter 
  1. Create the method public static int celsiusToFahrenheit(int celsius) in TemperatureConverter (Notice the int return type and the celsius parameter)
  2. Return any value, say 0, or raise an exception indicating that it's not implemented yet.

Running the tests

Our Test Driven Development is starting to appear.

Go to the JUnit tab and Rerun Tests

These tests will fail, because we haven't already implemented the celsiusToFahrenheit conversion method.

We obtain two failures but by very different reasons as before:

  1. testCelsiusToFahrenheit failed because a wrong conversion result was obtained (remember that we returned a constant 0)
  2. testInvalidCelsiusTemperature failed because no exception was generated for an invalid temperature

So, what's left is to go there an implement it.

celsiusToFahrenheit

Replace this

return 0

by

return (int)Math.round(celsius * 1.8 + 32);

Remember to change parameters name to celsius (from c) if it's not already named.

Running the tests again, and we will see how one test succeed but the other fails with the message

Undetected temperature below absolute zero: no exception generated

That's because we are expecting an exception to be thrown if the temperature is below the absolute zero but our first attempt doesn't do that.

Let's add this condition, but we first need the ABSOLUTE_ZERO_C constant.

Add ABSOLUTE_ZERO_C constant

TemperatureConverter class editor window.

  1. Add
    public static final int ABSOLUTE_ZERO_C = -273;

Modify celsiusToFahrenheit

Add the absolute zero check
  public static int celsiusToFahrenheit(int celsius) {
if (celsius < ABSOLUTE_ZERO_C) {
throw new RuntimeException("Invalid temperature: " + celsius + " below absolute zero");
}
return (int)Math.round(celsius * 1.8 + 32);

}

Run the tests

Run the tests again and we can verify that in our third attempt both tests passed.

Functional tests

We now have our TemperatureConverter class tested and ready to be included in our Android project.

Create the layout

Select main.xml in layout and add
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout id="@+id/linear" android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical">
<TextView id="@+id/message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="6dip"
android:text="Enter the temperature and press Convert">
</TextView>
<TableLayout id="@+id/table" android:layout_width="fill_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:stretchColumns="1">
<TableRow>
<TextView id="@+id/celsius_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="3dip"
android:textStyle="bold"
android:textAlign="end"
android:text="Celsius">
</TextView>
<EditText id="@+id/celsius"
android:padding="3dip"
android:numeric="true"
android:digits="-+0123456789"
android:textAlign="end"
android:singleLine="true"
android:scrollHorizontally="true"
android:nextFocusDown="@+id/convert"
>
</EditText>
</TableRow>
<TableRow>
<TextView id="@+id/fahrenheit_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="3dip"
android:textStyle="bold"
android:textAlign="end"
android:text="Fahrenheit">
</TextView>
<EditText id="@+id/fahrenheit"
android:padding="3dip"
android:textAlign="end"
android:singleLine="true"
android:scrollHorizontally="true">
</EditText>
</TableRow>
</TableLayout>
<Button id="@+id/convert" android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="6dip"
android:text="Convert"
android:textAlign="center"
android:layout_gravity="center_horizontal"
android:nextFocusUp="@+id/celsius"
>
</Button>
</LinearLayout>

There's a lot of documentation and tutorials online in case you need to know more about Android layouts. We now have an empty form.

Change the theme, if you want, in AndroidManifest.xml, adding

android:theme="@android:style/Theme.Dark"

to <application>

We will obtain this when we run as Android Application

Of course this is not functional yet.

Positron

provides an instrumentation and some support classes to help writing acceptance tests. It is provided as a jar that gets bundled with your application. Acceptance tests are written in junit, extending a custom base class. Positron can be downloaded from http://code.google.com/p/android-positron/downloads/list.

Positron jar must be added to build path. In this example we are using positron-0.4-alpha, but if by the time you read this there's a newer version you may use it.

Validation

Create com.example.tdd.validation package in test folder

Create OverallTest test case

In the newly created package com.example.tdd.validation create a new JUnit test case

  1. Source folder: TDD Android/test
  2. Package: com.example.tdd.validation
  3. Name: OverallTest
  4. Superclass: positron.TestCase
  5. Finish

Create Positron class

In the package com.example.tdd create the Positron class extending positron.Positron

  1. Source folder: TDD Android/test
  2. Package: com.example.tdd
  3. Name: Positron
  4. Superclass: positron.Positron
  5. Finish

Then, create our test suite including OverallTest tests. Other tests can also be added.

 @Override
protected TestSuite suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(OverallTest.class);
return suite;
}

Create the test


In OverallTest create the testConversion test case

 public void testConversion() {
Intent intent = new Intent(getTargetContext(), TemperatureConverterActivity.class);
startActivity(intent.addLaunchFlags(Intent.NEW_TASK_LAUNCH));

TemperatureConverterActivity activity = (TemperatureConverterActivity)activity();

// Is it our application ?
assertEquals(getTargetContext().getString(R.string.app_name), activity().getTitle());

// Do we have focus ?
assertEquals(activity.getCelsiusEditText(), activity.getCurrentFocus());

// Enter a temperature
sendString("123");

// Convert
press(DOWN, CENTER);

// Verify correct conversion 123C -> 253F
assertEquals("253", activity.getFahrenheitEditText().getText().toString());

}

This basically represents what we defines in our Use Case: Convert Temperature:

Actor Action System Response
1. The user enters a temperature in Celsius, and then press Convert 2. The system converts the temperature to Fahrenheit and the result is presented to the user
3. The user wants to enter another temperature and continues from 1 or presses Back to exit. 4. The process finishes.
Alternative Courses
Temperature is below absolute zero Indicate error
Invalid input characters entered Indicate error

Finally, add this to tearDown

 protected void tearDown() throws Exception {
finishAll();
}

Instrumentation

Add the instrumentation definition to AndroidManifest.xml
<instrumentation class=".Positron" android:functionalTest="true" android:targetPackage="com.example.tdd" android:label="Temperature Converter Acceptance Tests"/>


Complete TemperatureConverterActivity

In order to compile these tests we have to add the fileds related with the views widget in the GUI
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);

// Find text fields.
celsiusEditText = (EditText)findViewById(R.id.celsius);
fahrenheitEditText = (EditText)findViewById(R.id.fahrenheit);
convertButton = (Button)findViewById(R.id.convert);

// disable fahrenheit field
fahrenheitEditText.setEnabled(false);

}

Also add the fields proposed by the IDE

private EditText celsiusEditText;;
private EditText fahrenheitEditText;
private Button convertButton;

and using Source->Generate Getters and Setters... generate the corresponding getters.

Run the tests again

This time let's run the positron tests in the emulator. To achieve this

adb shell "/system/bin/am instrument -w com.example.tdd/.Positron"

and change to the DDMS inside Eclipse, and look for the LogCat window

I/Positron(1091): .F
I/Positron(1091): Time: 1.332
I/Positron(1091): There was 1 failure:
I/Positron(1091): 1) testConversion(com.example.tdd.validation.OverallTest)junit.framework.ComparisonFailure: expected:<253> but was:<>
I/Positron(1091): at com.example.tdd.validation.OverallTest.testConversion(OverallTest.java:62)
I/Positron(1091): at java.lang.reflect.Method.invokeNative(Native Method)
I/Positron(1091): at positron.harness.InstrumentedTestResult.run(InstrumentedTestResult.java:37)
I/Positron(1091): at junit.extensions.TestDecorator.basicRun(TestDecorator.java:22)
I/Positron(1091): at junit.extensions.TestSetup$1.protect(TestSetup.java:19)
I/Positron(1091): at junit.extensions.TestSetup.run(TestSetup.java:23)
I/Positron(1091): at positron.Positron.onStart(Positron.java:62)
I/Positron(1091): at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1115)
I/Positron(1091): FAILURES!!!
I/Positron(1091): Tests run: 1, Failures: 1, Errors: 0

Again, as expected the test failed because the conversion functionality is not yet implemented.

Implementing conversion functionality in TemperatureConverterActivity

Add OnClickListener to convert button


It's an interface definition for a callback to be invoked when a view, a button in this case, is clicked. We need to implement the onClick abstract method, which in our case will call the convert helper method to carry away the actual conversion.

        // Hook up button presses to the appropriate event handler.
convertButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
convert();
}
});

Add convert method

Helper method to get the temperature value entered into the Celsius text field, convert it into Integer and call celsiusToFahrenheit method in TemperatureConverter class.

   protected void convert() {
try {
int f = TemperatureConverter.celsiusToFahrenheit(Integer.parseInt(celsiusEditText.getText().toString()));
fahrenheitEditText.setText(String.valueOf(f));
}
catch (Exception ex) {
fahrenheitEditText.setText(ex.toString());
}

}

Run tests again

Now, when we run our tests again, we obtain
I/Positron(1334): Time: 1.368
I/Positron(1334): OK (1 test)

Let's convert our unit test into Positron

In the previous unit test TemperatureConverterTest let's change the base class into positron.TestCase. You may also have to comment out the generated constructor because (until now) there's no positron.TestCase constructor receiving a String.

Run tests again

Running the tests again now we can see all of the tests are run
I/Positron(1430): Time: 2.408
I/Positron(1430): OK (4 tests)

Monday, January 21, 2008

Setting emulator time in Android


Setting the emulator time in android is fairly easy using the date command inside the android emulator image. If you are developing and testing a time related application you may found useful to change system time to force some events to happen, like an appointment alarm.

To do this, just use

$ adb shell date secs


where secs is seconds since 1970-01-01 00:00:00 UTC.

Emulator time is updated every minute, so you have to wait until the display is update, however system time has already been changed.

If you are in Linux and want to obtain secs for an arbitrary date


$ date --date="2008-01-31 17:46:59" +%s


or as a one-liner

$ adb shell date $(date --date="2008-01-31 17:46:59" +%s)


notice that we are using two different date commands, one from the android platform and the other from our Linux system. If you are using Windows or OSX, I'm sure you can find a similar way of doing this.

There are other alternatives, like installing Busybox in the system image, but this is left for another article. Stay tuned.

Thursday, January 03, 2008

Test Driven Development and GUI Testing: Functional tests

Previous article:Test Driven Development and GUI Testing: Unit tests

Google Docs: This article can be viewed at Test Driven Development and GUI Testing: Functional tests

In the previous article Test Driven Development and GUI Testing: Unit tests, we followed the Test Driven Development approach to go from our Use Case:Convert Temprature to our TemperatureConverter class which is now fully tested and will be the foundation of our application.
We now trust it, because it has passed our tests. Furthermore, these tests give us the confidence to refactor, improve and change it.
This article is about building functional tests as we implement our swing GUI.

Adding functional tests

Functional tests, also know as Acceptance tests, are other fundamental concept in eXtreme Programming. Those tests would help us assure that our application correctly implements what we described in Use Case:Convert Temprature and are always written from a client or user perspective.
Writing functional tests to validate application GUI using Test Driven Development techniques is even more trickier at the beginning.
How can you write a functional test to test a GUI that still doesn't exist ?
Functional testing is approached much like unit testing as we seen before.
Well, let's see how.

Firstly, we are adding the first component of the application GUI.

Create JFrame

  1. Select the TDD project node
  2. Right click and add new JFrame From...
  3. Name it TemperatureConverterGUI
  4. Select Source Package location and tdd package
  5. OK
We now have an empty form.

Synchronize the model

  1. In the TemperatureConverterGUI source code editor, right click
  2. Select ReverseEngineer...
  3. Existing UML: TDD-UML
  4. OK

Create test

  1. Select Functional Test Packages node
  2. Right click and add New File... | Other | JUnit | NbTestCase Test New File.. | Testing Tool | JellyTestCase Test
  3. Select Next
  4. FileName: OverallTest
  5. Folder test/qa-functional/src/validation
  6. Finish

Add libraries

  1. Add Jellytools, Jemmy and NBUnit to Libraries

Synchronize the model

  1. Right click on the OverallTest editor
  2. Select Reverse Engineer...
  3. Existing UML: TDD-UML
  4. OK
  5. Add OverallTest class from Model to TDD Class Diagram

Finishing all of these steps, we have this TDD Class Diagram

Add Jelly functional tests

Jelly is NetBeans dependent, so if you would like to avoid this you can base your tests directly on Jemmy.

In our TDD Class Diagram
  1. In OverallTest class
  2. Rename placeholder test1 to testCorrectConversion
  3. Rename placeholder test2 to testIncorrectConversion
  4. Add attribute app type ClassReference and default value initializeApp()
  5. Add method initializeApp() returning ClassReference and being private and static
  6. Generate Code...
  7. OK

Let's add this code to initializeApp() method
   private static ClassReference initializeApp () {
try {
return new ClassReference("tdd.TemperatureConverterGUI");
} catch (ClassNotFoundException ex) {
throw new RuntimeException("Couldn't initialize app", ex);
}
}
Then press Ctrl + Shift + I to fix the imports.

Mark tests as not yet implemented

Stub tests, as generated by NetBeans New | JellyTestCase Test are empty, so we need to add these sentences temporarily
    public void testCorrectConversion () {
fail("This test is not yet implemented");
}

public void testIncorrectConversion () {
fail("This test is not yet implemented");
}

Add the tests to the suite

These tests should be added to the existing suite. This is not automatically changed when we changed the tests names in the TDD Class diagram.
    public static NbTestSuite suite () throws ClassNotFoundException {
NbTestSuite suite = new NbTestSuite();
suite.addTest(new OverallTest("testCorrectConversion"));
suite.addTest(new OverallTest("testIncorrectConversion"));
return suite;
}

Modify build-qa-functional.xml

To be able to compile and run Jelly test cases we have to modify the rules in build-qa-functional.xml

<!-- Path to Jemmy library -->
<path id="jemmy.path" location="/opt/java/netbeans-6.0/testtools/modules/ext/jemmy.jar">

<!-- Path to Jelly library -->
<path id="jelly.path" location="/opt/java/netbeans-6.0/testtools/modules/ext/jelly2-nb.jar">

<!-- ========= -->
<!-- Compilers -->
<!-- ========= -->

<!-- Compile functional tests. This target is used in cfg-qa-functional.xml. -->
<target name="qa-functional-compiler">

<!-- Build application before tests -->
<ant dir=".." target="jar">
<buildTests srcdir="qa-functional/src" compileexcludes="**/data/**">
<classpath>
<!-- Add classpath elements needed to compile tests -->
<path refid="jemmy.path">
<path refid="jelly.path">
<fileset dir="../dist" includes="*.jar"> </fileset>
</path>
</path>

<!-- ========= -->
<!-- Executors -->
<!-- ========= -->
<!-- Run tests in JVM -->
<target name="run-jvm">
<executeTests pluginname="jvm">
<classpath>
<!-- Add classpath elements needed to run tests -->
<path refid="jemmy.path">
<path refid="jelly.path">
<fileset dir="../dist" includes="*.jar"> </fileset>
</path>
</path>

Run the tests

As expected recently added tests will fail because we forced the fail condition until we implement them.

Review Temperature Converter mock-up

In this mock-up we can identify the widgets required by the application.

We need
  • a window having "Temperature Converter" title
  • two text fields, one editable to enter the temperature and the other not editable to show the conversion result
  • two labels corresponding to each text field showing the corresponding temperature units
  • one convert button to do the conversion
  • one close button to close the windows and exit the application
That's how our application will look like or its structure.

From the behavioral point of view, we can say that every time the user presses the Convert button, a conversion is carried away and the result showed. If there's a problem with the conversion the error is also showed in the conversion text field but to get user attention this is showed in red, for example:

Invalid temperature: -274 below absolute zero

Now that we have identified the required components we can proceed to write the tests.
Yes, we haven't written the application yet but using the knowledge we obtained analyzing this mock-up we are going to write our tests expecting the components to be there.

Implementing the tests

We are using Jelly/Jemmy to implement our swing GUI tests. Jelly/Jemmy use the concept of Operators.

Add Jemmy Operators

Jemmy operators is a set of classes which are test-side agents for application components. Operators provide all possible methods simulating user action with components, methods to find and wait components and windows. Also operators map all components methods through the event queue used for event dispatching. All Jelly operators are subclasses of Jemmy operators.

All of the operators provide access to their subcomponents by "getters" methods. These methods are implemented using the "lazy initialization" technique, so real suboperator instances are not initialized until it's necessary. All of the suboperators are initialized by verify() method invocation, so this method guarantees that all subcomponents are already loaded.


So, our first step is to add such Operators.
We have to add these attributes to our OverallTest class in the TDD Class Diagram
  • jfo type JFrameOperator
  • jlfo and jlco type JLabelOperator
  • jtffo and jtfco type JTextFieldOperator
  • jbcvo and jbclo type JButtonOperator

Add a String title attribute with default value "Temperature Converter" to keep window title.

Initialize Operators in findOperators

Let's add the private findOperator method.
Then
  1. Right click on the OverallTest class and select Generate Code...
  2. Select TDD as target project
  3. Select Functional Test Packages as Source Root
  4. Check Add Merge Markers to Existing Source Elements
  5. OK
When finished, select findOperators method, right click on it and Navigate To Source.
Add this code to the method
   private void findOperators () {
//wait frame
jfo = new JFrameOperator(title);
jlco = new JLabelOperator(jfo, "Celsius");
//
// Using the getLabelFor is a way to locate JTextFields, the other is by name or by initial text
//
jtfco = new JTextFieldOperator((JTextField) jlco.getLabelFor());
jlfo = new JLabelOperator(jfo, "Fahrenheit");
jtffo = new JTextFieldOperator((JTextField) jlfo.getLabelFor());
jbcvo = new JButtonOperator(jfo, "Convert");
jbclo = new JButtonOperator(jfo, "Close");
}

Write the actual tests

testCorrectConversion tests some conversions that are know to be correct, and as a double check the actual result is compared against TemperatureConverter.celsiusToFahrenheit() which has passed the unit tests.
After entering the text into the field, the Convert button is pressed and the value in the Fahrenheit text field is checked.

    public void testCorrectConversion () {
int[] temps = {100, -100, 0, -1, 1};

for (int t : temps) {
jtfco.setText(Integer.toString(t));
jbcvo.clickMouse();

// verify the result
int fahrenheit = TemperatureConverter.celsiusToFahrenheit(t);

assertEquals("conversion", "" + fahrenheit, jtffo.getText());
assertEquals(Color.BLACK, jtffo.getForeground());
}
}
testIncorrectConversion invokes the conversion with malformed or invalid parameters.
    public void testIncorrectConversion () {
String[] temps = { "aaa", "0-0", "--1", "1a", "-274" };

for (String s : temps) {
jtfco.setText(s);
jbcvo.clickMouse();

assertEquals(Color.RED, jtffo.getForeground());
}
}

Application start

Add this code to start the application and to find Jemmy operators. If you add the method calls and then you can use the IDE to complete the surrounding try-catch block.
    public void setUp () {
try {
System.out.println("######## " + getName() + " #######");
app.startApplication();
findOperators();
} catch (InvocationTargetException ex) {
Logger.getLogger(OverallTest.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchMethodException ex) {
Logger.getLogger(OverallTest.class.getName()).log(Level.SEVERE, null, ex);
}
}

Running the tests

Finally, our functional test infrastructure is ready. To sum up: we have now two tests that using the swing GUI, that we are going to implement right now, are converting temperatures and verifying the results and obtaining corresponding error messages when the input data is incorrect.
This was defined in our Use Case:Convert Temprature.
We can see an empty window, and after a while the browser is launched and both tests failing with

fail: Frame with title: "Temperature Converter"

Clicking on the Yes link under Workdir a screenshot is saved and can be analyzed to solve the problem.
That's because we need to implement our swing GUI.

UML TDD Class Diagram

This will be our final Class Diagram, after we implement the GUI

Next article

Next article Test Driven Development and GUI Testing: Implementing swing GUI (coming soon) will show how to build the swing based GUI using Test Driven Development approach and we will obtain our working and fully tested application.

Comercial de Vodafone filmado en Buenos Aires

Este es otro comercial filmado en Buenos Aires, esta vez el anunciante es Vodafone.

Monday, December 31, 2007

Publish to your blog directly from Google Docs

Do you know that you can publish documents directly from Google Docs ?
It's very handy and you can use Google Docs editor with its full power to add formating, links, pictures, tables and check spelling.

If you usually post large pieces of text with formatting t your blog you should take this seriously.

Help can be found here.

Saturday, December 29, 2007

Test Driven Development and GUI Testing: Unit tests

Previous article:Test Driven Development and GUI Testing: Temperature Converter details

We now have an idea of the application requirements as we defined our Use Case: Convert Temperature.
Using Test Driven Development we will implement this use case. We are also using UML to maintain our model.

Main project

Using Netbeans 6.0
  1. Create a Java project (File | New Project... | Java | Java Application)
  2. Name the project something like TDD
  3. Select the desired location
  4. Uncheck Create Main Class
  5. Finish

UML Project

  1. Create a new UML project (File | New Project... | UML | Java Platform model)
  2. Name it something like TDD-UML
  3. Choose previous location. If the previous project folder was TDD select this project folder
  4. Finish

Create Class Diagram

  1. Create a Class Diagram and name it TDD Class Diagram
  2. Create a package tdd dragging a package from the palette
  3. Create a TemperatureConverter class
  4. Create a Nested Link from TemperatureConverter to tdd package
Now, you should have a diagram like this


Generate code

  1. Select the tdd package
  2. Right click and select Generate Code...
  3. Select the previously created project, TDD as Target Project
  4. Select Source Packages as Source Root
  5. Press OK
We now have the structure of an empty class inside the tdd package

Add testing infrastructure using XTest

  1. Select TDD project node in Projects
  2. Right click and select New | Other...
  3. Select Testing Tools and then XTest infrastructure (if it's not there go to the plugin manager and install it)
  4. Finish

Generate test cases

  1. Right click on the TemperatureConverter class node in the TDD project
  2. Select Tools | Create JUnit Test...
  3. IMPORTANT: Select JUnit 3.x
  4. Accept the name tdd.TemperatureConverterTest for the class
  5. Select Location: Unit Test Packages
  6. OK
Now we will add our tests, even though our TemperatureConverter class is not clearly defined yet. Adding those tests will help us define our TemperatureConverter class and it's the essence of Test Driven Development.

Adding the test class to the UML model

  1. Right click on the editor window of the TemperatureConverterTest class
  2. Select Reverse Engineer...
  3. Select Use Existing UML Project and Target Project as TDD-UML
  4. OK

Complete the TDD Class Diagram

  1. Select TDD Class Diagram in the editor
  2. Drag and drop the TemperatureConverterTest class, which is inside the tdd package node, from the Model (TDD-UML | Model | tdd) to the diagram. Sometimes it's also needed to drag and drop the tdd package.
  3. You may want to change the color of the class to recognize it visually as a test, right click on it and select Class | Background color, we use green in this example.

Define the tests

Accordingly to our use case in TemperatureConverter we can define two tests
  • testCelsiusToFahrenheit, to test our converter
  • testInvalidCelsiusTemperature, to validate that wrong temperatures are detected
As a method to verify our converter we will use some results obtained from an external converter source and we will build a table with some representative values.
An online converter can be found at http://www.onlineconversion.com/temperature.htm.
Let's add these components to get our tests finished.

Add the tests methods

In TDD Class Diagram
  1. Right click on the TemperatureConverterTest Operations compartment and select Insert Operation... and insert the operations mentioned previously
  2. add testCelsiusToFahrenheit method
  3. add testInvalidCelsiusTemperature method

Add conversion table

In TDD Class Diagram
  1. Right click on the TemperatureConverterTest Attributes compartment and add attribute conversionTable, set its type to Map<Integer, Integer> and be sure to check the static and final properties
  2. Set the default value to new HashMap<Integer, Integer>()
We should have now this diagram

Generate the code

  1. Select the TemperatureConverterTest class only (be sure that no other component is selected), right click and Generate Code...
  2. Be sure to select Source Root: Unit Test packages
  3. OK

Initialization code

  1. Right click on the ConversionTable attribute and select Navigate to Source...
  2. If some imports are missing just press Shift+Ctrl+I
  3. Initialize the conversion table to this code, just after its definition

    static {

    // initialize (c, f) pairs

    conversionTable.put(0, 32);

    conversionTable.put(100, 212);

    conversionTable.put(-1, 30);

    conversionTable.put(-100, -148);

    conversionTable.put(32, 90);

    conversionTable.put(-40, -40);

    conversionTable.put(-273, -459);

    }

  4. Add this method (the prefix test is important for JUnit 3.x)

    public void testCelsiusToFahrenheit () {

    for (int c: conversionTable.keySet()) {

    int f = conversionTable.get(c);

    String msg = "" + c + "C -> " + f + "F";

    assertEquals(msg, f, TemperatureConverter.celsiusToFahrenheit(c));

    }

    }


  5. Add this method (the prefix test is important for JUnit 3.x)

    public void invalidCelsiusTemperature () {

    try {

    int f = TemperatureConverter.celsiusToFahrenheit(-274);

    } catch (RuntimeException ex) {

    if (ex.getMessage().contains("below absolute zero")) {

    return;

    }

    else {

    fail("Undetected temperature below absolute zero: " + ex.getMessage());

    }

    }


    fail("Undetected temperature below absolute zero: no exception generated");

    }

  6. If some imports are missing just press Shift+Ctrl+I

Create the real code

We have now the tests for code that still doesn't exist. If everything was fine, you should see a light bulb in the line where TemperatureConverter.celsiusToFahrenheit is invoked and gives you the to Create Method celsiusToFahrenheit(int) in tdd.TemperatureConverter
But, sometimes things use to fail, and if it's not appearing use this work around:
  1. Double click on the TemperatureConverter class node, and go to the editor, add an empty line, delete it, just to force the change, then save the file.
  2. Is this a Netbeans bug ?
Now Create Method celsiusToFahrenheit(int) in tdd.TemperatureConverter should have appeared, select it and create the method.

Running the tests

Our code compiles now, and we are in the condition of running the tests. After all, we are using Test Driven Development, don't forget it.
Go to the TDD project node and
  1. Right click and select XTest | Run unit Tests...
These tests will fail, because we haven't already implemented the celsiusToFahrenheit conversion method.
We obtain something like this



We can see that one test failed because celsiusToFahrenheit converter was not implemented and the other because this method didn't generate the expected exception on an invalid temperature.

So, what's left is to go there an implement it !

Implement celsiusToFahrenheit

We need to replace the default implementation of this method

throw new UnsupportedOperationException("Not yet implemented");


with

return (int)Math.round(celsius * 1.8 + 32);


Remember to change the parameter name from i to celsius.

Run the tests again

Running the tests again we will see how one test succeed but the other fails with the message

Undetected temperature below absolute zero: no exception generated

That's because we are expecting an exception to be thrown if the temperature is below the absolute zero but our first attempt doesn't do that.

Let's add this condition, but we first need the ABSOLUTE_ZERO_C constant.

  1. Synchronize the code with the UML by right clicking on the TemperatureConverter editor and then Reverse Engineeer...
  2. Select Use Existing UML PRoject and set it to TDD.
  3. Then OK and Yes to all.

Add ABSOLUTE_ZERO_C constant

This is the absolute zero temperature in Celsius. Now, our code and model are synchronized.

Go to the TDD Class Diagram.

Right click on the TemperatureConverter Attributes compartment in the class diagram

  1. Add ABSOLUTE_ZERO_C, with type int, default value set to -273, and check static and final in properties
  2. Select the TemperatureConverter class and right clicking select Generate Code..., be sure to select Source Root: Source pckages
  3. Select Add mergers to existing source
  4. OK


Now

  1. Select the celsiusToFahrenheit method and then right clicking Navigate to Source...

Modify celsiusToFahrenheit

Add the absolute zero check

if (celsius < ABSOL?UTE_ZERO_C) {

throw new RuntimeException("Invalid temperature: " + celsius + " below absolute zero");

}

return (int)Math.round(celsius * 1.8 + 32);





For some reason (Netbeans bug ?) conversionTable disappear from the Class Diagram and from the model, but it's still there because it's not possible to add another attribute with the same name and if code is generated the attribute is still there.

Run the tests one more time

And both tests pass


Next article

Next article Test Driven Development and GUI Testing: Functional tests will show how to build the swing based GUI using Test Driven Development approach and we will obtain our working and fully tested application.

Friday, December 28, 2007

Jemmy library problem with multiscreen captures

There's a small, but annoying, problem in the way the screen is capture with Jemmy's captureScreen method, as far as I know it's only evident in multiscreen configurations.
The root of the problem is the use of Toolkit.getDefaultToolkit().getScreenSize() which is not multiscreen aware and thus virtual screen bounds are incorrect.

I've detected the problem writing some Java Swing GUI tests that capture the screen or part of it as they run in multiscreen configurations and got weird results. After some investigation and debugging I've found the use of Toolkit.getDefaultToolkit().getScreenSize() in PNGEncoder module.
Anyway, I'm providing a link to a patch using multiscreen aware display dimensions available from GraphicsConfiguration and GraphicsDevice.

The captures you may be obtaining are either incomplete or noisy like the examples shown here.
Showed before and after applying the patch.
Screen capture
Text field capture
Patch
Patch can be downloaded from http://codtech.com/downloads/jemmy-encoder-multiscreen.patch.

Thursday, December 27, 2007

Test Driven Development and GUI Testing: Temperature Converter details

We will be introducing Test Driven Development concepts applied to GUI testing in this series. Obviously, to do it we need a GUI.
In this case we are using a very simple GUI application which is a variation of the Temperature Converter used in the Java Tutorials: Lesson: Learning Swing with the NetBeans IDE.


The original GUI looks like this. However we are introducing some modifications to the design.
One of the problems is that errors are not reported to the user, and the other, which is perhaps a matter of taste is the use of a label to show the result and no button to close forcing the user to use windows manager's close button.

Use Case: Convert Temperature
Although this example is extremely simple, it doesn't hurt if we apply the same set of techniques we would be applying in a more complex problem.















Actor Action System Response
1. The user enters a temperature in Celsius, and then press Convert
2. The system converts the temperature to Fahrenheit and the result is presented to the user
3. The user wants to enter another temperature and continues from 1 or presses Close to exit.
4. The process finishes.
Alternative Courses
Temperature is below absolute zero
Indicate error
Invalid input characters entered
Indicate error


Next article, shows how to start developing this simple use case using Netbeans IDE 6.0, UML, and Test Driven Development.

Wednesday, December 26, 2007

Test Driven Development and GUI Testing: First things first

As you can guess from some previous articles (e.g: http://dtmilano.blogspot.com/2007/04/pyhton-and-test-driven-development-part.html) I'm a proud supporter of Test Driven Development and I've been using it successfully in most of my projects. There are plenty of online resources explaining TDD so we are not going deeper on the subject. What it is not so easy to find is how to use TDD in GUI development and this is the article thread we are stating now.
We will use Netbeans 6 IDE to develop a very simple application (Temperature Converter: lets you convert temperatures in different units, pretty obvious duhh...) presenting a swing GUI which is our testing target. As usual JUnit is used for the testing infrastructure and Jemmy for the GUI components.
We will also be adding some UML to the recipe

Despite the fact that this is an extremely simple application, we will also drive the model from the UML perspective and at least this will help you understand how this can be done for more complex projects.

This Temperature Converter is a slight variation of the one appearing in the Java Tutorials: Lesson: Learning Swing with the NetBeans IDE.

Fasten you seat belts. We are ready to launch.

Next article: Test Driven Development and GUI Testing: Temperature Converter details

Monday, December 03, 2007

Dogfish Administration Console Watchdog for Glassfish


Have you ever been caught by this screen ?
Having to login to the machine where Glassfish domains are installed just to run a simple administrative command to get the server online again. Perhaps requiring a trip to the data center.

The message says:

The domain can be restarted using the asadmin start-domain command. In order to restart the domain:
  • login to the machine where domain is installed.
  • run /bin/asadmin list-domains to ensure that the domain shutdown process is complete
  • run /bin/asadmin start-domain command with appropriate parameters.

Well, I decided to start this tool would help to alleviate this situation.
Dogfish is kept as simple as possible (KISS philosophy), it's a secure web server, based on '
com.sun.net.httpserver' and relative classes, relies on Glassfish authentication, and let's you send the administrative commands to start, stop and restart domains and will be able to define some actions to restart domains in case of failure or no response.
More on this later.

Comments are gladly welcome.

Preview

Friday, November 30, 2007

javax.xml.ws.soap.SOAPFaultException: ERROR: No security header found


Here using Netbeans 6.0rc2 and Glassfish 2 (Sun Java System Application Server 9.1 (build b58g-fcs)) that came bundled in Netbeans download, using java version 1.6.0_03 on Ubuntu 7.10 gusty.

The SecureCalculatorApp and SecureCalculatorClientApp sample applications (New Project->Samples->Web Services->Secure Calculator) work perfect. However, if you try to develop and run a Java destkop application client to consume the CalculatorWS web service in SecureCalculatorApp you obtain

javax.xml.ws.soap.SOAPFaultException: ERROR: No security header found in the message

There are some posts with comments and solutions, but sometimes they don't apply exactly to these software versions, or even worst, sometimes it's not completely clear which versions they are related to. To avoid confusion, the versions I've been testing are clearly stated before.

You can download the full example from [here]. Then Build and Run.

Don't replace or update the certificates in Glassfish because it's not necessary, at least in the scope of this example.

Open the project properties and under lib
  1. Remove JAX-WS 2.1
  2. Add from Glassfish lib directories
  • webservices-rt.jar
  • appserv-rt.jar
  • javaee.jar
  • webservices-tools.jar
  • endorsed/activation.jar
  • endorsed/webservices-api.jar


Rebuild the project, and hopefully everything will work fine. Obviously, SecureCalculatorApp must be deployed for this example to work.

Troubleshooting
If something goes wrong, for example you are receiving an exception like this

Nov 30, 2007 2:32:20 PM [com.sun.xml.ws.policy.jaxws.PolicyConfigParser] parse
INFO: WSP1049: Loaded WSIT configuration from file: file:/home/diego/tmp/NetBeansProjects/SecureCalculatorGUI/build/classes/META-INF/wsit-client.xml
Nov 30, 2007 2:32:21 PM com.sun.xml.wss.jaxws.impl.SecurityPipeBase populateTruststoreProps
SEVERE: WSSPIPE0016: TrustStore URL was obtained as NULL from ConfigAssertion.


You may need to verify your Security settings in the Web Service Reference.
Right click on CalculatorWSService and open Edit Web Serice Attributes and verify these settings.



and clicking Truststore..., verify that Location points to /SecureCalculatorGUI/src/securecalculatorgui/resources/client-truststore.jks as showed in this screenshot


As usual, the password is changeit.

Hope this helps and saves you time looking into the wrong direction.
Comments, suggestions, fixes, etc. are gladly welcome.

Tuesday, November 20, 2007

cult, the fastest thin client ever


Using latest hardware and a fast network, in less than 10 seconds, including the time to download the Operating System from the network, the Microsoft Windows login screen appears (or the Unix/Linux XDMCP session as well...).
Definitely it's worth the try.
To ease the way of testing and initial deployment there's a live CDROM available, just to burn-n-boot, and it could also be copied directly to your TFTP server to serve network booting.
Visit http://cult-thinclient.sf.net.

Wednesday, October 10, 2007

Automagically create GUI applications with autoglade

I've been working on a tool named autoglade that could be of help to
simplify application development using glade as the interface designer.

You can download autoglade from http://sourceforge.net/projects/autoglade.

A basic tutorial can be found in the wiki: autoglade tutorial - first steps.

As the tutorial demonstrates, nautilus actions can be greatly enhanced using autoglade generated GUIs. You are not limited to zenity anymore.
More to come.

Comments, suggestions, critics, bugs, fixes, etc are gladly welcome.
Please use autoglade's mailing lists.

Tuesday, May 01, 2007

Time machine


"In 1980 most of us felt pretty certain that users --customers-- were going to demand the application portability that UNIX promised, and CFOs and CEOs were going to demand the hardware independence that UNIX (in theory) made possible.

But in 1988, the PC revolution had resulted in DOS and PCs stealing the opportunity that we had seen for UNIX, causing us to conclude that Microsoft would continue its domination simply because there was no alternative -- despite the obvious need for a better PC operating system than DOS.


Today I am not sure of the inevitability of Microsoft's continuing dominance on the PC platform, which begs the question: What sort of operating system could challenge those from Microsoft ?

It would have to be cheap and at the same time respected by technical users. It would also have to be compatible with one of the major operating systems standards. It would have to improve faster than its competitors, have true multiuser, multitasking capabilities, and have to come from a vendor with resources to develop the product for a worldwide market and roll out new features on massive, coordinated basis simultaneously.

That's where Linux comes in.

Linux is a 32-bit, multiuser, multitasking operating system that is as stable as, if not more than, any form on UNIX on the PC. The kernel is largely the work of Linux Torvalds, who is now employed by the University of Helsinki, although much of the original work was done while he was a student there. After writing a prototype kernel in 1991, Torvalds posted it to the Internet (in the comp.os.minix newsgroup) and invited the world to help him work on it.

A Propeller Head's Dream
Rob Kolstad, president of Berkeley Software Design, of Colorado Springs, CO, sees Linux as more theology than technology. "Linux users," he says, "do not argue the merits of their chosen technology, they evangelize on the method that was used to create it."

To begin to understand the evangelical aspect of Linux, consider the development cycle at any commercial operating system vendor. The marketing department meets with its largest customers' senior MIS managers (most og whom have not written code in 20 years) to find out what these customers are asking for.
They write reports, conduct surveys, and eventually come up with a list of, say, the 10 most important new features that their operating system developers should work on.

In Linux there are no marketing teams. Developers are the customers. They work on the parts of the code, utilities, or applications they have an interest inusing.
The code these developers build is posted on the net for others to download, test, and if appropriate, provide suggested changes, report bugs, or use for whatever purpose they chose. Coordination of all this development effort is casual but effective. There is a designated person responsible for each major aspect of the Linux operating system, Torvald's leadership of the kernel being a famous example.
[...]



This was published by Robert F. Young in Uniforum Monthly in October 1994.
Can you spot some coincidences with today's ecosystem ?
13 years have passed and are we still arguing about the same things ?

Sunday, April 29, 2007

Skoda Fabia

Otra vez Buenos Aires. Esta vez en el comercial del nuevo Skoda Fabia.
http://www.skoda-auto.com/importermastereng/model/newfabia/home
o aqui (la extended version).
Enjoy.

Friday, April 20, 2007

Python and Test Driven Development - Part I

Briefly, Test Driven Development is the strategy of starting the development process by the test cases and then provide the software that satisfies these tests, not the other way around.

Maybe you have been already charmed by the Test Driven Development and its innumerable virtues.

If not, I strongly recommend that you take a look at Wikipedia's (http://en.wikipedia.org/wiki/Test_driven_development) which is a good place to start and has a lot of external links.

The following UML activity diagram depicts the Test Driven Development process.

Start writing the tests, run the tests to verify that the test you have just written fails, write the code that satisfies it and the refactor the code to keep it tidy, clean an minimal.

Personally, the main advantage I've seen so far is that you focus your destination quickly and is much difficult to divert implementing options in your software that will never be used and wasting your precious time so much needed in order to arrive to the end of the project on time. To cut short what could be a very long story, I don't want to debate now if Test Driven Development could be applied to any project. I think that, as well as any other technique, you should use your judgment and expertise to recognize where it can be applied and where not. But having this in mind: there's no silver bullets.


1 Unit testing

Unit is the portion of your software being tested. You should also apply your experience to determine, in each case, what a unit can be. Sometimes it is a class, a set of classes or even a a method. In general, the tools are called xUnit where x is your favorite language (Java, Python, C++, VisualBasic).


2 Test suites

Tests are composed into suites. Suites are a convenient way to keep related tests together and simplifies running all of the tests in every iteration of the process. Normally, all of the tests should be run to verify that nothing else is broken.

3 Test runners

We will be using a python example. Also, assume that we have already written some tests classes stubs containing some tests placeholders. These classes are ATest, BTest and CTests, each in its corresponding module inside the tests subdirectory. These will help to maintain the separation between code and tests. They will test the correctness of classes A, B and C respectively.

#! /usr/bin/python

import sys
import unittest

class ATest(unittest.TestCase) :
def test1(self) :
self.assertEqual(1, 1)

def test2(self) :
self.assertEqual(1, 1)

if __name__ == '__main__':
unittest.main()

The same for the other classes.

In python, when you are writing a test driver you write something like this

#! /usr/bin/env python

import sys
import unittest

from tests.ATest import ATest
from tests.BTest import BTest
from tests.CTest import CTest
from tests.moretests.DTest import DTest

if __name__ == '__main__':
unittest.main()

This is a test driver that imports some classes to be tested, and the unittest discovers all of the test cases using introspection.

Nevertheless, there are some drawbacks with this approach:

  • we need to create a test driver like this in any project
  • we need to maintain the driver and if a new class is created in the project it must be added to the driver
  • the names of the classes must be known in advance

Thus, we will try to address this problems creating a generic test driver, independent from the project we are working on, and this driver will generate automatically the Test suite accordingly to a given pattern of classes and a path to look for the modules.

With all of this in mind, we try our second driver. This driver uses the function find which emulates partially the behavior of the Unix find command (see downloads).

#! /usr/bin/env python

import sys
import os
import re
import unittest

from find import find

def loadTestsFromPath(dir='.', pattern='*Test.py', maxdepth=None) :
modules = []
for f in find(dir, pattern, maxdepth) :
mo = os.path.basename(f)[:-3]
mp = re.sub('^\./?', '', os.path.dirname(f)).replace(os.sep, '.')
if mp:
mp += '.'
modules.append(mp+mo)

return modules


if __name__ == '__main__':
ts = unittest.TestSuite()
for m in loadTestsFromPath('.') :
ts.addTests(unittest.TestLoader().loadTestsFromName(m))

unittest.TestProgram(defaultTest='ts')

Now the situation is much better

  • this is a generic driver, there's no need to maintain it with the target project
  • the names of the classes are not included in the driver
  • automatically the driver finds the existing test cases in the filesystem

We still need some improvements, but it is pretty usable right now. The possible improvements are

  • we require the ability of passing command line parameters to obtain the maximum flexibility, for example the path to start the search and the maximum depth to continue searching
  • this command line options should be integrated with the unittest options
  • the pattern should be variable

These improvements will be the subject of the next part: Pyhton and Test Driven Development - Part II

Wednesday, April 18, 2007

Citrix ICA Client 10 on Fedora Core 6

And... because I'm still in love with Fedora, in spite of my migration to Ubuntu in most of our customers, here you are: http://codtech.com/wiki/index.php/Citrix_ICA_Client_10_on_Fedora_Core_6

In fact, I should say, there's a lot less desktop problems with Fedora than with Ubuntu, but this is another story.

Tuesday, April 10, 2007

Citrix ICA Client 10 and Ubuntu


If you have downloaded and installed the new Citrix ICA Client 10 in Ubuntu 6.10 Edgy Eft you may have already discovered that it doesn't work out of the box.
You may receive some font errors or missing dependencies, and because Citrix doesn't provide a DEB package these missing dependencies are not detected by the tar.gz package installer.

To simplify the installation, or better the post-install steps, I've made a script available at http://codtech.com/downloads/citrix-icaclient-10-ubuntu which verifies that dependencies packages are installed and patch the configuration files to get rid of the error with UTF fonts.

Download the script, and if Citrix is installed in a system location use sudo to run it.
Don't forget to set ICAROOT in the environment if it's not /usr/lib/ICAClient.

Comments are welcome.

Monday, April 09, 2007

Veolia

No solo crecen naranjas en Buenos Aires. Como veran en este comercial de Veolia que tambien se puede ver en canales europeos.

Thursday, March 29, 2007

Tropicana

Miren este comercial de Tropicana que estan pasando en la mayoria de los canales de UK.
No sabia que desde que me fui habian plantado tantos arboles.

Monday, February 05, 2007

El regalo de cumpleanos de Octavio

No hacen falta mas comentarios, Octavio con su nuevo Buzz Lightyear (y Zurg en off).

Sunday, February 04, 2007

New gnome-terminal-launcher released

A new release of gnome-terminal-launcher has been released last week. This new release includes some interesting features like gnome-terminal profiles sorting and GConf events notification.
If you are a sysadmin it's worth a try.
Visit gnome-terminal-launcher web site to read the details.

Thursday, January 11, 2007

Compaq Deskpro 50M


After almost 14 years, I finally threw away my Compaq Deskpro 50M, because there's a real need to reduce the number of things I'm moving.
Born as a high end workstation but it has bravely died as a PXES Universal Linux Thin Client, strictly speaking it was the only Intel 486DX thin client that I've been using so far for the development and testing of PXES images.

Farewell Deskpro 50M !

Tuesday, November 14, 2006

Sun opens Java under the GPL license

Finally, the day has come.
Yesterday, in a historic move, Sun opened the Java Platform Standard Edition (Java SE), Java Platform Micro Edition (Java ME), and Java Platform Enterprise Edition (Java EE) —under the GNU General Public License version 2 (GPLv2).
Read more...

Friday, November 10, 2006

What is a thin client, really ?

Sun Microsystems CEO Jonathan Schwartz wrote on his blog this definition about thin clients:


Industry convention says that apps written to browsers are defined to be "thin." But by that definition, thin really equates to "using someone else's runtime environment" -- in that the browser itself has to be present for the service to be rendered. And last I checked, browsers require operating systems and windowing environments. Not exactly thin. So in my book, it's inaccurate to say Google or YouTube are "thin clients" -- they're services that leverage someone else's thick client. A browser.


This is really true and seems to reflect a feeling that I've had already.

Why do people call thin client a small computer which has an operating system (Windows CE, XPe, or Linux, it doesn't matter) installed in a local media, perhaps a flash memory ?
And think that it's not only an operating system, but applications too.
Like the web browser.

Do they call it thin client because the disk usually doesn't spin ?
Because the upgrades are much more difficult and tedious than upgrading a fat client disk ?
Because the operating system and applications fits in only 64 or 128 MB of flash ?
So, the desktop PCs from sime time ago, when disks were much smaller, were thin client too.
And we didn't know it !

Real thin clients boot off the network. Stop.

Wednesday, October 04, 2006

Vuelta al cole


Cuando los primeros rayos del sol asoman sobre el horizonte, Augusto se prepara para su primer dia de clase en Saint Michael School en Junior I, lo que seria equivalente a Primer Grado.
No les parece mentira ?
No, que nos levantemos a esa hora (incluido yo que tome la foto) no, sino que ya vaya a la escuela ?
Si ayer era un bebe...