/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.maven.plugin;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.FileInputStream;

import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.junit.Test;

import ro.isdc.wro.extensions.processor.support.linter.LinterException;
import ro.isdc.wro.extensions.support.lint.LintReport;
import ro.isdc.wro.extensions.support.lint.ReportXmlFormatter.FormatterType;
import ro.isdc.wro.util.WroTestUtils;
import ro.isdc.wro.util.WroUtil;


/**
 * Test the {@link JsHintMojo} class.
 *
 * @author Alex Objelean
 */
public class TestJsHintMojo
    extends AbstractTestLinterMojo {
  /**
   * {@inheritDoc}
   */
  @Override
  protected AbstractLinterMojo newLinterMojo() {
    return new JsHintMojo() {
      @Override
      void onException(final Exception e) {
        fail("Shouldn't fail. Exception message: " + e.getMessage());
      }
    };
  }

  @Test
  public void usePredefOptions()
      throws Exception {
    getMojo().setOptions("predef=['YUI','window','document','OnlineOpinion','xui']");
    getMojo().setTargetGroups("undef");
    getMojo().execute();
  }

  @Test
  public void testMojoWithPropertiesSet()
      throws Exception {
    getMojo().setIgnoreMissingResources(true);
    getMojo().execute();
  }

  @Test
  public void testWroXmlWithInvalidResourcesAndIgnoreMissingResourcesTrue()
      throws Exception {
    setWroWithInvalidResources();
    getMojo().setIgnoreMissingResources(true);
    getMojo().execute();
  }

  @Test
  public void testResourceWithUndefVariables()
      throws Exception {
    getMojo().setTargetGroups("undef");
    getMojo().execute();
  }

  @Test
  public void shouldProcessMultipleGroupsMore()
      throws Exception {
    for (int i = 0; i < 10; i++) {
      shouldProcessMultipleGroups();
    }
  }

  @Test
  public void shouldProcessMultipleGroups()
      throws Exception {
    getMojo().setTargetGroups("undef,valid,g3");
    getMojo().execute();
  }

  @Test
  public void testEmptyOptions()
      throws Exception {
    getMojo().setOptions("");
    getMojo().setTargetGroups("undef");
    getMojo().execute();
  }

  @Test
  public void shouldGenerateXmlReportFile()
      throws Exception {
    final File reportFile = WroUtil.createTempFile();
    try {
      ((JsHintMojo) getMojo()).setReportFile(reportFile);
      getMojo().setOptions("undef, browser");
      getMojo().setTargetGroups(null);
      getMojo().setFailNever(true);
      getMojo().setIgnoreMissingResources(true);
      getMojo().execute();
    } finally {
      // Assert that file is big enough to prove that it contains serialized errors.
      assertTrue(reportFile.length() > 1000);
      FileUtils.deleteQuietly(reportFile);
    }
  }

  @Test
  public void shouldGenerateReportWithDefaultFormat()
      throws Exception {
    generateAndCompareReportFile(null, "jshint-default.xml");
  }

  @Test
  public void shouldGenerateReportWithCheckstyleFormat()
      throws Exception {
    generateAndCompareReportFile(FormatterType.CHECKSTYLE.getFormat(), "jshint-checkstyle.xml");
  }

  @Test(expected = MojoExecutionException.class)
  public void cannotGenerateReportWithInvalidFormat()
      throws Exception {
    final File reportFile = WroUtil.createTempFile();
    final JsHintMojo mojo = (JsHintMojo) getMojo();
    try {
      mojo.setReportFile(reportFile);
      mojo.setReportFormat("INVALID");
      mojo.setOptions("undef, browser");
      mojo.setTargetGroups(null);
      mojo.setFailNever(true);
      mojo.setIgnoreMissingResources(true);
      mojo.execute();
    } finally {
      FileUtils.deleteQuietly(reportFile);
    }
  }

  @Test
  public void shouldNotFailWhenThresholdIsGreaterThanNumberOfErrors()
      throws Exception {
    final JsHintMojo jsHintMojo = (JsHintMojo) getMojo();
    jsHintMojo.setFailThreshold(6);
    executeResourcesWithErrors();
  }

  @Test(expected = MojoExecutionException.class)
  public void shouldFailWhenThresholdEqualsWithNumberOfErrors()
      throws Exception {
    final JsHintMojo jsHintMojo = (JsHintMojo) getMojo();
    jsHintMojo.setFailThreshold(5);
    executeResourcesWithErrors();
  }

  /**
   * Checks that build doesn't fail when the failFast is true and there is no resources to be processed.
   */
  @Test
  public void shouldNotFailWhenNoErrorsFound()
      throws Exception {
    final JsHintMojo jsHintMojo = (JsHintMojo) getMojo();
    jsHintMojo.setFailThreshold(0);
    jsHintMojo.setFailFast(false);
    jsHintMojo.setIgnoreMissingResources(true);
    jsHintMojo.setTargetGroups("invalidWildcardResource");
    getMojo().execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void shouldReportOnlyFirstErrorWhenFailFastIsTrue()
      throws Exception {
    final JsHintMojo jsHintMojo = (JsHintMojo) getMojo();
    jsHintMojo.setFailFast(true);
    try {
      executeResourcesWithErrors();
    } finally {
      final LintReport<?> lintReport = jsHintMojo.getLintReport();
      assertEquals(1, lintReport.getReports().size());
    }
  }

  @Test(expected = MojoExecutionException.class)
  public void shouldReportAllErrorsWhenFailFastIsFalse()
      throws Exception {
    final JsHintMojo jsHintMojo = (JsHintMojo) getMojo();
    jsHintMojo.setFailFast(false);
    try {
      executeResourcesWithErrors();
    } catch (final Exception e) {
      e.printStackTrace();
      throw e;
    } finally {
      final LintReport<?> lintReport = jsHintMojo.getLintReport();
      assertEquals(2, lintReport.getReports().size());
    }
  }

  private void executeResourcesWithErrors()
      throws MojoExecutionException {
    getMojo().setTargetGroups("invalidResources");
    getMojo().setOptions("undef, browser");
    getMojo().execute();
  }

  private void generateAndCompareReportFile(final String reportFormat, final String expectedFileName)
      throws Exception {
    final File reportFile = WroUtil.createTempFile();
    final JsHintMojo mojo = (JsHintMojo) getMojo();
    try {
      mojo.setReportFile(reportFile);
      if (reportFormat != null) {
        mojo.setReportFormat(reportFormat);
      }
      mojo.setOptions("undef, browser");
      mojo.setTargetGroups(null);
      mojo.setFailNever(true);
      mojo.setIgnoreMissingResources(true);
      mojo.execute();
    } finally {
      // Assert that file is big enough to prove that it contains serialized errors.
      WroTestUtils.compare(getClass().getResourceAsStream("report/" + expectedFileName),
          new FileInputStream(reportFile));
      FileUtils.deleteQuietly(reportFile);
    }
  }

  @Test(expected = LinterException.class)
  public void shouldFailWhenThereAreLinterErrorsEvenWhenIncrementBuildIsEnabled()
      throws Throwable {
    getMojo().setParallelProcessing(true);
    getMojo().setIncrementalBuildEnabled(true);
    getMojo().setTargetGroups("invalidResources");
    try {
      getMojo().execute();
    } catch (final MojoExecutionException e) {
      assertTrue(e.getCause() instanceof LinterException);
      try {
        getMojo().execute();
      } catch (final MojoExecutionException secondException) {
        throw secondException.getCause();
      }
    } finally {
      getMojo().clean();
    }
  }
}
