/*
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.errorprone;

import static com.google.errorprone.BugPattern.Category.ONE_OFF;
import static com.google.errorprone.BugPattern.MaturityLevel.EXPERIMENTAL;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static com.google.errorprone.CompilationTestHelper.sources;
import static com.google.errorprone.DiagnosticTestHelper.diagnosticMessage;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.internal.matchers.StringContains.containsString;

import com.google.errorprone.BugPattern.Category;
import com.google.errorprone.BugPattern.MaturityLevel;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ExpressionStatementTreeMatcher;
import com.google.errorprone.bugpatterns.DeadException;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.Tree;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

/**
 * @author alexeagle@google.com (Alex Eagle)
 */
public class ErrorReportingJavaCompilerIntegrationTest {

  private DiagnosticTestHelper diagnosticHelper;
  private PrintWriter printWriter;
  private ByteArrayOutputStream outputStream;
  private ErrorProneCompiler.Builder compilerBuilder;
  ErrorProneCompiler compiler;

  @Before
  public void setUp() {
    diagnosticHelper = new DiagnosticTestHelper();
    outputStream = new ByteArrayOutputStream();
    printWriter = new PrintWriter(new OutputStreamWriter(outputStream));
    compilerBuilder = new ErrorProneCompiler.Builder()
        .named("test")
        .redirectOutputTo(printWriter)
        .listenToDiagnostics(diagnosticHelper.collector);
    compiler = compilerBuilder.build();
  }

  @Test
  public void fileWithError() throws Exception {
    int exitCode = compiler.compile(sources(getClass(),
        "com/google/errorprone/bugpatterns/EmptyIfStatementPositiveCases.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(1));

    Matcher<Iterable<? super Diagnostic<JavaFileObject>>> matcher = hasItem(
        diagnosticMessage(containsString("[EmptyIf]")));
    assertThat("Error should be found. " + diagnosticHelper.describe(),
        diagnosticHelper.getDiagnostics(), matcher);
  }

  @Test
  public void fileWithWarning() throws Exception {
    compiler = compilerBuilder.report(ErrorProneScanner.forMatcher(DeadException.class))
        .build();
    int exitCode = compiler.compile(sources(getClass(),
        "com/google/errorprone/bugpatterns/DeadExceptionPositiveCases.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(0));

    Matcher<Iterable<? super Diagnostic<JavaFileObject>>> matcher = hasItem(
        diagnosticMessage(containsString("[DeadException]")));
    assertThat("Warning should be found. " + diagnosticHelper.describe(),
        diagnosticHelper.getDiagnostics(), matcher);
  }

  @Test
  public void fileWithMultipleTopLevelClasses() throws Exception {
    int exitCode = compiler.compile(
        sources(getClass(), "com/google/errorprone/MultipleTopLevelClassesWithNoErrors.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(0));
  }

  @Test
  public void fileWithMultipleTopLevelClassesExtends() throws Exception {
    int exitCode = compiler.compile(
        sources(getClass(), "com/google/errorprone/MultipleTopLevelClassesWithNoErrors.java",
            "com/google/errorprone/ExtendedMultipleTopLevelClassesWithNoErrors.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(0));
  }

  /**
   * Regression test for a bug in which multiple top-level classes may cause
   * NullPointerExceptions in the matchers.
   */
  @Test
  public void fileWithMultipleTopLevelClassesExtendsWithError()
      throws Exception {
    int exitCode = compiler.compile(
        sources(getClass(), "com/google/errorprone/MultipleTopLevelClassesWithErrors.java",
            "com/google/errorprone/ExtendedMultipleTopLevelClassesWithErrors.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(1));

    Matcher<Iterable<? super Diagnostic<JavaFileObject>>> matcher = hasItem(
        diagnosticMessage(containsString("[SelfAssignment]")));
    assertThat("Warning should be found. " + diagnosticHelper.describe(),
        diagnosticHelper.getDiagnostics(), matcher);
  }

  @Test
  public void unhandledExceptionsAreReportedWithoutBugParadeLink() throws Exception {
    @BugPattern(name = "", explanation = "", summary = "",
        maturity = EXPERIMENTAL, severity = ERROR, category = ONE_OFF)
    class Throwing extends BugChecker implements ExpressionStatementTreeMatcher {
      @Override
      public Description matchExpressionStatement(ExpressionStatementTree tree, VisitorState state)
      {
        throw new IllegalStateException("test123");
      }
    }
    compilerBuilder.report(new ErrorProneScanner(new Throwing()));
    compiler = compilerBuilder.build();
    int exitCode = compiler.compile(
        sources(getClass(), "com/google/errorprone/MultipleTopLevelClassesWithErrors.java",
            "com/google/errorprone/ExtendedMultipleTopLevelClassesWithErrors.java"));
    outputStream.flush();
    assertThat(outputStream.toString(), exitCode, is(1));
    Matcher<Iterable<? super Diagnostic<JavaFileObject>>> matcher = hasItem(
        diagnosticMessage(CoreMatchers.<String>allOf(
            containsString("IllegalStateException: test123"),
            containsString("unhandled exception was thrown by the Error Prone"))));
    assertThat("Error should be reported. " + diagnosticHelper.describe(),
        diagnosticHelper.getDiagnostics(), matcher);
  }
}

@@ -997,55 +997,8 @@
 ge;%0A
-import static org.hamcrest.CoreMatchers.allOf;%0A
 impo
@@ -1042,16 +1042,16 @@
 asItem;%0A
+
 import s
@@ -1207,168 +1207,8 @@
 g;%0A%0A
-import com.google.errorprone.BugPattern.Category;%0Aimport com.google.errorprone.BugPattern.MaturityLevel;%0Aimport com.google.errorprone.BugPattern.SeverityLevel;%0A
 impo
@@ -1385,70 +1385,22 @@
 rns.
-DeadException;%0Aimport com.google.errorprone.fixes.SuggestedFix
+EmptyStatement
 ;%0Aim
@@ -1504,41 +1504,8 @@
 ee;%0A
-import com.sun.source.tree.Tree;%0A
 impo
@@ -1612,16 +1612,82 @@
 .Test;%0A%0A
+import javax.tools.Diagnostic;%0Aimport javax.tools.JavaFileObject;%0A
 import j
@@ -1747,24 +1747,24 @@
 reamWriter;%0A
+
 import java.
@@ -1784,75 +1784,8 @@
 r;%0A%0A
-import javax.tools.Diagnostic;%0Aimport javax.tools.JavaFileObject;%0A%0A
 /**%0A
@@ -3203,29 +3203,30 @@
 Matcher(
-DeadException
+EmptyStatement
 .class))
@@ -3343,29 +3343,30 @@
 atterns/
-DeadException
+EmptyStatement
 Positive
@@ -3589,21 +3589,22 @@
 g(%22%5B
-DeadException
+EmptyStatement
 %5D%22))

Use a less problematic checker as our example of a 'warning' for integration testing.