package com.ftinc.si.assist.test;

import java.text.NumberFormat;
import java.util.ArrayList;


/* JUNITのXML書式
<?xml version="1.0" encoding="UTF-8"?>
<!-- a description of the JUnit XML format and how Jenkins parses it. See also junit.xsd -->

<!-- if only a single testsuite element is present, the testsuites
     element can be omitted. All attributes are optional. -->
<testsuites disabled="" <!-- total number of disabled tests from all testsuites. -->
            errors=""   <!-- total number of tests with error result from all testsuites. -->
            failures="" <!-- total number of failed tests from all testsuites. -->
            name=""
            tests=""    <!-- total number of successful tests from all testsuites. -->
            time=""     <!-- time in seconds to execute all test suites. -->
        >

  <!-- testsuite can appear multiple times, if contained in a testsuites element.
       It can also be the root element. -->
  <testsuite name=""      <!-- Full (class) name of the test for non-aggregated testsuite documents.
                               Class name without the package for aggregated testsuites documents. Required -->
         tests=""     <!-- The total number of tests in the suite, required. -->
         disabled=""  <!-- the total number of disabled tests in the suite. optional -->
             errors=""    <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
                               for example an unchecked throwable; or a problem with the implementation of the test. optional -->
             failures=""  <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
                               by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
             hostname=""  <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
         id=""        <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
         package=""   <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
         skipped=""   <!-- The total number of skipped tests. optional -->
         time=""      <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
         timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
         >

    <!-- Properties (e.g., environment settings) set during test
     execution. The properties element can appear 0 or once. -->
    <properties>
      <!-- property can appear multiple times. The name and value attributres are required. -->
      <property name="" value=""/>
    </properties>

    <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
    <testcase name=""       <!-- Name of the test method, required. -->
          assertions="" <!-- number of assertions in the test case. optional -->
          classname=""  <!-- Full class name for the class the test method is in. required -->
          status=""
          time=""       <!-- Time taken (in seconds) to execute the test. optional -->
          >

      <!-- If the test was not executed or failed, you can specify one
           the skipped, error or failure elements. -->

      <!-- skipped can appear 0 or once. optional -->
      <skipped/>

      <!-- Indicates that the test errored. An errored test is one
           that had an unanticipated problem. For example an unchecked
           throwable or a problem with the implementation of the
           test. Contains as a text node relevant data for the error,
           for example a stack trace. optional -->
      <error message="" <!-- The error message. e.g., if a java exception is thrown, the return value of getMessage() -->
         type=""    <!-- The type of error that occured. e.g., if a java execption is thrown the full class name of the exception. -->
         ></error>

      <!-- Indicates that the test failed. A failure is a test which
       the code has explicitly failed by using the mechanisms for
       that purpose. For example via an assertEquals. Contains as
       a text node relevant data for the failure, e.g., a stack
       trace. optional -->
      <failure message="" <!-- The message specified in the assert. -->
           type=""    <!-- The type of the assert. -->
           ></failure>

      <!-- Data that was written to standard out while the test was executed. optional -->
      <system-out></system-out>

      <!-- Data that was written to standard error while the test was executed. optional -->
      <system-err></system-err>
    </testcase>

    <!-- Data that was written to standard out while the test suite was executed. optional -->
    <system-out></system-out>
    <!-- Data that was written to standard error while the test suite was executed. optional -->
    <system-err></system-err>
  </testsuite>
</testsuites>
 */

public class TestLogger {
	private static String s_suiteName;
	private static int s_countCmds;
	private static int s_skipped;
	private static int s_disabled;
	private static int s_errors;
	private static int s_failures;
	private static int s_asserts;
	private static long s_milisec;
	private static NumberFormat s_formatsec = NumberFormat.getNumberInstance();
	
	private static String s_curCaseName = null;
	
	private static ArrayList<String> s_results = new ArrayList<String>();
	private static String s_out =null;
	private static String s_err = null;
	
	public void initLog() {
		s_formatsec.setMaximumIntegerDigits(3);   // 整数部分の最大桁数を設定
		s_formatsec.setMinimumIntegerDigits(3);   // 整数部分の最小桁数を設定
		s_formatsec.setMaximumFractionDigits(2);  // 小数部分の最大桁数を設定
		s_formatsec.setMinimumFractionDigits(2);  // 小数部分の最小桁数を設定
	}

	//TestCase実行の始まり登録
	public static void beginTestSuite(TestCaseRecord carec) {
		s_results.clear();
		s_countCmds = 0;
		s_errors = 0;
		s_failures = 0;
		s_skipped = 0;
		s_disabled = 0;
		s_asserts = 0;
		s_out = ""; //$NON-NLS-1$
		s_err = ""; //$NON-NLS-1$
		s_milisec = 0;
		
		s_suiteName = carec.name;
	}
	
	
	//TestCaseの実行結果を返す。途中で抜ける場合も考慮すること。stopとexit
	public static String endTestSuite() {
		//単位を秒にする。
		String stime = s_formatsec.format(s_milisec / 1000.0);
		
		String t_result = "<testsuite name=\"" + s_suiteName + "\" tests=\"" + Integer.toString(s_countCmds)  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			+ "\" disabled=\"" + Integer.toString(s_disabled) + "\" skipped=\"" + Integer.toString(s_skipped)  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			+ "\" assertions=\"" + Integer.toString(s_asserts) 
			+ "\" errors=\"" + Integer.toString(s_errors) + "\" failures=\"" + Integer.toString(s_failures)  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			+ "\" time=\"" + stime + "\">\n"; //$NON-NLS-1$ //$NON-NLS-2$
		
		for (int i = 0; i < s_results.size(); i++) {
			t_result += s_results.get(i);
		}
		
		t_result += "<system-out>" + s_out + "</system-out>\n"; //$NON-NLS-1$ //$NON-NLS-2$
		t_result += "<system-err>" + s_err + "</system-err>\n"; //$NON-NLS-1$ //$NON-NLS-2$
		t_result += "</testsuite>\n"; //$NON-NLS-1$
		return t_result;
	}

	private static boolean s_preparing = true;
	public static void cmdPrepare(String name) {
		s_curCaseName = name;
		s_preparing = true;
	}

	
	//テスト数のカウント。
	public static void cmdBegin(String name) {
		s_countCmds++;
		s_curCaseName = name;
		s_preparing = false;
	}
	
	public static String cmdName() {
		return s_suiteName + "(" + s_curCaseName + ")";
	}
	
	
	public static void cmdIsOK(long xtime, String inName, int nasserts) {
		//単位を秒にする。
		String stime = s_formatsec.format(xtime / 1000.0);
		s_milisec += xtime;
		s_asserts += nasserts;

		String t_result = "<testcase classname=\"" + s_suiteName + "\" name=\"" + s_curCaseName + "\" assertions=\"" + Integer.toString(nasserts) + "\" time=\"" + stime + "\"/>\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
		s_results.add(t_result);
	}
	
	public static void cmdFailed(long xtime, String msg, String inName, int nasserts) {
		if (s_preparing) {
			s_countCmds++;
			s_preparing = false;
		}
		s_failures++;
		
		//単位を秒にする。
		String stime = s_formatsec.format(xtime / 1000.0);
		s_milisec += xtime;
		s_asserts += nasserts;
		
		//結果を文字列にする。
		String t_result = "<testcase classname=\"" +  s_suiteName + "\" name=\"" + s_curCaseName + "\" assertions=\"" + Integer.toString(nasserts) + "\" time=\"" + stime + "\">\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$

		t_result += "<failure type=\"AssertionError\" message=\"test failed\">" + msg + "</failure>\n"; //$NON-NLS-1$ //$NON-NLS-2$

		t_result += "</testcase>\n"; //$NON-NLS-1$
		s_results.add(t_result);
	}

	//excptsが不定長なのは、内部のテスト実行と外部プロセス実行の二つから例外が発生する可能性があるため。
	public static void cmdCauseError(long xtime, String msg, String inName, Throwable... excpts) {
		if (s_preparing) {
			s_countCmds++;
			s_preparing = false;
		}
		s_failures++;
		s_errors++;

		//単位を秒にする。
		String stime = s_formatsec.format(xtime / 1000.0);
		s_milisec += xtime;
		
		String t_result = "<testcase classname=\"" +  s_suiteName + "\" name=\"" + s_curCaseName + "\" time=\"" + stime + "\">\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$

		String str_classes = ""; //$NON-NLS-1$
		for (int i = 0; i < excpts.length; i ++) {
			if (str_classes.length() > 0) {
				str_classes += "|"; //$NON-NLS-1$
			}
			if (excpts[i] != null) {
				str_classes += excpts[i].getClass().getName();
			}
		}
		t_result += "<error type=\"" + str_classes + "\" message=\"UnexpectedException\">" + msg + "</error>\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		
		t_result += "</testcase>\n"; //$NON-NLS-1$
		s_results.add(t_result);
	}
	
	public static void cmdSkipped(long xtime, String inName) {
		String t_result = "<testcase classname=\"" + s_suiteName + "\" name=\"" + s_curCaseName + "\"/>\n";
		s_results.add(t_result);
		s_results.add("<skipped/>\n"); //$NON-NLS-1$
		s_results.add("</testcase>");
		s_skipped++;
		if (s_preparing) {
			s_preparing = false;
		}
	}

	public static void cmdDisabled(long xtime, String inName) {
		String t_result = "<testcase classname=\"" + s_suiteName + "\" name=\"" + s_curCaseName + "\"/>\n";
		s_results.add(t_result);
		s_results.add("<skipped/>\n"); //$NON-NLS-1$
		s_results.add("</testcase>");
		s_disabled++;
		if (s_preparing) {
			s_preparing = false;
		}
	}

	//System.outの代わり。
	public static void out(String str) {
		s_out += str + "\n"; //$NON-NLS-1$
	}
	
	//System.errの代わり。
	public static void err(String str) {
		if (s_err != null) {//RemoteDBの中からFsonを使用する際に呼ばれる可能性がある。
			s_err += str + "\n"; //$NON-NLS-1$
		}
	}
	
}
