Денис Тимофеев (denistimofeev) wrote,
Денис Тимофеев

Creation Android.jar for Unit tests

Post about android. So on english.

Any method in Google's SDK android.jar throws an exception. So you couldn't use it for unit testing.

Stephen Ng wrote good article with partial solution: "Manually remove throw statements in needed classes".

This is very expensive-in-time and boring work. We can automatize the removal.

But first of all we need sources of android.jar
Here the instruction how to download android sources and build them: source.android.com/source/download.html
After make completes you can find stubs sources for android.jar in directory like:

I used them instead of real sources, because they already in one place.

So, let's start:

First try
- remove all throw statements from method body.

- some method has non-void return type
- some class has no standard constructor

Second try:
- remove all throws statements and add
  • return 1 to byte, short, int, long, float, double, char methods
  • return false for boolen methods
  • null for other object methods
  • save super() calls in constructors
- some classes have final uninitialized fileds. Oh! I forgot them.

third try:
- set byte, short, int, long, float, double, char fields to 0
- set boolean fields to false
- set other fields to null

- some classes have static{} section with initalization of final fields

fourth try:
- initialize uninitialized fields only

This is it.

- remove all throws statements and add
  • return 1 to byte, short, int, long, float, double, char methods
  • return false for boolen methods
  • null for other object methods
  • save super() calls in constructors
- initialize uninitialized filds only
  • set byte, short, int, long, float, double, char fields to 1
  • set boolean filds to false
  • set other fields to null

Useful links to Java language specification:
Method declaration: java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4
Constructor Declaration: java.sun.com/docs/books/jls/third_edition/html/classes.html#8.8
Field declaration: java.sun.com/docs/books/jls/third_edition/html/classes.html#8.3

Than I imported parced sources to eclipse project, build them and created android.jar in console.
Here you can download it: www.4shared.com/get/KWwSl5an/android.html

#!/usr/bin/perl -w
use utf8;
binmode STDOUT, ":utf8";
binmode STDIN, ":utf8";

@targets = ();
@targetdirs = ();
foreach $parameter (@ARGV){
chomp $parameter;

if (not -d $parameter){
print "You have passed not a dir. Script gets dir only and performs processing.\n";
push @targetdirs,$parameter;

while($dirname = shift @targetdirs){
opendir $dir, $dirname;
while( $file = readdir $dir){
if (-d $full and $file ne "." and $file ne ".."){
push @targetdirs,$full;
if($file =~ m/^.+\.java$/){
push @targets,$full;

my $BracketsN;
$BracketsN = qr/ (?> [^()]+ | \( (??{ $BracketsN }) \) )* /x;

my $BracesN;
$BracesN = qr/ (?> [^{}]+ | \{ (??{ $BracesN }) \} )* /x;

my $AngelBracketsN;
$AngelBracketsN = qr/ (?> [^<>]+ | < (??{ $AngelBracketsN }) > )* /x;

$NotReserved = qr/(?!abstract\s+|assert\s+|boolean\s+|break\s+|byte\s+|case\s+|catch\s+|char\s+|class\s+|const\s+|continue\s+|default\s+|do\s+|double\s+|else\s+|enum\s+|extends\s+|false\s+|final\s+|finally\s+|float\s+|for\s+|goto\s+|if\s+|implements\s+|import\s+|instanceof\s+|int\s+|interface\s+|long\s+|native\s+|new\s+|null\s+|package\s+|private\s+|protected\s+|public\s+|return\s+|short\s+|static\s+|strictfp\s+|super\s+|switch\s+|synchronized\s+|this\s+|throw\s+|throws\s+|transient\s+|true\s+|try\s+|void\s+|volatile\s+|while\s+)/;
$JavaLiteral = qr/\s+$NotReserved(?:\w|_|\$)[.\$_\w\d]*/;
$JL = $JavaLiteral;
$exception = qr/(?:\s+throws $JL)?/x;
$array = qr/(?:\s*\[\s*\])?/;
$typeParameters = qr/(?:\<$AngelBracketsN\>)?/;
$superClass = qr/(?:\s+extends$JL$typeParameters)?/;
$superInterface = qr/(?:\s+implements$JL$typeParameters(?:\s*,\s*$JL$typeParameters)*)?/;
undef $/;
while($name = shift @targets){
#Ищем повторяющиеся слова.
open my $in, "<:encoding(UTF-8)", $name or die "can't read file $name\n";
$text = <$in>;
close $in or die "can't close $name after read\n";

#Looking for class names
@classNames = ();
findClass($text, \@classNames);

my %constructor = ();
foreach $classname (@classNames){
while($text =~ m{((? $start=$1;
if($body =~ m/.*(super\s*\($BracketsN\)).*/gxs){
$constructor{ $start } = $body;

$ret="return null;";
$text =~ s/([.\$_\d\w]+$array$JL\s*\([^()]*\)$array$exception\s*\{)($BracesN)(\})/$1$ret$3/gx;

$ret="return false;";
$text =~ s{((?:^|\s+)boolean$JL\s*\([^()]*\)$exception\s*\{)($BracesN)(\})}{$1$ret$3}xg;

$text =~ s{((?:^|\s+)(?:void)$JL\s*\([^()]*\)$exception\s*\{)($BracesN)(\})}{$1$ret$3}xg;

$ret="return 0;";
$text =~ s{((?:^|\s+)(?:byte|short|int|long|float|double|char)$JL\s*\([^()]*\)$exception\s*\{)($BracesN)(\})}{$1$ret$3}xg;

while ( my ($key, $value) = each(%constructor) ) {
$key =~ s/\./\\\./gx;
$key =~ s/\(/\\\(/gx;
$key =~ s/\)/\\\)/gx;
$key =~ s/\{/\\\{/gx;
$key =~ s/\[/\\\[/gx;
$key =~ s/\]/\\\]/gx;
$key =~ s/\?/\\\?/gx;

$text =~ s/((? }

my $NotIntializedInStatic="";
@inStatic = ();

while($text =~ m/static\s*\{($BracesN)\}/gx){
$static = $1;

while($static =~ m/\s*([.\$_\w\d]+)\s*=\s*[.\$_\w\d]+(?=;|\s)/gx){
push @inStatic, $1;
if(scalar(@inStatic) > 0){
foreach $initialized (@inStatic){
$list .= $initialized . "|";
chop $list;
$NotIntializedInStatic = qr/(?!$list)/;

#Need to initialize uninitialized final variables
$text =~ s/(final(?:\s+(?:transient|static))* \s+ boolean \s+ $NotIntializedInStatic[.\$_\w\d]* \s*);/$1=false;/gx;
$text =~ s/(final(?:\s+(?:transient|static))* \s+ (?:byte|short|int|long|float|double|char) \s+ $NotIntializedInStatic[.\$_\w\d]* \s*);/$1=0;/gx;
$text =~ s/(final(?:\s+(?:transient|static))* \s+
$typeParameters $array \s+ $NotIntializedInStatic[.\$_\w\d]* \s*);/$1=null;/gx;

open my $out, '>:encoding(UTF-8)', $name or die "can't open file $name for write\n";
print $out $text;
close $out or die "couldn't write to $name\n";

sub findClass{
my ($text, $classNames) = @_;

#Java classes looks like:
#ClassModifiers_opt class Identifier TypeParameters_opt Super_opt Interfaces_opt
while($text =~ m/class($JL) $typeParameters $superClass $superInterface\s*\{($BracesN)\}/gx){
$classname = $1;
$classbody = $2;

$classname =~ s/\s//gx;

push @$classNames, $classname;

#looking for inner classes
findClass($classbody, \@$classNames);

Script gets path to folder with sources, reads all *.java files, edits them and saves back.

example of call:

script.pl /way_to_you_android_source/android_stubs_current_intermediates

Questions to myself for future investigation:
- How does initial stubs with throws generate from real code?
- How to automatically build android.jar in script?
- Which sources I need? Can I download them in script?
Tags: android, андройд
  • Post a new comment


    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded