am 48ffea93: am cf69b0c7: reconcile main tree with open-source eclair

Merge commit '48ffea93dff6b952b4ad5818b748dadd2dd7c19e'

* commit '48ffea93dff6b952b4ad5818b748dadd2dd7c19e':
  android-2.1_r1 snapshot
diff --git a/ANDROID-CHANGES.txt b/ANDROID-CHANGES.txt
index d4cade0..3fd8dba 100644
--- a/ANDROID-CHANGES.txt
+++ b/ANDROID-CHANGES.txt
@@ -5,4 +5,4 @@
 2) Remove reference to sun.misc.* in core/java14/com/vladium/util/IJREVersion.java
 3) Remove reference to sun.misc.* and SunJREExitHookManager class from core/java13/com/vladium/util/exit/ExitHookManager.java
 4) Add java.security.cert.Certificate cast to core/java12/com/vladium/emma/rt/InstrClassLoader.java to fix compiler error
-5) Copy /core/res/com/vladium/emma/rt/RTExitHook.closure into source tree so it does not have to be generated in the build
+5) Move out/core/res/com/vladium/emma/rt/RTExitHook.closure (from Emma ant build) into pregenerated/ so it does not have to be generated in Android's make-based build, but also doesn't break Emma's build.
diff --git a/Android.mk b/Android.mk
index a553921..1914af7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -7,13 +7,13 @@
 # ============================================================
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, core)
+LOCAL_SRC_FILES := $(call all-java-files-under, core pregenerated)
 
 LOCAL_MODULE := emma
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_RESOURCE_DIRS := core/res
+LOCAL_JAVA_RESOURCE_DIRS := core/res pregenerated/res
 
 LOCAL_NO_EMMA_INSTRUMENT := true
 
@@ -25,10 +25,10 @@
 # ============================================================
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, core)
+LOCAL_SRC_FILES := $(call all-java-files-under, core pregenerated)
 
 LOCAL_MODULE := emmalib
 
-LOCAL_JAVA_RESOURCE_DIRS := core/res
+LOCAL_JAVA_RESOURCE_DIRS := core/res pregenerated/res
 
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/ant/ant14/com/vladium/emma/emmajavaTask.java b/ant/ant14/com/vladium/emma/emmajavaTask.java
index cf4339a..929390c 100644
--- a/ant/ant14/com/vladium/emma/emmajavaTask.java
+++ b/ant/ant14/com/vladium/emma/emmajavaTask.java
@@ -19,6 +19,7 @@
 import com.vladium.emma.report.IReportEnums.DepthAttribute;
 import com.vladium.emma.report.IReportEnums.UnitsTypeAttribute;
 import com.vladium.emma.report.ReportCfg.Element_HTML;
+import com.vladium.emma.report.ReportCfg.Element_LCOV;
 import com.vladium.emma.report.ReportCfg.Element_TXT;
 import com.vladium.emma.report.ReportCfg.Element_XML;
 
@@ -471,6 +472,11 @@
         return m_reportCfg.createTxt ();
     }
     
+    public final Element_LCOV createLcov ()
+    {
+        return m_reportCfg.createLcov ();
+    }
+
     public final Element_HTML createHtml ()
     {
         return m_reportCfg.createHtml ();
@@ -584,4 +590,4 @@
     private Boolean m_outFileMerge;
 
 } // end of class
-// ----------------------------------------------------------------------------
\ No newline at end of file
+// ----------------------------------------------------------------------------
diff --git a/ant/ant14/com/vladium/emma/report/IReportEnums.java b/ant/ant14/com/vladium/emma/report/IReportEnums.java
index 7b81457..e1d2305 100644
--- a/ant/ant14/com/vladium/emma/report/IReportEnums.java
+++ b/ant/ant14/com/vladium/emma/report/IReportEnums.java
@@ -33,6 +33,7 @@
         {
             "txt",
             "html",
+            "lcov",
             "xml",
         };
 
@@ -93,4 +94,4 @@
     } // end of nested class
 
 } // end of interface
-// ----------------------------------------------------------------------------
\ No newline at end of file
+// ----------------------------------------------------------------------------
diff --git a/ant/ant14/com/vladium/emma/report/ReportCfg.java b/ant/ant14/com/vladium/emma/report/ReportCfg.java
index 71a01b4..2d8b73f 100644
--- a/ant/ant14/com/vladium/emma/report/ReportCfg.java
+++ b/ant/ant14/com/vladium/emma/report/ReportCfg.java
@@ -195,6 +195,24 @@
     
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     
+    public static class Element_LCOV extends Element
+    {
+        protected final String getType ()
+        {
+            return TYPE;
+        }
+
+        Element_LCOV (final Task task, final IProperties settings)
+        {
+            super (task, settings);
+        }
+
+        static final String TYPE = "lcov";
+
+    } // end of nested class
+
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
     public static class Element_XML extends Element
     {
         protected final String getType ()
@@ -308,6 +326,12 @@
                                                      new Element_TXT (m_task, m_settings));
     }
     
+    public Element_LCOV createLcov ()
+    {
+        return (Element_LCOV) addCfgElement (Element_LCOV.TYPE,
+                                                     new Element_LCOV (m_task, m_settings));
+    }
+
     public Element_HTML createHtml ()
     {
         return (Element_HTML) addCfgElement (Element_HTML.TYPE,
@@ -421,4 +445,4 @@
     private transient boolean m_processed;
 
 } // end of class
-// ----------------------------------------------------------------------------
\ No newline at end of file
+// ----------------------------------------------------------------------------
diff --git a/ant/ant14/com/vladium/emma/report/reportTask.java b/ant/ant14/com/vladium/emma/report/reportTask.java
index b726753..eb57873 100644
--- a/ant/ant14/com/vladium/emma/report/reportTask.java
+++ b/ant/ant14/com/vladium/emma/report/reportTask.java
@@ -16,6 +16,7 @@
 import com.vladium.emma.ant.FileTask;
 import com.vladium.emma.ant.SuppressableTask;
 import com.vladium.emma.report.ReportCfg.Element_HTML;
+import com.vladium.emma.report.ReportCfg.Element_LCOV;
 import com.vladium.emma.report.ReportCfg.Element_TXT;
 import com.vladium.emma.report.ReportCfg.Element_XML;
 
@@ -47,7 +48,7 @@
             
             if ((reportTypes == null) || (reportTypes.length == 0)) // no "txt" default for report processor
                 throw (BuildException) newBuildException (getTaskName ()
-                    + ": no report types specified: provide at least one of <txt>, <html>, <xml> nested elements", location).fillInStackTrace ();
+                    + ": no report types specified: provide at least one of <txt>, <html>, <lcov>, <xml> nested elements", location).fillInStackTrace ();
 
             String [] files = getDataPath (true);
             if ((files == null) || (files.length == 0))
@@ -104,6 +105,11 @@
         return m_reportCfg.createTxt ();
     }
     
+    public Element_LCOV createLcov ()
+    {
+        return m_reportCfg.createLcov ();
+    }
+
     public Element_HTML createHtml ()
     {
         return m_reportCfg.createHtml ();
@@ -176,4 +182,4 @@
     private ReportCfg m_reportCfg;   
 
 } // end of class
-// ----------------------------------------------------------------------------
\ No newline at end of file
+// ----------------------------------------------------------------------------
diff --git a/core/java12/com/vladium/emma/report/AbstractReportGenerator.java b/core/java12/com/vladium/emma/report/AbstractReportGenerator.java
index 6e37179..2a79c20 100644
--- a/core/java12/com/vladium/emma/report/AbstractReportGenerator.java
+++ b/core/java12/com/vladium/emma/report/AbstractReportGenerator.java
@@ -43,6 +43,8 @@
         
         if ("html".equals (type))
             return new com.vladium.emma.report.html.ReportGenerator ();
+        else if ("lcov".equals (type))
+            return new com.vladium.emma.report.lcov.ReportGenerator ();
         else if ("txt".equals (type))
             return new com.vladium.emma.report.txt.ReportGenerator ();
         else if ("xml".equals (type))
@@ -255,4 +257,4 @@
     private static final int MAX_DEBUG_INFO_WARNING_COUNT = 3; // per package
     
 } // end of class
-// ----------------------------------------------------------------------------
\ No newline at end of file
+// ----------------------------------------------------------------------------
diff --git a/core/java12/com/vladium/emma/report/lcov/ReportGenerator.java b/core/java12/com/vladium/emma/report/lcov/ReportGenerator.java
new file mode 100644
index 0000000..08a6310
--- /dev/null
+++ b/core/java12/com/vladium/emma/report/lcov/ReportGenerator.java
@@ -0,0 +1,413 @@
+/* Copyright 2009 Google Inc. All Rights Reserved.
+ * Derived from code Copyright (C) 2003 Vladimir Roubtsov.
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the Common Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * $Id$
+ */
+
+package com.vladium.emma.report.lcov;
+
+import com.vladium.emma.EMMARuntimeException;
+import com.vladium.emma.IAppErrorCodes;
+import com.vladium.emma.data.ClassDescriptor;
+import com.vladium.emma.data.ICoverageData;
+import com.vladium.emma.data.IMetaData;
+import com.vladium.emma.report.AbstractReportGenerator;
+import com.vladium.emma.report.AllItem;
+import com.vladium.emma.report.ClassItem;
+import com.vladium.emma.report.IItem;
+import com.vladium.emma.report.ItemComparator;
+import com.vladium.emma.report.MethodItem;
+import com.vladium.emma.report.PackageItem;
+import com.vladium.emma.report.SourcePathCache;
+import com.vladium.emma.report.SrcFileItem;
+import com.vladium.util.Descriptors;
+import com.vladium.util.Files;
+import com.vladium.util.IProperties;
+import com.vladium.util.IntObjectMap;
+import com.vladium.util.asserts.$assert;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+/**
+ * @author Vlad Roubtsov, (C) 2003
+ * @author Tim Baverstock, (C) 2009
+ *
+ * Generates LCOV format files:
+ *    http://manpages.ubuntu.com/manpages/karmic/man1/geninfo.1.html
+ */
+public final class ReportGenerator extends AbstractReportGenerator
+                                   implements IAppErrorCodes
+{
+    public String getType()
+    {
+        return TYPE;
+    }
+
+    /**
+     * Queue-based visitor, starts with the root, node visits enqueue child
+     * nodes.
+     */
+    public void process(final IMetaData mdata,
+                        final ICoverageData cdata,
+                        final SourcePathCache cache,
+                        final IProperties properties)
+        throws EMMARuntimeException
+    {
+        initialize(mdata, cdata, cache, properties);
+
+        long start = 0;
+        long end;
+        final boolean trace1 = m_log.atTRACE1();
+
+        if (trace1)
+        {
+            start = System.currentTimeMillis();
+        }
+
+        m_queue = new LinkedList();
+        for (m_queue.add(m_view.getRoot()); !m_queue.isEmpty(); )
+        {
+            final IItem head = (IItem) m_queue.removeFirst();
+            head.accept(this, null);
+        }
+        close();
+
+        if (trace1)
+        {
+            end = System.currentTimeMillis();
+            m_log.trace1("process", "[" + getType() + "] report generated in "
+                         + (end - start) + " ms");
+        }
+    }
+
+    public void cleanup()
+    {
+        m_queue = null;
+        close();
+        super.cleanup();
+    }
+
+
+    /**
+    * Visitor for top-level node; opens output file, enqueues packages.
+    */
+    public Object visit(final AllItem item, final Object ctx)
+    {
+        File outFile = m_settings.getOutFile();
+        if (outFile == null)
+        {
+            outFile = new File("coverage.lcov");
+            m_settings.setOutFile(outFile);
+        }
+
+        final File fullOutFile = Files.newFile(m_settings.getOutDir(), outFile);
+
+        m_log.info("writing [" + getType() + "] report to ["
+                   + fullOutFile.getAbsolutePath() + "] ...");
+
+        openOutFile(fullOutFile, m_settings.getOutEncoding(), true);
+
+        // Enqueue packages
+        final ItemComparator order =
+                m_typeSortComparators[PackageItem.getTypeMetadata().getTypeID()];
+        for (Iterator packages = item.getChildren(order); packages.hasNext(); )
+        {
+            final IItem pkg = (IItem) packages.next();
+            m_queue.addLast(pkg);
+        }
+
+        return ctx;
+    }
+
+    /**
+     * Visitor for packages; enqueues source files contained by the package.
+     */
+    public Object visit(final PackageItem item, final Object ctx)
+    {
+        if (m_verbose)
+        {
+            m_log.verbose("  report: processing package [" + item.getName() + "] ...");
+        }
+
+        // Enqueue source files
+        int id = m_srcView
+                 ? SrcFileItem.getTypeMetadata().getTypeID()
+                 : ClassItem.getTypeMetadata().getTypeID();
+        final ItemComparator order = m_typeSortComparators[id];
+        for (Iterator srcORclsFiles = item.getChildren(order);
+             srcORclsFiles.hasNext();
+            )
+        {
+            final IItem srcORcls = (IItem) srcORclsFiles.next();
+            m_queue.addLast(srcORcls);
+        }
+
+        return ctx;
+    }
+
+    /**
+     * Visitor for source files: doesn't use the enqueue mechanism to examine
+     * deeper nodes because it writes the 'end_of_record' decoration here.
+     */
+    public Object visit (final SrcFileItem item, final Object ctx)
+    {
+        row("SF:".concat(item.getFullVMName()));
+
+        // TODO: Enqueue ClassItems, then an 'end_of_record' object
+
+        emitFileCoverage(item);
+
+        row("end_of_record");
+        return ctx;
+    }
+
+    /** Issue a coverage report for all lines in the file, and for each
+     * function in the file.
+     */
+    private void emitFileCoverage(final SrcFileItem item)
+    {
+        if ($assert.ENABLED)
+        {
+            $assert.ASSERT(item != null, "null input: item");
+        }
+
+        final String fileName = item.getFullVMName();
+
+        final String packageVMName = ((PackageItem) item.getParent()).getVMName();
+
+        if (!m_hasLineNumberInfo)
+        {
+            m_log.info("source file '"
+                       + Descriptors.combineVMName(packageVMName, fileName)
+                       + "' has no line number information");
+        }
+        boolean success = false;
+
+        try
+        {
+            // For each class in the file, for each method in the class,
+            // examine the execution blocks in the method until one with
+            // coverage is found. Report coverage or non-coverage on the
+            // strength of that one block (much as for now, a line is 'covered'
+            // if it's partially covered).
+
+            // TODO: Intertwingle method records and line records
+
+            {
+                final ItemComparator order = m_typeSortComparators[
+                        ClassItem.getTypeMetadata().getTypeID()];
+                int clsIndex = 0;
+                for (Iterator classes = item.getChildren(order);
+                     classes.hasNext();
+                     ++clsIndex)
+                {
+                    final ClassItem cls = (ClassItem) classes.next();
+
+                    final String className = cls.getName();
+
+                    ClassDescriptor cdesc = cls.getClassDescriptor();
+
+                    // [methodid][blocksinmethod]
+                    boolean[][] ccoverage = cls.getCoverage();
+
+                    final ItemComparator order2 = m_typeSortComparators[
+                            MethodItem.getTypeMetadata().getTypeID()];
+                    for (Iterator methods = cls.getChildren(order2); methods.hasNext(); )
+                    {
+                        final MethodItem method = (MethodItem) methods.next();
+                        String mname = method.getName();
+                        final int methodID = method.getID();
+
+                        boolean covered = false;
+                        if (ccoverage != null)
+                        {
+                            if ($assert.ENABLED)
+                            {
+                                $assert.ASSERT(ccoverage.length > methodID, "index bounds");
+                                $assert.ASSERT(ccoverage[methodID] != null, "null: coverage");
+                                $assert.ASSERT(ccoverage[methodID].length > 0, "empty array");
+                            }
+                            covered = ccoverage[methodID][0];
+                        }
+
+                        row("FN:" + method.getFirstLine() + "," + className + "::" + mname);
+                        row("FNDA:" + (covered ? 1 : 0) + "," + className + "::" + mname);
+                    }
+                }
+            }
+
+            // For each line in the file, emit a DA.
+
+            {
+                final int unitsType = m_settings.getUnitsType();
+                // line num:int -> SrcFileItem.LineCoverageData
+                IntObjectMap lineCoverageMap = null;
+                int[] lineCoverageKeys = null;
+
+                lineCoverageMap = item.getLineCoverage();
+                $assert.ASSERT(lineCoverageMap != null, "null: lineCoverageMap");
+                lineCoverageKeys = lineCoverageMap.keys();
+                java.util.Arrays.sort(lineCoverageKeys);
+
+                for (int i = 0; i < lineCoverageKeys.length; ++i)
+                {
+                    int l = lineCoverageKeys[i];
+                    final SrcFileItem.LineCoverageData lCoverageData =
+                            (SrcFileItem.LineCoverageData) lineCoverageMap.get(l);
+
+                    if ($assert.ENABLED)
+                    {
+                        $assert.ASSERT(lCoverageData != null, "lCoverage is null");
+                    }
+                    switch (lCoverageData.m_coverageStatus)
+                    {
+                        case SrcFileItem.LineCoverageData.LINE_COVERAGE_ZERO:
+                            row("DA:" + l + ",0");
+                            break;
+
+                        case SrcFileItem.LineCoverageData.LINE_COVERAGE_PARTIAL:
+                            // TODO: Add partial coverage support to LCOV
+                            row("DA:" + l + ",1");
+                            break;
+
+                        case SrcFileItem.LineCoverageData.LINE_COVERAGE_COMPLETE:
+                            row("DA:" + l + ",1");
+                            break;
+
+                        default:
+                            $assert.ASSERT(false, "invalid line coverage status: "
+                                           + lCoverageData.m_coverageStatus);
+
+                    } // end of switch
+                }
+            }
+
+            success = true;
+        }
+        catch (Throwable t)
+        {
+            t.printStackTrace(System.out);
+            success = false;
+        }
+
+        if (!success)
+        {
+            m_log.info("[source file '"
+                       + Descriptors.combineVMName(packageVMName, fileName)
+                       + "' not found in sourcepath]");
+        }
+    }
+
+    public Object visit (final ClassItem item, final Object ctx)
+    {
+        return ctx;
+    }
+
+    private void row(final StringBuffer str)
+    {
+        if ($assert.ENABLED)
+        {
+            $assert.ASSERT(str != null, "str = null");
+        }
+
+        try
+        {
+            m_out.write(str.toString());
+            m_out.newLine();
+        }
+        catch (IOException ioe)
+        {
+            throw new EMMARuntimeException(IAppErrorCodes.REPORT_IO_FAILURE, ioe);
+        }
+    }
+
+    private void row(final String str)
+    {
+        if ($assert.ENABLED)
+        {
+            $assert.ASSERT(str != null, "str = null");
+        }
+
+        try
+        {
+            m_out.write(str);
+            m_out.newLine();
+        }
+        catch (IOException ioe)
+        {
+            throw new EMMARuntimeException(IAppErrorCodes.REPORT_IO_FAILURE, ioe);
+        }
+    }
+
+    private void close()
+    {
+        if (m_out != null)
+        {
+            try
+            {
+                m_out.flush();
+                m_out.close();
+            }
+            catch (IOException ioe)
+            {
+                throw new EMMARuntimeException(IAppErrorCodes.REPORT_IO_FAILURE, ioe);
+            }
+            finally
+            {
+                m_out = null;
+            }
+        }
+    }
+
+    private void openOutFile(final File file, final String encoding, final boolean mkdirs)
+    {
+        try
+        {
+            if (mkdirs)
+            {
+                final File parent = file.getParentFile();
+                if (parent != null)
+                {
+                    parent.mkdirs();
+                }
+            }
+            file.delete();
+            if (file.exists())
+            {
+                throw new EMMARuntimeException("Failed to delete " + file);
+            }
+            m_out = new BufferedWriter(
+                    new OutputStreamWriter(new FileOutputStream(file), encoding),
+                    IO_BUF_SIZE);
+        }
+        catch (UnsupportedEncodingException uee)
+        {
+            throw new EMMARuntimeException(uee);
+        }
+        catch (IOException fnfe) // FileNotFoundException
+        {
+            // note: in J2SDK 1.3 FileOutputStream constructor's throws clause
+            // was narrowed to FileNotFoundException:
+            throw new EMMARuntimeException(fnfe);
+        }
+    }
+
+    private LinkedList /* IITem */ m_queue;
+    private BufferedWriter m_out;
+
+    private static final String TYPE = "lcov";
+
+    private static final int IO_BUF_SIZE = 32 * 1024;
+}
+
diff --git a/core/res/com/vladium/emma/report/report_usage.res b/core/res/com/vladium/emma/report/report_usage.res
index f91c241..efb56e1 100644
--- a/core/res/com/vladium/emma/report/report_usage.res
+++ b/core/res/com/vladium/emma/report/report_usage.res
@@ -6,7 +6,7 @@
 
 'r', 'report':
 	required, mergeable, values: 1,
-	'<list of {txt|html|xml}>',
+	'<list of {txt|html|lcov|xml}>',
 	"coverage report type list";
 
 'sp', 'sourcepath':
diff --git a/core/res/com/vladium/emma/run_usage.res b/core/res/com/vladium/emma/run_usage.res
index 42d6f7c..c04c430 100644
--- a/core/res/com/vladium/emma/run_usage.res
+++ b/core/res/com/vladium/emma/run_usage.res
@@ -21,7 +21,7 @@
 
 'r', 'report':
 	optional, mergeable, values: 1,
-	'<list of {txt|html|xml}>',
+	'<list of {txt|html|lcov|xml}>',
 	"coverage report type list";
 
 'sp', 'sourcepath':
diff --git a/pregenerated/ANDROID-CHANGES.txt b/pregenerated/ANDROID-CHANGES.txt
new file mode 100644
index 0000000..d4664a5
--- /dev/null
+++ b/pregenerated/ANDROID-CHANGES.txt
@@ -0,0 +1,8 @@
+This directory exists to hold the file
+pregenerated/res/com/vladium/emma/rt/RTExitHook.closure, which is normally
+deposited in the out/ directory during a standard Emma Ant build, but the
+Android make-based build doesn't accommodate source-generation.
+
+This is a hack, but it's convenient to keep Emma's own build system working
+alongside Android's make-basedsystem.
+
diff --git a/core/res/com/vladium/emma/rt/RTExitHook.closure b/pregenerated/res/com/vladium/emma/rt/RTExitHook.closure
similarity index 100%
rename from core/res/com/vladium/emma/rt/RTExitHook.closure
rename to pregenerated/res/com/vladium/emma/rt/RTExitHook.closure
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..73b91cd
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,136 @@
+#!/bin/bash
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+# Author: weasel@google.com (Tim Baverstock)
+#
+# This program and the accompanying materials are made available under
+# the terms of the Common Public License v1.0 which accompanies this
+# distribution, and is available at http://www.eclipse.org/legal/cpl-v10.html
+#
+# This script tests the emma jar from the sources in this directory.
+# This script has to be run from its current directory ONLY.
+# Sample usages:
+# To just test emma.jar:
+# ./test.sh
+
+TESTDIR=/tmp/test-emma/$$
+JAVADIR=$TESTDIR/android3/java
+SOURCEDIR=$JAVADIR/com/android/bunnies
+mkdir -p $SOURCEDIR
+
+cat <<END >$SOURCEDIR/Bunny.java
+package com.android.bunnies;
+
+import java.util.Random;
+
+public class Bunny {
+  int randomNumber1 = (new Random()).nextInt();
+
+  int randomNumber2;
+
+  {
+    Random r = new Random();
+    randomNumber2 = r.nextInt();
+  }
+
+  int addOne(int a) {
+    int b = a + 1;
+    return identity(a + 1)
+            ? 1
+            : 0;
+  }
+
+  int dontAddOne(int a) {
+    return a;
+  }
+
+  boolean identity(int a) {
+    return a != a;
+  }
+
+  public static void main(String[] args) {
+    Bunny thisThing = new Bunny();
+    SubBunny thatThing = new SubBunny();
+    System.out.println(thisThing.addOne(2));
+    System.out.println(thatThing.addOne(2));
+  }
+}
+END
+cat <<END >$SOURCEDIR/SubBunny.java
+package com.android.bunnies;
+import com.android.bunnies.Bunny;
+class SubBunny extends Bunny {
+  int addOne(int a) {
+    int b = a + 2;
+    return identity(a) && identity(b) || identity(b)
+            ? 1
+            : 0;
+  }
+
+  boolean identity(int a) {
+    return a == a;
+  }
+}
+END
+
+GOLDEN=$TESTDIR/golden.lcov
+cat <<END >$GOLDEN
+SF:com/android/bunnies/SubBunny.java
+FN:5,SubBunny::addOne (int): int
+FNDA:1,SubBunny::addOne (int): int
+FN:12,SubBunny::identity (int): boolean
+FNDA:1,SubBunny::identity (int): boolean
+FN:3,SubBunny::SubBunny (): void
+FNDA:1,SubBunny::SubBunny (): void
+DA:3,1
+DA:5,1
+DA:6,1
+DA:12,1
+end_of_record
+SF:com/android/bunnies/Bunny.java
+FN:23,Bunny::dontAddOne (int): int
+FNDA:0,Bunny::dontAddOne (int): int
+FN:27,Bunny::identity (int): boolean
+FNDA:1,Bunny::identity (int): boolean
+FN:16,Bunny::addOne (int): int
+FNDA:1,Bunny::addOne (int): int
+FN:5,Bunny::Bunny (): void
+FNDA:1,Bunny::Bunny (): void
+FN:31,Bunny::main (String []): void
+FNDA:1,Bunny::main (String []): void
+DA:5,1
+DA:6,1
+DA:11,1
+DA:12,1
+DA:13,1
+DA:16,1
+DA:17,1
+DA:23,0
+DA:27,1
+DA:31,1
+DA:32,1
+DA:33,1
+DA:34,1
+DA:35,1
+end_of_record
+END
+
+javac -g $(find $SOURCEDIR -name \*.java)
+
+COVERAGE=$TESTDIR/coverage.dat
+java -cp dist/emma.jar emmarun -r lcov -cp $JAVADIR \
+     -sp $JAVADIR -Dreport.lcov.out.file=$COVERAGE com.android.bunnies.Bunny
+
+# Don't really need to test these separately, but it's useful to me for now.
+
+if ! diff <(sort $GOLDEN) <(sort $COVERAGE) >$TESTDIR/diff-sorted; then
+  echo Tests failed: Additional or missing lines: See $TESTDIR/diff-sorted
+  exit
+fi
+if ! diff $GOLDEN $COVERAGE >$TESTDIR/diff-ordered; then
+  echo Tests failed: same lines, different order: See $TESTDIR/diff-ordered
+  exit
+fi
+rm -rf $TESTDIR
+echo Tests passed.
+