/*
 * Copyright 2008 Google Inc.
 *
 * 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.gwt.dev.jjs.impl.codesplitter;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.impl.JJSTestBase;
import com.google.gwt.dev.util.UnitTestTreeLogger;

/**
 * Tests the error messages generated by {@link ReplaceRunAsyncs}.
 */
public class ReplaceRunAsyncsErrorMessagesTest extends JJSTestBase {
  private UnitTestTreeLogger.Builder testLoggerBuilder;

  @Override
  public void setUp() {
    addCommonTestCode();
    initializeTestLoggerBuilder();
  }

  public void testAmbiguousClassLiteral() {
    sourceOracle.addOrReplace(new MockJavaResource("test.SplitPoint3") {
      @Override
      public CharSequence getContent() {
        StringBuilder code = new StringBuilder();
        code.append("package test;\n");
        code.append("import com.google.gwt.core.client.GWT;\n");
        code.append("public class SplitPoint3 {\n");
        code.append("  void doStuff() {\n");
        // Intentionally reuse SplitPoint1.class
        code.append("    GWT.runAsync(SplitPoint1.class, null);\n");
        code.append("  }\n");
        code.append("}\n");
        return code;
      }
    });

    addSnippetImport("test.SplitPoint3");
    expectError("Errors in 'test/EntryPoint.java'");
    expectError("Line 8: Multiple runAsync calls are named test.SplitPoint1");
    expectError("One call is at 'test/SplitPoint1.java:5'");
    expectError("One call is at 'test/SplitPoint3.java:5'");
    testSnippet("RunAsyncCode.runAsyncCode(SplitPoint1.class);");
  }

  public void testMissingOnSuccess() {
    sourceOracle.addOrReplace(new MockJavaResource("test.SplitPoint4") {
        @Override
      public CharSequence getContent() {
        StringBuilder code = new StringBuilder();
        code.append("package test;\n");
        code.append("import com.google.gwt.core.client.GWT;\n");
        code.append("import com.google.gwt.core.client.RunAsyncCallback;\n");
        code.append("public class SplitPoint4 {\n");
        code.append(
            "  public abstract class AbstractRunAsyncCallback implements RunAsyncCallback {\n");
        code.append("    public void run() {\n");
        code.append("      GWT.runAsync(this);\n");
        code.append("    }\n");
        code.append("  }\n");
        code.append("}\n");
        return code;
      }
    });

    addSnippetImport("test.SplitPoint4");
    expectError("Errors in 'test/SplitPoint4.java'");
    expectError("Line 7: Only a RunAsyncCallback with a defined onSuccess() can "
        + "be passed to runAsync().");
    testSnippet("new SplitPoint4();");
  }

  public void testNonClassLiteral() {
    expectError("Errors in 'test/EntryPoint.java'");
    expectError("Line 7: Only a class literal may be passed to runAsyncCode");
    testSnippet("RunAsyncCode.runAsyncCode(new SplitPoint1().getClass());");
  }

  public void testNonExistentSplitPoint() {
    expectError("Errors in 'test/EntryPoint.java'");
    expectError("Line 7: No runAsync call is named java.lang.String");
    testSnippet("RunAsyncCode.runAsyncCode(String.class);");
  }

  private void addCommonTestCode() {
    addSnippetImport("com.google.gwt.core.client.prefetch.RunAsyncCode");

    sourceOracle.addOrReplace(new MockJavaResource("test.SplitPoint1") {
      @Override
      public CharSequence getContent() {
        StringBuilder code = new StringBuilder();
        code.append("package test;\n");
        code.append("import com.google.gwt.core.client.GWT;\n");
        code.append("public class SplitPoint1 {\n");
        code.append("  void doStuff() {\n");
        code.append("    GWT.runAsync(SplitPoint1.class, null);\n");
        code.append("  }\n");
        code.append("}\n");
        return code;
      }
    });
    addSnippetImport("test.SplitPoint1");

    sourceOracle.addOrReplace(new MockJavaResource("test.SplitPoint2") {
      @Override
      public CharSequence getContent() {
        StringBuilder code = new StringBuilder();
        code.append("package test;\n");
        code.append("import com.google.gwt.core.client.GWT;\n");
        code.append("public class SplitPoint2 {\n");
        code.append("  void doStuff() {\n");
        code.append("    GWT.runAsync(SplitPoint2.class, null);\n");
        code.append("  }\n");
        code.append("}\n");
        return code;
      }
    });
    addSnippetImport("test.SplitPoint2");
  }

  private void expectError(String msg) {
    testLoggerBuilder.expectError(msg, null);
  }

  private void initializeTestLoggerBuilder() {
    testLoggerBuilder = new UnitTestTreeLogger.Builder();
    testLoggerBuilder.setLowestLogLevel(TreeLogger.ERROR);
  }

  private void testSnippet(String codeSnippet) {
    UnitTestTreeLogger testLogger = testLoggerBuilder.createLogger();
    logger = testLogger;

    try {
      JProgram program = compileSnippet("void", codeSnippet, true);
      ReplaceRunAsyncs.exec(logger, program);
      fail("Expected a compile error");
    } catch (UnableToCompleteException e) {
      // expected
    }

    testLogger.assertCorrectLogEntries();
  }
}
