/*
 * 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 junit.framework.TestCase;

import javax.tools.*;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaCompiler.CompilationTask;
import java.io.File;
import java.net.URISyntaxException;

import static java.util.Arrays.asList;
import static java.util.Locale.ENGLISH;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertThat;

/**
 * @author alexeagle@google.com (Alex Eagle)
 */
public class PreconditionsCheckNotNullTest extends TestCase {

  private File projectRoot;
  private javax.tools.JavaCompiler compiler;
  private StandardJavaFileManager fileManager;
  private DiagnosticCollector<JavaFileObject> diagnostics;

  protected void setUp() throws Exception {
    super.setUp();
    // Hacky way to find the project root. This lets us avoid adding the .java example
    // files to the classpath when running the tests, so they don't need to compile.
    String pathToFileInProject = getClass()
        .getResource("/" + this.getClass().getName().replaceAll("\\.", "/") + ".class")
        .toURI().getPath();
    projectRoot = new File(pathToFileInProject
        .substring(0, pathToFileInProject.lastIndexOf("error-prone") + "error-prone".length()));
    compiler = ToolProvider.getSystemJavaCompiler();
    fileManager = compiler.getStandardFileManager(null, null, null);
    diagnostics = new DiagnosticCollector<JavaFileObject>();
  }

  // TODO: parameterize the test so each new error type doesn't create a new test method?
  public void testErrorExpectedForPositiveCase1() throws URISyntaxException {
    errorExpectedWithCorrectLineNumber("PositiveCase1.java", 7L, 32L);
  }
  
  public void testErrorExpectedForPositiveCase2() throws URISyntaxException {
    errorExpectedWithCorrectLineNumber("PositiveCase2.java", 10L, 55L);
  }
  
  private void errorExpectedWithCorrectLineNumber(String filename, long lineNum, long colNum) {
    File exampleSource = new File(projectRoot, "error-patterns/guava/" + filename);
    assertTrue(exampleSource.exists());
    assertFalse(createCompileTask(exampleSource).call());
    boolean found = false;
    for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
      String message = diagnostic.getMessage(ENGLISH);
      System.out.println("message = " + message);
      if (diagnostic.getKind() == Kind.ERROR && message.contains("Preconditions#checkNotNull")) {
        assertThat(message, containsString("\"string literal\""));
        assertThat(diagnostic.getLineNumber(), is(lineNum));
        assertThat(diagnostic.getColumnNumber(), is(colNum));
        found = true;
        return;

      }
    }
    assertTrue(found);
  }
  
  public void testNoErrorForNegativeCase1() throws URISyntaxException {
    File exampleSource = new File(projectRoot, "error-patterns/guava/NegativeCase1.java");
    assertTrue(exampleSource.exists());
    assertTrue(createCompileTask(exampleSource).call());
    for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
      String message = diagnostic.getMessage(ENGLISH);
      if (diagnostic.getKind() == Kind.ERROR && message.contains("Preconditions#checkNotNull")) {
        fail("Error in negative case");
      }
    }
  }

  private CompilationTask createCompileTask(File exampleSource) {
    CompilationTask task = compiler
        .getTask(null, fileManager, diagnostics, null, asList(ErrorProneProcessor.class.getName()),
            fileManager.getJavaFileObjects(exampleSource));
    task.setProcessors(asList(new ErrorProneProcessor()));
    return task;
  }
}

@@ -839,16 +839,48 @@
 ception;
+%0Aimport java.net.URLClassLoader;
 %0A%0Aimport
@@ -1103,16 +1103,100 @@
 tThat;%0A%0A
+import com.google.common.base.Joiner;%0Aimport com.google.common.base.Preconditions;%0A%0A
 /**%0A * @
@@ -2800,32 +2800,83 @@
 %0A    assertTrue(
+exampleSource.getAbsolutePath() + %22 should exist%22, 
 exampleSource.ex
@@ -2900,16 +2900,39 @@
 rtFalse(
+%22Compile should fail%22, 
 createCo
@@ -3142,58 +3142,8 @@
 H);%0A
-      System.out.println(%22message = %22 + message);%0A
     
@@ -3494,16 +3494,43 @@
 ertTrue(
+%22Warning should be found%22, 
 found);%0A
@@ -3732,34 +3732,111 @@
 rce.
-exists());%0A    assertTrue(
+getAbsolutePath() + %22 should exist%22, exampleSource.exists());%0A    assertTrue(%22Compile should succeed%22, 
 crea
@@ -4326,22 +4326,193 @@
 nostics,
- null,
+%0A            asList(%22-classpath%22, Joiner.on(File.pathSeparator).join(%0A              getPath(ErrorProneProcessor.class),%0A              getPath(Preconditions.class))),%0A           
  asList(
@@ -4688,11 +4688,634 @@
 ask;%0A  %7D
+%0A%0A  private String getPath(Class clazz) %7B%0A    String classname = clazz.getName().replace('.', '/') + %22.class%22;%0A    URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();%0A    String path = classLoader.findResource(classname).getFile();%0A    int jarDelimiter = path.indexOf(%22!%22);%0A    if (jarDelimiter %3E= 0) %7B%0A      return path.substring(0, jarDelimiter);%0A    %7D else %7B%0A      int packageStart = path.indexOf(clazz.getPackage().getName().replace('.', '/'));%0A      if (packageStart %3E= 0) %7B%0A        return path.substring(0, packageStart);%0A      %7D else %7B%0A        return path;%0A      %7D%0A    %7D%0A  %7D
 %0A%7D%0A

Fix broken test on linux. The classloader in the in-process compiler is not inherited from the test runner's classloader, so our classes aren't visible. So we explicitly create a classpath to run the compiler.