Coberturaの原理


Coberturaの原理
Coberturaは基本的なコードを検出し、テストパッケージの実行時にどのコードが実行されているかを確認し、どのコードが実行されていないかを測定するオープンソースツールです.
公式アドレス:http://sourceforge.net/projects/cobertura/  日本のダウンロード:http://sourceforge.jp/projects/sfnet_cobertura/releases/
CoberturaはMavenコンパイルプラットフォームに対応するcobertura-maven-pluginプラグインがあり、コードのコンパイル、検出、集積などの各周期が流れ線式に自動化できます.
プラグインのアドレス:http://mojo.codehaus.org/cobertura-maven-plugin/
Coberturaの原理と過程
cobertura実行過程は大体以下の通りです.  1,instrumentを使って、コンパイルしたクラスファイルを修正します.  2,テストを実行して、テストデータをxx.serに出力します.  3,reportを用いてカバーレート報告を生成する.
1,instruument
coberturaはinstrumentを使って私達がコンパイルしたクラスファイルを修正して、コードの中にcoberturaの統計コードを入れます.そして、一つの.serファイル(上書きデータの出力用)を生成します.instrumentを使用して実行される過程で、CoberturaInstrumenterは、まず分析モニターを呼び出し、所与のコンパイルされたclassを分析し、touch Point(ソースコードのカバーされている行に対応すると考えられる)と必要な他の情報を得る.その後、インジェクションモニタを呼び出して、新しいclassに情報を注入し、\target\generated-classesディレクトリに保存する.
デモのソースコードは以下の通りです.
public class FeeCodeTranslate {
    private static final Map<String, String> FEECODEMAP = new HashMap<String, String>();
    static{
        FeeCodeTranslate.FEECODEMAP.put("      ", "JJFY");
        FeeCodeTranslate.FEECODEMAP.put("      ", "DKFY");
        FeeCodeTranslate.FEECODEMAP.put("      ", "CSFY");
    }
    public static String nameToCode(String name){
        String code = FeeCodeTranslate.FEECODEMAP.get(name);
        return code == null ? "" : code;
    }
}
ソースコード生成.classファイルの後、逆コンパイルは以下の通りです.
public class FeeCodeTranslate {
    private static final Map FEECODEMAP;
    public FeeCodeTranslate() //       
    {
    }
    public static String nameToCode(String name){
        String code = (String)FEECODEMAP.get(name);
        return code != null ? code : "";
    }
    static {
        FEECODEMAP = new HashMap();
        FEECODEMAP.put("      ", "JJFY");
        FEECODEMAP.put("      ", "DKFY");
        FEECODEMAP.put("      ", "CSFY");
    }
}
instrumentを使って実行したら、新しいクラスが得られます.
public class FeeCodeTranslate {

    public static final transient int __cobertura_counters[]; //       
    private static final Map FEECODEMAP;

    public FeeCodeTranslate()
    {
        int i = 0;
        __cobertura_counters[1]++; //       
        super();
    }

    public static String nameToCode(String name){
        String code;
        __cobertura_init();     //       ,       
        int i = 0;
        __cobertura_counters[7]++; //       
        code = (String)FEECODEMAP.get(name);
        __cobertura_counters[8]++; //       
        code;
        int j = 10;
        JVM INSTR ifnonnull 67;
           goto _L1 _L2
_L1:
        __cobertura_counters[9]++;  //       
        j = 0;
        "";
          goto _L3
_L2:
        __cobertura_counters[j]++;  //       
        j = 0;
        code;
_L3:
        return;
    }

    public static void __cobertura_init() { //       
        if (__cobertura_counters == null) {
            __cobertura_counters = new int[11];
            TouchCollector.registerClass("com/baidu/idc/base/FeeCodeTranslate");
        }
    }
    //       
    public static void __cobertura_classmap_0(LightClassmapListener  lightclassmaplistener) {
        lightclassmaplistener.putLineTouchPoint(11, 1, "<init>", "()V");
        lightclassmaplistener.putLineTouchPoint(13, 2, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(15, 3, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(16, 4, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(17, 5, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(18, 6, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(21, 7, "nameToCode", "(Ljava/lang/String;)Ljava/lang/String;");
        lightclassmaplistener.putLineTouchPoint(22, 8, "nameToCode", "(Ljava/lang/String;)Ljava/lang/String;");
        lightclassmaplistener.putJumpTouchPoint(22, 10, 9);
        LightClassmapListener  = lightclassmaplistener;
    }
    //       
    public static void __cobertura_classmap( LightClassmapListener lightclassmaplistener) {
        lightclassmaplistener.setClazz("com/baidu/idc/base/FeeCodeTranslate");
        lightclassmaplistener.setSource("FeeCodeTranslate.java");
        __cobertura_classmap_0(lightclassmaplistener);
        LightClassmapListener  = lightclassmaplistener;
    }
    //       
    public static int[] __cobertura_get_and_reset_counters(){
        int ai[] = __cobertura_counters;
        __cobertura_counters = new int[__cobertura_counters.length];
        return ai;
    }
    static {
        __cobertura_init(); //       
        int i = 0;
        __cobertura_counters[2]++; //       
        FEECODEMAP = new HashMap();
        __cobertura_counters[3]++; //       
        FEECODEMAP.put("      ", "JJFY");
        __cobertura_counters[4]++; //       
        FEECODEMAP.put("      ", "DKFY");
        __cobertura_counters[5]++; //       
        FEECODEMAP.put("      ", "CSFY");
        __cobertura_counters[6]++; //       
    }
}
2,テストを実行します
テストケース実行時に、coberturaが修正した.classコマンドを呼び出します.classipath設定時、instructed clasesはorial(uninstructed)clasesの前にあります.こうやってテストする時はcoberturaの修正したクラスを引用します.テスト情報はcobertura.serファイルに書き込みます.
コマンド実行時の注意事項は、次のantのセグメントを参照してください.
    <target name="test" depends="init,compile">
        <junit fork="yes" dir="${basedir}" failureProperty="test.failed">
            <!--
                Specify the name of the coverage data file to use.
            -->
            <sysproperty key="net.sourceforge.cobertura.datafile" file="${reports.dir}/coverage.ser" />

            <!--
                Note the classpath order: instrumented classes are before the
                original (uninstrumented) classes.  This is important.
            -->
            <classpath location="${instrumented.dir}" />
            <classpath location="${classes.dir}" />

            <!--
                The instrumented classes reference classes used by the
                Cobertura runtime, so Cobertura and its dependencies
                must be on your classpath.
            -->
            <classpath refid="cobertura_classpath" />

            <formatter type="xml" />
            <test name="${testcase}" todir="${reports.xml.dir}" if="testcase" />
            <batchtest todir="${reports.xml.dir}" unless="testcase">
                <fileset dir="${src.dir}">
                    <include name="**/*Test.java" />
                </fileset>
            </batchtest>
        </junit>

        <junitreport todir="${reports.xml.dir}">
            <fileset dir="${reports.xml.dir}">
                <include name="TEST-*.xml" />
            </fileset>
            <report format="frames" todir="${reports.html.dir}" />
        </junitreport>
    </target>
3,レポートを生成する
cobertura.serからオーバーレイレートデータを取得し、srcで提供されたソースコードに関連して最終的なカバーレート報告を生成し、target\site\coberturaパスの下に置く.
java -cp "%COBERTURA_HOME%cobertura.jar;..." 
net.sourceforge.cobertura.reporting.Main --format html 
--datafile cobertura.ser --destination cobertura-site src
最終的にカバー率報告データを下図に示します.
Cobertura原理_第1张图片