/hg/MauveTestCoverage: 2012-01-17 Pavel Tisnovsky <ptisnovs at re...
ptisnovs at icedtea.classpath.org
ptisnovs at icedtea.classpath.org
Tue Jan 17 04:59:43 PST 2012
changeset 5ef74c026b81 in /hg/MauveTestCoverage
details: http://icedtea.classpath.org/hg/MauveTestCoverage?cmd=changeset;node=5ef74c026b81
author: Pavel Tisnovsky <ptisnovs at redhat.com>
date: Tue Jan 17 14:02:07 2012 +0100
2012-01-17 Pavel Tisnovsky <ptisnovs at redhat.com>
* src/FileUtils.java: Added new static methods
used by other classes.
* src/PrintTestCoverage.java: Added check if the .class file
is really Mauve test. More refactoring and Javadoc.
diffstat:
ChangeLog | 8 +
src/FileUtils.java | 64 +++++++
src/PrintTestCoverage.java | 369 ++++++++++++++++++++++++++++++++++++--------
3 files changed, 375 insertions(+), 66 deletions(-)
diffs (truncated from 689 to 500 lines):
diff -r 730c5549c0f9 -r 5ef74c026b81 ChangeLog
--- a/ChangeLog Tue Jan 10 14:08:47 2012 +0100
+++ b/ChangeLog Tue Jan 17 14:02:07 2012 +0100
@@ -1,3 +1,11 @@
+2012-01-17 Pavel Tisnovsky <ptisnovs at redhat.com>
+
+ * src/FileUtils.java:
+ Added new static methods used by other classes.
+ * src/PrintTestCoverage.java:
+ Added check if the .class file is really Mauve test.
+ More refactoring and Javadoc.
+
2012-01-10 Pavel Tisnovsky <ptisnovs at redhat.com>
* src/FileUtils.java:
diff -r 730c5549c0f9 -r 5ef74c026b81 src/FileUtils.java
--- a/src/FileUtils.java Tue Jan 10 14:08:47 2012 +0100
+++ b/src/FileUtils.java Tue Jan 17 14:02:07 2012 +0100
@@ -38,7 +38,9 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
+import java.io.EOFException;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
@@ -54,6 +56,11 @@
class FileUtils
{
/**
+ * EOF constant used as special return value in some methods.
+ */
+ private static final int EOF = -1;
+
+ /**
* Read content of given text file and return it as list of strings. No
* exception is thrown during reading.
*
@@ -222,6 +229,7 @@
*/
private static void writeAllLinesToTextFile(BufferedWriter bufferedWriter, List<String> lines) throws IOException
{
+ // for all lines
for (String line : lines)
{
bufferedWriter.write(line);
@@ -230,4 +238,60 @@
}
}
+ /**
+ * Read one byte from given file input stream with check if EOF is
+ * reached.
+ *
+ * @param fileInputStream instance of already opened FileInputStream
+ * @return
+ * @throws IOException
+ * thrown if an I/O error occurs
+ */
+ static int readOneByte(FileInputStream fileInputStream) throws IOException
+ {
+ int i = fileInputStream.read();
+ // -1 means that EOF is reached
+ if (i == EOF)
+ {
+ throw new EOFException("End of file reached!");
+ }
+ return i;
+ }
+
+ /**
+ * Read two bytes from given file input stream with check if EOF is
+ * reached.
+ *
+ * @param fileInputStream instance of already opened FileInputStream
+ * @return
+ * @throws IOException
+ * thrown if an I/O error occurs
+ */
+ static int readTwoBytes(FileInputStream fileInputStream) throws IOException
+ {
+ int i1 = readOneByte(fileInputStream);
+ int i2 = readOneByte(fileInputStream);
+ // combine all two read bytes into a word
+ return (i1 << 8) | (i2);
+ }
+
+ /**
+ * Read four bytes from given file input stream with check if EOF is
+ * reached.
+ *
+ * @param fileInputStream instance of already opened FileInputStream
+ * @return
+ * @throws IOException
+ * thrown if an I/O error occurs
+ */
+ static int readFourBytes(FileInputStream fileInputStream) throws IOException
+ {
+ int i1 = readOneByte(fileInputStream);
+ int i2 = readOneByte(fileInputStream);
+ int i3 = readOneByte(fileInputStream);
+ int i4 = readOneByte(fileInputStream);
+ // combine all four read bytes into a word
+ return (i1 << 24) | (i2 << 16) | (i3 << 8) | (i4);
+ }
+
}
diff -r 730c5549c0f9 -r 5ef74c026b81 src/PrintTestCoverage.java
--- a/src/PrintTestCoverage.java Tue Jan 10 14:08:47 2012 +0100
+++ b/src/PrintTestCoverage.java Tue Jan 17 14:02:07 2012 +0100
@@ -47,6 +47,14 @@
import java.util.Set;
import java.util.TreeSet;
+
+
+/**
+ * This enumeration represents all record types which could be stored in a
+ * constant pool.
+ *
+ * @author Pavel Tisnovsky
+ */
enum ConstantPoolTag
{
CONSTANT_Class(7),
@@ -61,18 +69,38 @@
CONSTANT_NameAndType(12),
CONSTANT_Utf8(1);
+ /**
+ * Value represented one enumeration item.
+ */
private int value;
+ /**
+ * Constructor
+ *
+ * @param value
+ * enumeration value
+ */
private ConstantPoolTag(int value)
{
this.value = value;
}
+ /**
+ * Getter for a field value.
+ *
+ * @return value of a field named value
+ */
public int getValue()
{
return this.value;
}
+ /**
+ * Converts integer value to a enumeration item.
+ *
+ * @param value integer value
+ * @return selected enumeration item or null
+ */
public static ConstantPoolTag intToConstantPoolTag(int value)
{
for (ConstantPoolTag val : values())
@@ -86,27 +114,65 @@
}
}
+/**
+ * Abstract class which represents any constant pool record.
+ *
+ * @author Pavel Tisnovsky
+ */
abstract class ConstantPoolRecord
{
+ /**
+ * Tag associated with each constant pool record.
+ */
private ConstantPoolTag tag;
+ /**
+ * Constructor common for all subclasses.
+ *
+ * @param tag
+ * tag associated with each constant pool record.
+ */
public ConstantPoolRecord(ConstantPoolTag tag)
{
this.tag = tag;
}
+ /**
+ * Get tag associated with each constant pool record.
+ *
+ * @return value of the field 'tag'
+ */
public ConstantPoolTag getTag()
{
return this.tag;
}
+ /**
+ * Set tag associated with each constant pool record.
+ *
+ * @param tag
+ * new value of the field 'tag'
+ */
public void setTag(ConstantPoolTag tag)
{
this.tag = tag;
}
+ /**
+ * Returns true if this record occupies two constant pool entries. Only such
+ * entries storing long and double values need to return true.
+ *
+ * @return true if this record occupies two constant pool entries
+ */
abstract boolean isRecordOccupyingTwoPoolEntries();
+ /**
+ * Converts constant pool entry into a printable string.
+ *
+ * @param poolEntries
+ * whole constant pool
+ * @return constant pool entry represented as a printable string
+ */
abstract public String toString(ConstantPoolRecord[] poolEntries);
}
@@ -599,42 +665,26 @@
}
}
+
+
/**
- *
+ * This class is used to find out, which API methods are called from given
+ * tests. Bytecode is investigated: this means this tool is based on static
+ * coverage checking.
+ *
* @author Pavel Tisnovsky
*/
public class PrintTestCoverage
{
/**
- * Name of file containing directory to tests.
+ * Default name of file containing directory to tests.
*/
private static final String TEST_DIRECTORY = "test_directory.txt";
- private static int readByte(FileInputStream fin) throws IOException
- {
- return fin.read();
- }
-
- private static int readTwoBytes(FileInputStream fin) throws IOException
- {
- int i1 = readByte(fin);
- int i2 = readByte(fin);
- return (i1 << 8) | (i2);
- }
-
- private static int readFourBytes(FileInputStream fin) throws IOException
- {
- int i1 = readByte(fin);
- int i2 = readByte(fin);
- int i3 = readByte(fin);
- int i4 = readByte(fin);
- return (i1 << 24) | (i2 << 16) | (i3 << 8) | (i4);
- }
-
@SuppressWarnings("boxing")
private static void processMagicNumber(FileInputStream fin) throws Exception
{
- int magic = readFourBytes(fin);
+ int magic = FileUtils.readFourBytes(fin);
System.err.format("Magic constant: 0x%x\n", magic);
if (magic != 0xcafebabe)
{
@@ -645,80 +695,80 @@
@SuppressWarnings("boxing")
private static void processClassVersion(FileInputStream fin) throws IOException
{
- int minorVersion = readTwoBytes(fin);
- int majorVersion = readTwoBytes(fin);
+ int minorVersion = FileUtils.readTwoBytes(fin);
+ int majorVersion = FileUtils.readTwoBytes(fin);
System.err.format("Major version: %d\n", majorVersion);
System.err.format("Minor version: %d\n", minorVersion);
}
private static ConstantPoolRecord readConstantPoolEntry(FileInputStream fin) throws IOException
{
- ConstantPoolTag tag = ConstantPoolTag.intToConstantPoolTag(readByte(fin));
+ ConstantPoolTag tag = ConstantPoolTag.intToConstantPoolTag(FileUtils.readOneByte(fin));
switch (tag)
{
case CONSTANT_Class:
{
- int classNameIndex = readTwoBytes(fin);
+ int classNameIndex = FileUtils.readTwoBytes(fin);
return new ClassRecord(tag, classNameIndex);
}
case CONSTANT_Fieldref:
{
- int classIndex = readTwoBytes(fin);
- int nameTypeIndex = readTwoBytes(fin);
+ int classIndex = FileUtils.readTwoBytes(fin);
+ int nameTypeIndex = FileUtils.readTwoBytes(fin);
return new FieldReferenceRecord(tag, classIndex, nameTypeIndex);
}
case CONSTANT_InterfaceMethodref:
{
- int classIndex = readTwoBytes(fin);
- int nameTypeIndex = readTwoBytes(fin);
+ int classIndex = FileUtils.readTwoBytes(fin);
+ int nameTypeIndex = FileUtils.readTwoBytes(fin);
return new InterfaceReferenceRecord(tag, classIndex, nameTypeIndex);
}
case CONSTANT_Methodref:
{
- int classIndex = readTwoBytes(fin);
- int nameTypeIndex = readTwoBytes(fin);
+ int classIndex = FileUtils.readTwoBytes(fin);
+ int nameTypeIndex = FileUtils.readTwoBytes(fin);
return new MethodReferenceRecord(tag, classIndex, nameTypeIndex);
}
case CONSTANT_NameAndType:
{
- int nameIndex = readTwoBytes(fin);
- int descriptorIndex = readTwoBytes(fin);
+ int nameIndex = FileUtils.readTwoBytes(fin);
+ int descriptorIndex = FileUtils.readTwoBytes(fin);
return new NameAndTypeRecord(tag, nameIndex, descriptorIndex);
}
case CONSTANT_String:
{
- int stringIndex = readTwoBytes(fin);
+ int stringIndex = FileUtils.readTwoBytes(fin);
return new StringRecord(tag, stringIndex);
}
case CONSTANT_Integer:
{
- int constant = readFourBytes(fin);
+ int constant = FileUtils.readFourBytes(fin);
return new IntegerRecord(tag, constant);
}
case CONSTANT_Long:
{
- int high = readFourBytes(fin);
- int low = readFourBytes(fin);
+ int high = FileUtils.readFourBytes(fin);
+ int low = FileUtils.readFourBytes(fin);
return new LongRecord(tag, high, low);
}
case CONSTANT_Float:
{
- int bits = readFourBytes(fin);
+ int bits = FileUtils.readFourBytes(fin);
return new FloatRecord(tag, bits);
}
case CONSTANT_Double:
{
- int highbits = readFourBytes(fin);
- int lowbits = readFourBytes(fin);
+ int highbits = FileUtils.readFourBytes(fin);
+ int lowbits = FileUtils.readFourBytes(fin);
return new DoubleRecord(tag, highbits, lowbits);
}
case CONSTANT_Utf8:
{
- int length = readTwoBytes(fin);
+ int length = FileUtils.readTwoBytes(fin);
StringBuffer buf = new StringBuffer(length);
for (int i = 0; i < length; i++)
{
- buf.append((char)readByte(fin));
+ buf.append((char)FileUtils.readOneByte(fin));
}
return new Utf8Record(tag, buf.toString());
}
@@ -733,7 +783,7 @@
{
ConstantPoolRecord[] poolEntries = null;
- int constantPoolCount = readTwoBytes(fin) - 1;
+ int constantPoolCount = FileUtils.readTwoBytes(fin) - 1;
System.err.format("\nConstant pool size: %d\n", constantPoolCount);
poolEntries = new ConstantPoolRecord[constantPoolCount];
@@ -750,6 +800,65 @@
return poolEntries;
}
+ /**
+ * Read and returns flags set for the class
+ *
+ * @param fin
+ * input stream for the bytecode
+ * @param poolEntries
+ * constant pool entries
+ * @return flags set for the class
+ * @throws IOException
+ */
+ private static int processClassFlags(FileInputStream fin, ConstantPoolRecord[] poolEntries) throws IOException
+ {
+ // read flags set for the class
+ int flags = FileUtils.readTwoBytes(fin);
+ return flags;
+ }
+
+ /**
+ * @param fin
+ * @param poolEntries
+ * @return
+ * @throws IOException
+ */
+ private static String readClassOrSuperclassOrInterfaceName(FileInputStream fin, ConstantPoolRecord[] poolEntries)
+ throws IOException
+ {
+ // read index into constant pool
+ int index = FileUtils.readTwoBytes(fin);
+ // constant pool array is indexed from zero, but in bytecode, one-based indexing is used
+ index--;
+ ConstantPoolRecord poolRecord = poolEntries[index];
+ if (poolRecord instanceof ClassRecord)
+ {
+ return ((ClassRecord)poolRecord).getClassName(poolEntries);
+ }
+ return null;
+ }
+
+ private static String processClassName(FileInputStream fin, ConstantPoolRecord[] poolEntries) throws IOException
+ {
+ return readClassOrSuperclassOrInterfaceName(fin, poolEntries);
+ }
+
+ private static String processSuperclassName(FileInputStream fin, ConstantPoolRecord[] poolEntries) throws IOException
+ {
+ return readClassOrSuperclassOrInterfaceName(fin, poolEntries);
+ }
+
+ private static Set<String> processAllImplementedInterfaces(FileInputStream fin, ConstantPoolRecord[] poolEntries) throws IOException
+ {
+ int interfacesCount = FileUtils.readTwoBytes(fin);
+ Set<String> interfaceNames = new TreeSet<String>();
+ for (int i = 0; i < interfacesCount; i++)
+ {
+ interfaceNames.add(readClassOrSuperclassOrInterfaceName(fin, poolEntries));
+ }
+ return interfaceNames;
+ }
+
@SuppressWarnings("unused")
private static void printConstantPool(ConstantPoolRecord[] poolEntries)
{
@@ -762,7 +871,7 @@
}
}
- private static void printMethodList(String testedClassName, ConstantPoolRecord[] poolEntries, Set<String> methodSet)
+ private static void determineAllCalledAPIMethods(String testedClassName, ConstantPoolRecord[] poolEntries, Set<String> methodSet)
{
for (ConstantPoolRecord record : poolEntries)
{
@@ -777,7 +886,7 @@
}
}
- private static void printTestCoverageForOneTestClass(String testedClassName, File directory, String fileName, Set<String> methodSet)
+ private static void determineTestCoverageForOneTestClass(String testedClassName, File directory, String fileName, Set<String> methodSet)
{
FileInputStream fin = null;
try
@@ -785,11 +894,7 @@
fin = new FileInputStream(new File(directory, fileName));
try
{
- processMagicNumber(fin);
- processClassVersion(fin);
- ConstantPoolRecord[] poolEntries = processContantPool(fin);
- //printConstantPool(poolEntries);
- printMethodList(testedClassName, poolEntries, methodSet);
+ processAllRelevantBytecodeParts(testedClassName, methodSet, fin);
}
catch (Exception e)
{
@@ -813,45 +918,176 @@
}
}
+ /**
+ * Read all relevant parts of bytecode from a .class file.
+ *
+ * @param testedClassName
+ * tested class name.
More information about the distro-pkg-dev
mailing list