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

Creation Android.jar for Unit tests

Post about android. So on english.

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


Solution:
Stephen Ng wrote good article with partial solution: "Manually remove throw statements in needed classes".
sites.google.com/site/androiddevtesting/

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:
/way_to_you_android_source/out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates

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.

disadvantages:
- 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
disadvantages:
- 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

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


fourth try:
- initialize uninitialized fields only

This is it.


Totally
- 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";
exit;
}
push @targetdirs,$parameter;
}


while($dirname = shift @targetdirs){
opendir $dir, $dirname;
while( $file = readdir $dir){
$full=$dirname."/".$file;
if (-d $full and $file ne "." and $file ne ".."){
push @targetdirs,$full;
next;
}
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;
$body=$2;
$super="";
if($body =~ m/.*(super\s*\($BracketsN\)).*/gxs){
$super=$1.";";
}
$body=$super;
$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;

$ret="return;";
$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){
$list="";
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+
(?!boolean\s+|byte\s+|short\s+|int\s+|long\s+|float\s+|double\s+|char\s+)[.\$_\d\w]+
$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

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments