/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (C) 2000-2002 The Apache Software Foundation.  All rights
 * reserved.
 * Copyright (C) 2010 John Lewis
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "Ant" and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package net.sourceforge.cobertura.test;

import groovy.util.AntBuilder;
import groovy.util.Node;
import net.sourceforge.cobertura.ant.ReportTask;
import net.sourceforge.cobertura.test.util.TestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.tools.ant.taskdefs.Java;
import org.junit.Test;

import java.io.File;
import java.util.HashMap;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class FunctionalTest extends AbstractCoberturaTestCase {
	AntBuilder ant = TestUtils.getCoberturaAntBuilder(TestUtils
			.getCoberturaClassDir());

	Node dom;
	IgnoreUtil ignoreUtil;

	@Test
	public void interfaceFunctionalTest() throws Exception {
		/*
		 * Interfaces are not instrumented (yet).   So, instrument an interface and make
		 * sure line/conditional information is not in the report.
		 */
		/*
		 * Use a temporary directory and create a few sources files.
		 */
		File tempDir = TestUtils.getTempDir();
		File srcDir = new File(tempDir, "src");
		File instrumentDir = new File(tempDir, "instrument");

		File mainSourceFile = new File(srcDir, "mypackage/Main.java");
		File datafile = new File(srcDir, "cobertura.ser");
		mainSourceFile.getParentFile().mkdirs();

		File interfaceSourceFile = new File(srcDir,
				"mypackage/MyInterface.java");

		FileUtils
				.write(
						interfaceSourceFile,
						"\n package mypackage;"
								+ "\n "
								+ "\n public interface MyInterface {"
								+ "\n 	public static final Object MY_CONSTANT = new Object();  /* the test expects this to be line 5 */"
								+ "\n }");

		FileUtils.write(mainSourceFile, "\n package mypackage;" + "\n "
				+ "\n public class Main implements MyInterface {" + "\n "
				+ "\n  public static void main(String[] args) {"
				+ "\n   System.out.println(new Main());"
				+ "\n   System.out.println(MY_CONSTANT);" + "\n  }" + "\n }");
		TestUtils.compileSource(ant, srcDir);
		TestUtils.instrumentClasses(ant, srcDir, datafile, instrumentDir);

		/*
		 * Kick off the Main (instrumented) class.
		 */
		Java java = new Java();
		java.setProject(TestUtils.project);
		java.setClassname("mypackage.Main");
		java.setDir(srcDir);
		java.setFork(true);
		java.setFailonerror(true);
		java.setClasspath(TestUtils.getCoberturaDefaultClasspath());
		java.execute();

		/*
		 * Now create a cobertura xml file and make sure the correct counts are in it.
		 */
		ReportTask reportTask = new ReportTask();
		reportTask.setProject(TestUtils.project);
		reportTask.setDataFile(datafile.getAbsolutePath());
		reportTask.setFormat("xml");
		reportTask.setDestDir(srcDir);
		reportTask.execute();

		dom = TestUtils.getXMLReportDOM(srcDir.getAbsolutePath()
				+ "/coverage.xml");

		List<Node> lines = TestUtils.getLineCounts(dom,
				"mypackage.MyInterface", "<clinit>", null);

		// When/if interfaces are instrumented, the next line can go away and the
		// lines in this method that have been commented out, can be uncommented.
		assertEquals("Interfaces are being instrumented", lines.size(), 0);
	}

	@Test
	public void conditionalInFinallyFunctionalTest() throws Exception {
		/*
		 * Use a temporary directory and create a few sources files.
		 */
		File tempDir = TestUtils.getTempDir();
		File srcDir = new File(tempDir, "src");
		File instrumentDir = new File(tempDir, "instrument");

		File mainSourceFile = new File(srcDir, "mypackage/Main.java");
		File datafile = new File(srcDir, "cobertura.ser");
		mainSourceFile.getParentFile().mkdirs();

		FileUtils.write(mainSourceFile, "\n package mypackage;" + "\n " + "\n "
				+ "\n " + "\n public class Main {" + "\n  "
				+ "\n  private boolean isDisabled() {" + "\n   return true;"
				+ "\n  }" + "\n  " + "\n  private void doSomething() {"
				+ "\n  }" + "\n  " + "\n  public void aMethod() {"
				+ "\n   boolean disabled = false;" + "\n   try {"
				+ "\n    disabled = isDisabled();" + "\n   } finally {"
				+ "\n   if (disabled)" + "\n    doSomething();" + "\n   }"
				+ "\n  }" + "\n  " + "\n  " + "\n }");

		TestUtils.compileSource(ant, srcDir);

		TestUtils.instrumentClasses(ant, srcDir, datafile, instrumentDir);

		/*
		 * Now create a cobertura xml file and make sure the correct counts are in it.
		 */
		ReportTask reportTask = new ReportTask();
		reportTask.setProject(TestUtils.project);
		reportTask.setDataFile(datafile.getAbsolutePath());
		reportTask.setFormat("xml");
		reportTask.setDestDir(srcDir);
		reportTask.execute();

		dom = TestUtils.getXMLReportDOM(srcDir.getAbsolutePath()
				+ "/coverage.xml");

		List<Node> lines = TestUtils.getLineCounts(dom, "mypackage.Main",
				"aMethod", null);
		Node conditionalLine = null;
		for (Node line : lines) {
			if ("20".equals(line.attribute("number"))) {
				conditionalLine = line;
			}
		}
		assertNotNull(conditionalLine);
		assertEquals("0% (0/2)", conditionalLine
				.attribute("condition-coverage"));
	}

	@Test
	public void callJunit() throws Exception {
		/*
		 * Use a temporary directory and create a few sources files.
		 */
		File tempDir = TestUtils.getTempDir();
		final File srcDir = new File(tempDir, "src");

		final File reportDir = new File(tempDir, "report");
		reportDir.mkdirs();

		final File instrumentDir = new File(tempDir, "instrument");
		instrumentDir.mkdirs();

		File buildDir = new File(tempDir, "build");
		buildDir.mkdirs();

		File testSourceFile = new File(srcDir, "mypackage/MyTest.groovy");
		testSourceFile.getParentFile().mkdirs();

		FileUtils
				.write(
						testSourceFile,
						"\n package mypackage"
								+ "\n "
								+ "\n import junit.framework.TestSuite"
								+ "\n import junit.framework.Test"
								+ "\n "
								+ "\n "
								+ "\n public class MyTest extends TestSuite {"
								+ "\n public MyTest(String arg0) {"
								+ "\n 	super(arg0);"
								+ "\n }"
								+ "\n "
								+ "\n public static Test suite() {"
								+ "\n 	"
								+ "\n 	// do something that will cause Sub's static initializer to run"
								+ "\n 	Sub.class" + "\n "
								+ "\n 	return new TestSuite(\"Empty Suite\")"
								+ "\n }" + "\n }");

		File superSourceFile = new File(srcDir, "mypackage/Super.java");
		File subSourceFile = new File(srcDir, "mypackage/Sub.java");
		File datafile = new File(srcDir, "cobertura.ser");

		FileUtils.write(superSourceFile, "\n package mypackage;" + "\n "
				+ "\n public class Super {" + "\n  static {"
				+ "\n   Sub.aStaticMethod();" + "\n  };" + "\n }");

		FileUtils.write(subSourceFile, "\n package mypackage;" + "\n "
				+ "\n public class Sub extends Super {" + "\n  "
				+ "\n  public static void aStaticMethod() {"
				+ "\n   System.out.println(\"aStaticMethod called\");"
				+ "\n  }" + "\n  " + "\n }");

		// compile to the srcDir
		TestUtils.compileGroovy(ant, srcDir);

		// instrument all but the test class (in place)
		TestUtils.instrumentClasses(ant, srcDir, datafile, instrumentDir,
				new HashMap() {
					{
						put("excludeClassesRegexList", "mypackage.MyTest");
					}
				});

		// run the MyTest
		TestUtils.junit(new HashMap() {
			{
				put("testClass", "mypackage.MyTest");
				put("ant", ant);
				put("buildDir", srcDir);
				put("instrumentDir", instrumentDir);
				put("reportDir", reportDir);
			}
		});

		/*
		 * Now create a cobertura xml file and make sure the correct counts are in it.
		 */
		ReportTask reportTask = new ReportTask();
		reportTask.setProject(TestUtils.project);
		reportTask.setDataFile(datafile.getAbsolutePath());
		reportTask.setFormat("xml");
		reportTask.setDestDir(srcDir);
		reportTask.execute();

		dom = TestUtils.getXMLReportDOM(srcDir.getAbsolutePath()
				+ "/coverage.xml");

		List<Node> lines = TestUtils.getLineCounts(dom, "mypackage.Sub",
				"aStaticMethod");

		Node aStaticMethodLine = null;
		for (Node line : lines) {
			if ("7".equals(line.attribute("number"))) {
				aStaticMethodLine = line;
				break;
			}
		}

		assertNotNull(aStaticMethodLine);
		assertEquals(1, Integer.valueOf(
				aStaticMethodLine.attribute("hits").toString()).intValue());
	}

	@Test
	public void simpleFunctionalTest() throws Exception {
		/*
		 * Use a temporary directory and create a few sources files.
		 */
		File tempDir = TestUtils.getTempDir();
		File srcDir = new File(tempDir, "src");
		File instrumentDir = new File(tempDir, "instrument");

		File mainSourceFile = new File(srcDir, "mypackage/Main.java");
		File simpleSourceFile = new File(srcDir, "mypackage/Simple.java");
		File datafile = new File(srcDir, "cobertura.ser");
		mainSourceFile.getParentFile().mkdirs();

		FileUtils.write(simpleSourceFile, "\n package mypackage;" + "\n "
				+ "\n public class Simple {" + "\n }");

		FileUtils.write(mainSourceFile, "\n package mypackage;" + "\n " + "\n "
				+ "\n " + "\n public class Main {" + "\n " + "\n "
				+ "\n  public boolean isSimple() {" + "\n   return false;"
				+ "\n  }" + "\n " + "\n " + "\n  private Object create() {"
				+ "\n   if (isSimple()) {"
				+ "\n    Object result = new Simple();" + "\n   } else {"
				+ "\n    Object result = new Main();" + "\n   }"
				+ "\n   return null;" + "\n " + "\n  }" + "\n }");

		TestUtils.compileSource(ant, srcDir);

		TestUtils.instrumentClasses(ant, srcDir, datafile, instrumentDir);
	}
}