Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Thursday, July 19, 2012

Code coverage with Android and EMMA

Recently, I have been looking in to tools in Android that would help me improve code quality and find bugs before putting the product out.  I picked up a copy of Diego Torres Milano's "Android Application Testing Guide" as a good place to get me started.   Since there are a lot of tools out there, and not all of them support Android, it was a useful guide to tools that should be working.

One of the tools I was looking in to is code coverage.   Over the years, EMMA has been integrated with the Android build tools, which is supposed to make it easier to use.   Unfortunately, if you don't use them just the right way they can end up being a horrid time suck trying to figure out why your instrumented build just isn't working.

For my testing, I was using the latest tools available as of this writing.  (That would be ADT 20)   Lots of the information available on the Internet is for older versions of tools that don't use the same commands as ADT 20.  I also started with the most basic app I could, and set it up so that I had an easy way to intentionally miss some code, or make sure I hit it all.   The code looked something like this :
@Override
public void onCreate(Bundle savedInstanceState) {
   String appname = "unknown.app";
   super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);
}

public void testme() {
    System.out.println("This is a test!");
}

A pretty simple "app" that mostly comes out of the Android tools when you create a new project.   The "testme()" method is there because it will allow us to explicitly call some code (or not) to let us see that code coverage is really working.

Next, I created a test app using the Android tools and created a new test case.   The test case extends ActivityInstrumentationTestCase2, as it will start up the activity we want to test in a state that is easy to use.  The test class looked something like this :

public class MyTest extends ActivityInstrumentationTestCase2 {
public MyTest()
{
   super(TestActivity.class);
}

public void testCodeCoverage()
{
}

The "TestActivity" referenced in the above code is the name of the Activity that we created earlier.   Also, note that our test doesn't actually do anything.   For now, this is okay.   When the MyTest class is run, ActivityInstrumentationTestCase2 will create the activity, which will result in the onCreate() method being called.   So, once we have our code coverage running, we will have a block of code that was covered, and a block that was not.

At this point, we are going to use the command line.   We also need to make sure that we have a fairly current version of Apache Ant ready to use.   For my tests I was using 1.8.2.

At the command line, we want to convert our apps to use Ant for building.   To do this, we need to use the "android" command that is included in the SDK.  (On Windows, it is "android.bat")  We need to run two commands, one to convert the "TestActivity" app and one to convert the "MyTest" test project.  The command will look something like this :

android update project --path /path/to/TestActivityProject --name TestActivityProject --target android-16
The "--name" parameter should be set to whatever you named your project, and "--target" should be set to the API level that you want to test with.   The "--path" parameter requires the FULL path name to your project.   So, if you are on windows it might be something like "c:\users\foo\workspace\TestActivityProject" I tested with an Android 4.1 VM, so I chose "android-16".  The version you use isn't too important, unless you plan to use Ant to make your final builds.

Next, to convert the test project :

android update test-project --main ../TestActivityProject --path /path/to/MyTestProject
The "--main" parameter confused me a bit at first.   It should be a relative path from the directory you store "MyTestProject" in, to the path of "TestActivityProject".   If you are using Eclipse and putting your code in a workspace, you should normally need to use ../.

Finally, we are ready to build and run our test.  Go in to the directory for "TestActivityProject" and run "ant".  (If ant isn't in your path, you may need to use a full path to ant instead.)

But, whats this?  We get a help screen like the following :


Android Ant Build. Available targets:
   help:      Displays this help.
   clean:     Removes output files created by other targets.
              The 'all' target can be used to clean dependencies
              (tested projects and libraries)at the same time
              using: 'ant all clean'
   debug:     Builds the application and signs it with a debug key.
              The 'nodeps' target can be used to only build the
              current project and ignore the libraries using:
              'ant nodeps debug'
   release:   Builds the application. The generated apk file must be
              signed before it is published.
              The 'nodeps' target can be used to only build the
              current project and ignore the libraries using:
              'ant nodeps release'
   instrument:Builds an instrumented package and signs it with a
              debug key.
   test:      Runs the tests. Project must be a test project and
              must have been built. Typical usage would be:
                  ant [emma] debug install test
   emma:      Transiently enables code coverage for subsequent
              targets.
   install:   Installs the newly build package. Must either be used
              in conjunction with a build target (debug/release/
              instrument) or with the proper suffix indicating
              which package to install (see below).
              If the application was previously installed, the
              application is reinstalled if the signature matches.
   installd:  Installs (only) the debug package.
   installr:  Installs (only) the release package.
   installi:  Installs (only) the instrumented package.
   installt:  Installs (only) the test and tested packages (unless
              nodeps is used as well.
   uninstall: Uninstalls the application from a running emulator or
              device. Also uninstall tested package if applicable
              unless 'nodeps' is used as well.

This is where I really screwed up and spent a lot of time scratching my head.   Since we want to use EMMA, we need an instrumented build, right?   So the command that we need is "ant emma instrument install test" to get a build, install it, and run the test in it.

While this seems like the most intuitive answer, you will quickly find out that it doesn't work.  Everything will compile and install fine, and we can assume that the resulting apps are instrumented.   However, when it gets down to running the test, you will be greeted with something like this :


     [echo] Running tests ...
     [exec] INSTRUMENTATION_RESULT: shortMsg=java.lang.IllegalAccessError
     [exec] INSTRUMENTATION_RESULT: longMsg=java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation     [exec] INSTRUMENTATION_CODE: 0

Hmm..  Something didn't work.  This is where I spent a ton of time trying different combinations and searching to understand what was going on.   Finally, as I was giving up, I noticed a browser page that I had already opened that said for EMMA code coverage, we need to run "ant emma debug install test".  I didn't think it would work, but I gave it a shot.  Crazy as it is, it DID work!  (I literally spent 7 hours trying to figure out what the above error message was all about.)

Once your instrumented build is run, the necessary coverage files will be downloaded, and a coverage.html file will be created in your test project's "bin" directory.   Opening that up should show that your onCreate() method ran all of its code, but the testme() method wasn't touched at all.   If you go in to the testCodeCoverage() method in the test app, you should be able to add the line "getActivity().testme()" and then run the instrumented build again.   This time, you should get 100% code coverage.

Hopefully, this post saves someone out there a significant portion of their time getting started with code coverage!

Monday, April 11, 2011

Color changing rounded rectangles and Android

I recently came across an interesting problem. I needed to have a rounded rectangle as the background of one of my widgets, but I needed to allow the border color of the rounded rectangle to be set by a configuration setting.

Before the color changing requirement showed up, I was using a 9-patch to create my rounded rectangle. It was simple, and worked quite well. So, after some digging I came to the conclusion that I would need to change the colors in the 9-patch image in order to achieve my goals. I ended up burning a lot of time trying to figure out how to do this. (It isn't as simple as changing the colors in a normal .png file, unfortunately.)

While digging, I came across this post that hinted that I might not need the 9-patch to do what I wanted. But, the documentation for the shape XML is pretty much non-existent. Which made things hard.

A little more digging found an article that linked to this page. This provided me the attributes for the XML that allow me to do what I needed to do. So, I set out to give it a shot. I started with a rounded rectangle with a white background and a black line around the outside. The XML looked like this :

<!--?xml version="1.0" encoding="utf-8"?-->
<shape android="http://schemas.android.com/apk/res/android" shape="rectangle">
<solid color="#ffffffff">
<stroke width="2dip" color="#ff000000">
<corners radius="75dip">
</corners></stroke></solid></shape>


When I created the XML file in eclipse, there wasn't an option to create a shape XML file. So, I picked one of the other types, and just deleted what was automatically created, and replaced it with the code above. However, I also needed to move the XML file so that it was in the drawable directory. (If you are like I was, you are thinking, "But an XML file isn't a drawable!" But, you'll just have to trust me.)

This provided me the basic rounded rectangle that I needed. But, changing colors on-the-fly was still an issue. After a bit of digging, I found that using this XML to create a drawable would give me everything I needed to get the job done.

I decided to set the background for the layout that contained everything I was going to use. (Which is why I am using a LinearLayout instead of some other widget. But, any widget that allows a background to be set should work.)

For a test, I changed my onCreate() method to include this code :

workareaLayout = (LinearLayout)findViewById(R.id.rightColumnInner);

GradientDrawable sd = (GradientDrawable)this.getResources().getDrawable(R.drawable.roundrect);
sd.setColor(0xff00ff00);
sd.setStroke(10, 0xffff0000);
workareaLayout.setBackgroundDrawable(sd);

This created a nice rounded rectangle that is suitable for Christmas. (It has a green background with a large red border.)

The sd.setColor() call sets the background color for the rectangle. The color is an ARGB value, so we start with 0xff since we don't want any transparency. The 00ff00 portion indicates that we want to be the brightest green we can have.

The sd.setStroke() call specifies the width of the border line, along with the color. For this example, the width is 10, and the color is bright red, with no transparency. The documentation isn't clear what a width of "10" means. (Is it pixels? Or one of the other measures commonly used in Android development?) But, it appears that it is probably pixels.

And with that, you should have the ability to create all kinds of crazy rounded rectangles. However, keep in mind that this same method should work for other types of shapes that can be created with the shape XML.

Monday, July 12, 2010

The right way to do databases in Android

Feel free to comment below on what a loser I am. I am sitting in a cabin outside of Yellowstone National Park, writing a blog entry about doing databases on Android. However, I do think I should get a little geek cred as I am posting it from an EDGE connection on T-Mobile across the FroYo wireless tether. But, anyway...

When I first started working with Android, I found a simple tutorial on how to build SQLite databases. A few weeks ago, I was trying to track down the information again for project for my day job. After searching a bit, I was unable to find the original tutorial, but I found one that was a little better. The newer tutorial pointed me toward a much better way to handle databases.

In the Android SDK is a class called SQLiteOpenHelper. This class has two methods that Eclipse will force you to implement. The first is onCreate(), the second is onUpdate().

The onCreate() method is automagically called when you instantiate your descendant class, and the target database doesn't exist. In this method, you should issue all of the necessary SQLite commands to create the tables in your database. But, hold on a second. How can you put tables in the database when you didn't specify the database name? Simple, when the class is instantiated, the database name is passed in. The SQLiteOpenHelper class then takes care of creating the database for you, before the onCreate() method is called.

The onUpdate() method is also really handy and cool. When you instantiate the descendant class, you also pass in a database version number. If the version number you pass in is higher than the version number when the database was created, the onUpdate() method is called with version number stored in the database, and the version number presented when the class is instantiated. onUpdate() is responsible for issuing the correct SQLite commands needed to bring the database up to the schema version specified when the class was instantiated.

Okay, so blah, blah, blah.. Sample code!?


public class DBOpenHelper extends SQLiteOpenHelper {

private final String MY_DATA_TABLE = "t_Data";
private final String MY_DATA2_TABLE = "t_Data2";

public DBOpenHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}

@Override
public void onCreate(SQLiteDatabase myDB) {

/* Create a Table in the Database. */
myDB.execSQL("CREATE TABLE IF NOT EXISTS "
+ MY_DATA_TABLE
+ " (mystring1 VARCHAR, mystring2 VARCHAR);");

myDB.execSQL("CREATE TABLE IF NOT EXISTS "
+ MY_DATA2_TABLE
+ " (mystring3 VARCHAR);");
}

@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
// We don't use this just yet.
}

}




Great! So the database gets created, but how do we actually use it?

Well, I am glad you asked. The SQLiteOpenHelper has methods that will return a handle to the database in either read only, or read/write flavors. So, if we wanted to use our open helper to create the database, and add a record to the second table, it would look something like this :



public void addDbData()
{
DBOpenHelper dbHelper = new DBOpenHelper(this, "my_database", null, 1);
SQLiteDatabase myDb = dbHelper.getWriteableDatabase(); // Open for write.

// Now, use the normal SQLiteDatabase methods to work with the DB.
myDb.execSQL("insert into " + MY_DATA2_TABLE + " set mystring3='Foo!';");
}


After you call addDbData(), you will now have a new database created with the name 'my_database', with schema version 1. The MY_DATA2_TABLE will also contain a single record containing the string "Foo!".

Two things worth pointing out in the addDbData() method. First, if you only want to read data from your database, you can change "getWriteableDatabase()" to "getReadableDatabase()". Second, always be sure you close your database after you are finished with it! The Java garbage collector won't do it for you, and you will get leak errors sent to your logcat console.

I am not going to get in to the update method of the SQLiteOpenHelper. I leave that as an exercise for you.

Wednesday, June 9, 2010

Something simple. Get text from a ListView.

Perhaps this topic is considered too simple for most people, which is why there aren't more postings about it. But, it seems like a useful thing to know. Specifically, when you are using a ListView with the android.R.layout.simple_list_item_1 view, how the heck do you get the text from the item clicked in the OnItemClickListener class?

It is pretty simple. The second parameter of the onItemClick is a View that contains just the item that was clicked. (Or tapped, whatever.) If you used Eclipse to automagically generate the callback method for you, the variable will be named "arg1", which is what we will assume for this example.

Inside of the onItemClick method, you can figure out the text using the following code :

String s = ((TextView)arg1.findViewById(android.R.id.text1)).getText().toString();


Pretty simple, huh? But, the code doesn't quite explain itself. So, I'll take a stab at it.

A ListView is exactly what the name says. It is a list of views. When you create the ArrayAdapter that you will use to display the data, you also tell the ArrayAdapter what view it should use. In our case, we chose "android.R.layout.simple_list_item_1" so we could use a simple array for the data. What we are doing is referencing a very simple layout that only displays a single line of text. I am sure if you Google around you can find the XML definition.

Inside the layout is a single definition. It is a TextView named "text1". Since it is a built-in layout, we will find the layout information under "android.R", instead of just "R" where our layout definitions show up.

So, by executing findViewById() on the view that was clicked, and passed in as "arg1", we can get the text that the user clicked on.

Pretty simple huh?

Monday, May 17, 2010

Stupid Android ListView tricks

I recently made some major changes to the Movie Track app that I put out. What started as an app that I did for my own use seems to have gained a few users. So, I decided to upgrade the look and feel a bit. In the process of this, I changed several of the screens from using a ListActivity to using a normal XML layout that contained a ListView. After mucking around with all of the settings to get things looking the way I wanted, I ran in to an interesting snag.

Everything displayed the way I wanted it to, but to "click" on an item that was in the ListView, you had to actually "click" on the text of the ListView, and not just on the row in the ListView. Perhaps I am a perfectionist at some level, but this annoyed me to no end.

Much Google later, I couldn't find the answer on how to do it. So, I started to mess with settings. A hair-brained idea or two later, I decided to try changing the ListView's layout-width from "wrap_content" to "fill_parent". Amazingly enough, that did exactly what I wanted!

Sure, this was a bit of a "duh" experience, and a newbie mistake. But, I figured I would publish it here in case some other developer runs across the same thing, and gets as frustrated as I was getting!

Saturday, May 15, 2010

Using Ubuntu 10.04 LTS server for Subversion

My old subversion server is in need of a bit of an upgrade. I decided to move it to one of my VMware ESXi servers. (Yes, I really have ESXi servers in my basement. What can I say?) Anyway, since this is a low volume service, moving to an ESXi server seems to be the most economical way to keep my code safe without running the power bill (any farther) through the roof.

Since I have enough VMs to keep track of, I am going to build this one on Ubuntu 10.04 LTS, since Ubuntu seems easier to keep up-to-date. However, taking you step-by-step through the installation isn't something I am going to do. Rather, I will comment on any "non-standard" steps I take, and then include all of the follow-up information needed to get the subversion server up and running.

When I installed the server, I didn't select any of the options on the type of server I wanted to set up. Instead, I just continued past that screen and completed the installation. Once the machine rebooted, I logged in with the account I created, and got to work.

The first two things you will probably want to do is set a static IP address for your server, and install the openssh server so you can work on the machine remotely. Setting the static IP is easy. Good instructions can be found here : http://www.howtogeek.com/howto/ubuntu/change-ubuntu-server-from-dhcp-to-a-static-ip-address/. Installing OpenSSH is also easy. At the server's command line run the command "sudo apt-get install openssh-server".

Once the IP address is set, you will either need to reboot the machine, or manually change the address by hand. I suggest a quick reboot, as it shouldn't take too long. Once the machine reboots, we can get on to setting up and configuring the subversion server.

There are several different ways to configure a subversion server. And I have used many of them. By far, my preferred method is to do subversion over http as the Tortoise SVN client for Windows will let me cache my username and password in this mode, and not have to enter it a bunch of times anytime I do any work in the repository.

So, the first thing we need to do is install Apache. I went ahead and installed it using the command "sudo apt-get install apache2". After a few seconds, I had Apache installed and ready to rock.

The next thing I needed to do was actually install subversion. This is easily done by running the command "sudo apt-get install subversion". After a few more seconds, I had subversion installed.

If we Google around, we can find some information on what to do next at https://help.ubuntu.com/community/Subversion. The only problem is, that I am using the server version, so the UI steps are of little help. But no problem, we can easily do the same thing from the command line!

First, we want to create a subversion group to use. To do that, we need to run the command "sudo addgroup subversion". You should get a message indicating that the group was added, along with listing the GID (group ID) of the group that was just created.

To do that, run the command "sudo nano /etc/group", then find the group we just created, and add both "www-data" and your username to the end of the subversion group line.

Now, you should be a member of the subversion group, but you will need to log out and back in before the change will take affect. So do that now.

Next, we need to create the directories to store the files that we upload via subversion. To do that, execute the following commands :

cd /home
sudo mkdir svn
cd svn
sudo mkdir repo
sudo svnadmin create /home/svn/repo


Then, fix the permissions with these commands :

cd /home/svn
sudo chown -R www-data:subversion repo
sudo chmod -R g+rws repo

Now, to bring things full circle, we need to make sure we have the Apache 2 SVN libraries installed. To do that, run the command "sudo apt-get install libapache2-svn".

Now, we need to edit the Apache config so that it knows how to deal with the subversion requests. Change in to the directory /etc/apache2/mods-available, and run the command "sudo nano dav_svn.conf". Then, add the following block to the end of the file to enable subversion through Apache :


DAV svn
SVNPath /home/svn/repo
AuthType Basic
AuthName "Subversion Repository"
AuthUserFile /etc/subversion/passwd

Require valid-user




Whew! This is a lot of work. BUT, we are almost there! The last thing we need to do is set up authentication for the web interface so that not just anyone can muck around in our repository. Doing that is easy. Run the command "sudo htpasswd -c /etc/subversion/passwd " where is replaced with the user name you want to use to log in. To add additional users, use the same command line, but remove the "-c" option. (If you don't remove the -c option you will erase the first user you created!!!)

Congratulations! At this point, you should have a subversion server all set up! To access the server, you would use the URL http://
/svn .

What does it take to add the Mob to your existing Android applications?

When I get bored, I code. And when I get money hungry, I also code. I currently have one app out on the Android marketplace, that has been downloaded several times. In all honesty, I didn't expect the app to become the next big thing. I also wasn't sure it would be useful to people beyond myself. But, it seems that there are at least a few other people out there that believe my Movie Track app has some value.

Now, to be fair, I am all for free apps. I have done a lot of free open source development in my time, and have generally enjoyed it. But, I also like to get a little something extra out of my work when possible. So, I have decided I am going to try adding AdMob to my Movie Track program, and see if I can generate a few bucks along the way. (For those that hate ads, I'll also offer an ad-free version for $1.)

Signing up for AdMob is pretty easy. Just hop over to the AdMob web site, and create a new account. Once the account is created, you will get an e-mail with a link that needs to be clicked in order to verify the e-mail account is yours. But, once you have done that, it starts to get confusing in a hurry as to what to do next.

First, some terminology. When I first logged in with my shiny new account, there was a lot of talk about "Advertisers" and "Publishers". Since we are putting ads in to our programs, we are "Publishers". So, unless you want to advertise things in other people's programs, you should look for information that pertains to publishers.

Once you log in to the site, and click on the Marketplace link, the Campaigns tab shows information that is only relevant to advertisers. Or does it? Clicking on the "New Advertiser Training Videos" takes you to a page with videos about how to use AdMob to advertise new products and services. However, if you scroll down toward the bottom of the page, you will also find that there are videos for publishers down there as well.

A quick look at some of those links tell us that we need to create an Android App profile on the AdMob site in order to allow us to put ads in, and track the results. To do this, we need to hover over the "Sites & Apps" tab, and select the "Add Site/App" option from the drop-down menu. Next, click on the "Android App" icon, and scroll down to enter the rest of the information.

As you fill this form out, there is a section for the Android package URL. The help information that is available says it must be a mobile site, or the URL to your package in the market place. (The FAQ says the URL in the market place will be formatted as : market://details?id) However, digging around, it seems there should be a "=" between the "id" and the "". You can find more information at http://developer.android.com/guide/publishing/publishing.html.

This left me with a couple of questions that I will probably post more about later as I dig deeper. The first one is, "What is this URL used for?". And the second is, "What if I have not yet published the app? What do I put in there?".

Since this field isn't in bold, I am going to guess that it may not be important what goes in there. But, I like to be complete where possible, so I will be putting in the link to my app in the form of "market://details?id=info.geektaco.movietrack". For the category, I selected "Entertainment", and then I entered a description of the Movie Track program, and clicked the "Continue" button.

It would appear that I entered the correct information, as the next screen I was presented with is a link to download the "Install Code" to add to my program. I went ahead and grabbed the SDK, and a copy of the PDF version of the SDK documentation for later use, and then clicked "Go to Sites/Apps".

Now, we are getting somewhere. We should now see the dashboard for AdMob. In the dashboard is a link to update your payment information. That part seems pretty straight forward, so I won't go in to detail here.

And now, for the fun part! Let's add the ads to our program, and go make some money!!!

(Continued in later posts)

Friday, March 12, 2010

Well, isn't this QT... (Building 64-bit binaries on Windows 32-bit)

For a project that I work on, we have a 32-bit build "server" running on Windows XP Professional. We are starting to expand this project to support 64-bit systems. No problem, right? Visual Studio has cross-compilers for x64 machines. So, we can still use our 32-bit system to build 64-bit binaries.

Umm.. Sure... Something like that.. I spent a rather long weekend trying to figure out how to do this very thing. The problem that I ran in to is the UI for the project uses the QT libraries.

I started off trying to build the x64 QT libraries using the x64 compilers on a Windows XP development machine. I didn't get very far in the process before I hit a dead end. The build would bail out when it attempted to run qmake.exe. After beating my head on it for a bit, I realized that the build system that QT uses expects to compile tools like qmake, and then execute them to complete the rest of its build process. So, the build process would build qmake for the x64 architecture, then attempt to run it, which obviously wouldn't work. (Remember, I was on a 32 bit system.)

I suspect that someone that wanted to badly enough could work around this with various types of trickery. But, I happened to have an x64 machine kicking around my house, so I just built it on that machine, and then decided to try to move it to the 32-bit build system. The x64 build went smoothly when it was done on an x64 machine. (Imagine that!)

Of course, once I moved my shiny new x64 libraries over to the Windows XP build machine, nothing worked. It didn't work because all of the .exe files that were used to handle the QT magic were 64 bit binaries! So, to get things working, I had to find a way around it.

Since the tools that are used in compiling QT programs basically take some type of data (usually XML) and translate it to code that can be compiled. The code that is generated is nothing more than a text file that is then fed in to a compiler. So, it seemed that I might be able to replace some of the .exe files that QT was attempting to call, and end up with something that worked, even if it was something the QT guys would turn up their noses at.

My first attempt was to copy all of the .exe files from the /bin/ directory of a 32-bit install over the .exe files in the /bin/ directory of the 64-bit install. After doing this, I kicked off a build and waited to see what blew up so I could figure out how to fix it. To my surprise, the project built without complaining.

I figured it couldn't POSSIBLY be THAT easy. But, it looks like it is. The binary that was built seems to work on x64 versions of Windows when cross-compiled from a 32-bit version of Windows.