Package com.google.caja.parser.quasiliteral

Source Code of com.google.caja.parser.quasiliteral.RewriterTestCase

// Copyright (C) 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.caja.parser.quasiliteral;

import com.google.caja.lexer.CharProducer;
import com.google.caja.lexer.FilePosition;
import com.google.caja.lexer.ParseException;
import com.google.caja.parser.ParseTreeNode;
import com.google.caja.parser.ParseTreeNodeContainer;

import com.google.caja.parser.js.Block;
import com.google.caja.parser.js.Declaration;
import com.google.caja.parser.js.FunctionConstructor;
import com.google.caja.parser.js.FunctionDeclaration;
import com.google.caja.parser.js.Identifier;
import com.google.caja.parser.js.Reference;
import com.google.caja.parser.js.SyntheticNodes;
import com.google.caja.parser.js.UncajoledModule;
import com.google.caja.util.CajaTestCase;
import com.google.caja.util.RhinoAsserts;
import com.google.caja.util.TestUtil;
import com.google.caja.reporting.MessageLevel;
import com.google.caja.reporting.Message;
import com.google.caja.reporting.MessageTypeInt;

import java.io.IOException;
import java.util.List;
import java.util.Arrays;

/**
* @author ihab.awad@gmail.com
*/
public abstract class RewriterTestCase extends CajaTestCase {
  protected Rewriter rewriter;

  /**
   * Given some code, execute it without rewriting and return the value of the
   * last expression in the code.
   */
  protected abstract Object executePlain(String program) throws IOException, ParseException;

  /**
   * Given some code, rewrite it, then execute it in the proper
   * context for the rewritten version and return the value of the
   * last expression in the original code.
   *
   * @param pre a prefix program fragment to be executed plain.
   * @param program a program fragment to be rewritten.
   * @param post a postfix program fragment to be executed plain.
   */
  protected abstract Object rewriteAndExecute(String pre, String program, String post)
      throws IOException, ParseException;

  protected Object rewriteAndExecute(String program) throws IOException, ParseException {
    return rewriteAndExecute(";", program, ";");
  }

  // TODO(ihab.awad): Refactor tests to use checkAddsMessage(...) instead
  protected void checkFails(String input, String error) throws Exception {
    mq.getMessages().clear();
    getRewriter().expand(new UncajoledModule(new Block(
        FilePosition.UNKNOWN, Arrays.asList(js(fromString(input, is))))));

    assertFalse(
        "Expected error, found none: " + error,
        mq.getMessages().isEmpty());

    StringBuilder messageText = new StringBuilder();
    Message firstError = null;
    for (Message m: mq.getMessages()) {
      if (m.getMessageLevel().compareTo(MessageLevel.WARNING) >= 0) {
        firstError = m;
      }
    }
    firstError.format(mc, messageText);
    assertTrue(
        "First error is not \"" + error + "\": " + messageText.toString(),
        messageText.toString().contains(error));
  }

  protected void checkSucceeds(ParseTreeNode inputNode,
                               ParseTreeNode expectedResultNode) {
    checkSucceeds(inputNode, expectedResultNode, MessageLevel.WARNING);
  }

  protected void checkSucceeds(
      ParseTreeNode inputNode,
      ParseTreeNode expectedResultNode,
      MessageLevel highest) {
    mq.getMessages().clear();
    ParseTreeNode actualResultNode = getRewriter().expand(inputNode);
    for (Message m : mq.getMessages()) {
      if (m.getMessageLevel().compareTo(highest) >= 0) {
        fail(m.toString());
      }
    }
    if (expectedResultNode != null) {
      // Test that the source code-like renderings are identical. This
      // will catch any obvious differences between expected and
      // actual.
      assertEquals(render(expectedResultNode), render(actualResultNode));
      // Then, for good measure, test that the S-expression-like
      // formatted representations are also identical. This will catch
      // any differences in tree topology that somehow do not appear
      // in the source code representation (usually due to programming
      // errors).
      assertEquals(
          TestUtil.format(expectedResultNode),
          TestUtil.format(actualResultNode));
    }
  }

  protected void assertMessageNotPresent(String src, MessageTypeInt type, MessageLevel level)
      throws Exception {
    checkDoesNotAddMessage(js(fromString(src)), type, level);
  }

  protected void assertMessageNotPresent(String src, MessageTypeInt type) throws Exception {
    checkDoesNotAddMessage(js(fromString(src)), type);
  }

  protected void checkDoesNotAddMessage(
      ParseTreeNode inputNode, MessageTypeInt type)  {
    mq.getMessages().clear();
    getRewriter().expand(inputNode);
    if (containsConsistentMessage(mq.getMessages(),type)) {
      fail("Unexpected add message of type " + type);
    }
  }

  protected void checkDoesNotAddMessage(
      ParseTreeNode inputNode, MessageTypeInt type, MessageLevel level)  {
    mq.getMessages().clear();
    getRewriter().expand(inputNode);
    if (containsConsistentMessage(mq.getMessages(),type, level)) {
      fail("Unexpected add message of type " + type + " and level " + level);
    }
  }

  protected void assertAddsMessage(String src, MessageTypeInt type, MessageLevel level)
      throws Exception {
    checkAddsMessage(js(fromString(src)), type, level);
  }

  protected void checkAddsMessage(
        ParseTreeNode inputNode,
        MessageTypeInt type)  {
    checkAddsMessage(inputNode, type, type.getLevel());
  }

  protected void checkAddsMessage(
        ParseTreeNode inputNode,
        MessageTypeInt type,
        MessageLevel level)  {
    mq.getMessages().clear();
    getRewriter().expand(inputNode);
    if (!containsConsistentMessage(mq.getMessages(), type, level)) {
      fail("Failed to add message of type " + type + " and level " + level);
    }
  }

  protected boolean containsConsistentMessage(List<Message> list, MessageTypeInt type) {
    for (Message m : list) {
      System.out.println("**" + m.getMessageType() + "|" + m.getMessageLevel());
      if (m.getMessageType().equals(type)) {
        return true;
      }
    }
    return false;
  }

  protected boolean containsConsistentMessage(
      List<Message> list, MessageTypeInt type, MessageLevel level) {
    for (Message m : list) {
      System.out.println("**" + m.getMessageType() + "|" + m.getMessageLevel());
      if (m.getMessageType().equals(type) && m.getMessageLevel() == level) {
        return true;
      }
    }
    return false;
  }

  protected void checkSucceeds(String input, String expectedResult)
      throws Exception {
    checkSucceeds(js(fromString(input)), js(fromString(expectedResult)));
  }

  protected void checkSucceeds(CharProducer cp) throws Exception {
    checkSucceeds(js(cp), null);
  }

  /**
   * Asserts that the given caja code produces the same value both cajoled and
   * uncajoled.
   *
   * @param caja executed in the context of jsUnitCore.js for its value.  The
   *    value is computed from the last statement
   */
  protected void assertConsistent(String caja)
      throws IOException, ParseException {
    assertConsistent(null, caja);
  }

  private void assertConsistent(String message, String caja)
      throws IOException, ParseException {
    Object plainResult = executePlain(caja);
    Object rewrittenResult = rewriteAndExecute(caja);

    String plainRepr = RhinoAsserts.structuralForm(plainResult);
    String rewrittenRepr = RhinoAsserts.structuralForm(rewrittenResult);
    if ("undefined".equals(plainRepr)) {
      // This usually indicates an error, such as failing to return a value.
      fail("Consistency check returned undefined");
    }

    System.err.println(
        "Results: "
        + "plain=<" + plainRepr + "> "
        + "rewritten=<" + rewrittenRepr + "> "
        + "for " + getName());
    assertEquals(message, plainRepr, rewrittenRepr);
  }

  protected final <T extends ParseTreeNode> T syntheticTree(T node) {
    for (ParseTreeNode c : node.children()) { syntheticTree(c); }
    return makeSynthetic(node);
  }

  protected final <T extends ParseTreeNode> T makeSynthetic(T node) {
    SyntheticNodes.s(node);
    return node;
  }

  protected ParseTreeNode rewriteTopLevelNode(ParseTreeNode node) {
    return getRewriter().expand(node);
  }

  protected Rewriter getRewriter() {
    return rewriter;
  }

  protected void setRewriter(Rewriter r) {
    rewriter = r;
  }

  protected ParseTreeNode emulateIE6FunctionConstructors(ParseTreeNode node) {
    final Rewriter w = new Rewriter(mq, true, false) { /* concrete */ };
    w.addRule(new Rule() {
      @Override
      @RuleDescription(
          name="blockScope",
          reason="Set up the root scope and handle block scope statements",
          synopsis="")
      public ParseTreeNode fire(ParseTreeNode node, Scope scope) {
        if (node instanceof Block) {
          Scope s2;
          if (scope == null) {
            s2 = Scope.fromProgram((Block) node, w);
          } else {
            s2 = Scope.fromPlainBlock(scope);
          }
          return QuasiBuilder.substV(
              "@startStmts*; @body*;",
              "startStmts", new ParseTreeNodeContainer(s2.getStartStatements()),
              "body", expandAll(
                  new ParseTreeNodeContainer(node.children()), s2));
        }
        return NONE;
      }
    });
    w.addRule(new Rule() {
      @Override
      @RuleDescription(
          name="fnDeclarations",
          reason="function declarations contain function constructors but don't"
              + " have the same discrepencies on IE 6 as function constructors",
          synopsis="")
      public ParseTreeNode fire(ParseTreeNode node, Scope scope) {
        if (node instanceof FunctionDeclaration) {
          FunctionDeclaration decl = ((FunctionDeclaration) node);
          FunctionConstructor ctor = decl.getInitializer();

          Scope s2 = Scope.fromFunctionConstructor(scope, ctor);
          FunctionConstructor rewritten
              = (FunctionConstructor) QuasiBuilder.substV(
                  "function @ident(@formals*) { @stmts*; @body*; }",
                  "ident", ctor.getIdentifier(),
                  "formals", expandAll(
                      new ParseTreeNodeContainer(ctor.getParams()), s2),
                  "stmts", new ParseTreeNodeContainer(s2.getStartStatements()),
                  "body", expandAll(
                      new ParseTreeNodeContainer(ctor.getBody().children()), s2)
                  );
          return new FunctionDeclaration(rewritten);
        }
        return NONE;
      }
    });
    w.addRule(new Rule() {
      @Override
      @RuleDescription(
          name="ie6functions",
          reason="simulate IE 6's broken scoping of function constructors as "
              + "described in JScript Deviations Section 2.3",
          synopsis="")
      public ParseTreeNode fire(ParseTreeNode node, Scope scope) {
        if (node instanceof FunctionConstructor) {
          FunctionConstructor ctor = (FunctionConstructor) node;
          Scope s2 = Scope.fromFunctionConstructor(scope, ctor);
          if (ctor.getIdentifierName() == null) {
            return expandAll(node, s2);
          }
          Identifier ident = ctor.getIdentifier();
          Reference identRef = new Reference(ident);
          identRef.setFilePosition(ident.getFilePosition());
          scope.addStartStatement(
              new Declaration(FilePosition.UNKNOWN, ident, identRef));
          return QuasiBuilder.substV(
              "(@var = function @ident(@formals*) { @stmts*; @body*; })",
              "var", identRef,
              "ident", ident,
              "formals", new ParseTreeNodeContainer(ctor.getParams()),
              "stmts", new ParseTreeNodeContainer(s2.getStartStatements()),
              "body", expandAll(
                  new ParseTreeNodeContainer(ctor.getBody().children()), s2)
              );
        }
        return NONE;
      }
    });
    w.addRule(new Rule() {
      @Override
      @RuleDescription(
          name="catchAll",
          reason="Handles non function constructors.",
          synopsis="")
      public ParseTreeNode fire(ParseTreeNode node, Scope scope) {
        return expandAll(node, scope);
      }
    });
    return w.expand(node);
  }
}
TOP

Related Classes of com.google.caja.parser.quasiliteral.RewriterTestCase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.