/*
* Copyright 2015 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.template.soy.sharedpasses.opti;
import com.google.common.collect.ImmutableMap;
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.Subject;
import com.google.common.truth.SubjectFactory;
import com.google.common.truth.Truth;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.template.soy.SoyModule;
import com.google.template.soy.base.SourceLocation;
import com.google.template.soy.exprparse.ExpressionParser;
import com.google.template.soy.exprparse.SoyParsingContext;
import com.google.template.soy.exprtree.ExprRootNode;
import com.google.template.soy.passes.ResolveFunctionsVisitor;
import com.google.template.soy.shared.restricted.SoyFunction;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Unit tests for {@link SimplifyExprVisitor}.
*
*/
@RunWith(JUnit4.class)
public final class SimplifyExprVisitorTest {
@Test
public void testSimplifyFullySimplifiableExpr() {
assertThat("-99+-111").simplifiesTo("-210");
assertThat("-99+-111").simplifiesTo("-210");
assertThat("-99 + '-111'").simplifiesTo("'-99-111'");
assertThat("false or 0 or 0.0 or ''").simplifiesTo("''");
assertThat("0 <= 0").simplifiesTo("true");
assertThat("'22' == 22").simplifiesTo("true");
assertThat("'22' == '' + 22").simplifiesTo("true");
// With functions.
assertThat("max(4, 8)").simplifiesTo("8");
assertThat("floor(7/2)").simplifiesTo("3");
}
@Test
public void testSimplifyNotSimplifiableExpr() {
assertThat("$boo").simplifiesTo("$boo");
assertThat("$boo % 3").simplifiesTo("$boo % 3");
assertThat("not $boo").simplifiesTo("not $boo");
assertThat("$boo + ''").simplifiesTo("$boo + ''");
// With functions.
assertThat("max(4, $boo)").simplifiesTo("max(4, $boo)");
assertThat("floor($boo / 3)").simplifiesTo("floor($boo / 3)");
}
@Test
public void testSimplifyPartiallySimplifiableExpr() {
assertThat("3 * 5 % $boo").simplifiesTo("15 % $boo");
assertThat("not false and not $boo").simplifiesTo("not $boo");
assertThat("'a' + 'b' + $boo").simplifiesTo("'ab' + $boo");
// With functions.
assertThat("max(max(4, 8), $boo)").simplifiesTo("max(8, $boo)");
assertThat("floor($boo / (1.0 + 2))").simplifiesTo("floor($boo / 3.0)");
}
@Test
public void testSimplifyListAndMapLiterals() {
assertThat("['a' + 'b', 1 - 3]").simplifiesTo("['ab', -2]");
assertThat("['a' + 'b': 1 - 3]").simplifiesTo("['ab': -2]");
assertThat("[8, ['a' + 'b', 1 - 3]]").simplifiesTo("[8, ['ab', -2]]");
assertThat("['z': ['a' + 'b': 1 - 3]]").simplifiesTo("['z': ['ab': -2]]");
// With functions.
// Note: Currently, ListLiteralNode and MapLiteralNode are never considered to be constant,
// even though in reality, they can be constant. So in the current implementation, this keys()
// call cannot be simplified away.
assertThat("keys(['a' + 'b': 1 - 3])").simplifiesTo("keys(['ab': -2])");
}
@Test
public void testSimplifyBinaryLogicalOps() {
// 'and'
assertThat("true and true").simplifiesTo("true");
assertThat("true and false").simplifiesTo("false");
assertThat("false and true").simplifiesTo("false");
assertThat("false and false").simplifiesTo("false");
assertThat("true and $boo").simplifiesTo("$boo");
assertThat("$boo and true").simplifiesTo("$boo and true"); // Can't simplify
assertThat("true and 1").simplifiesTo("1");
assertThat("1 and true").simplifiesTo("true");
assertThat("false and 1").simplifiesTo("false");
assertThat("1 and false").simplifiesTo("false");
assertThat("false and $boo").simplifiesTo("false");
assertThat("$boo and false").simplifiesTo("$boo and false"); // Can't simplify
// 'or'
assertThat("true or true").simplifiesTo("true");
assertThat("true or false").simplifiesTo("true");
assertThat("false or true").simplifiesTo("true");
assertThat("false or false").simplifiesTo("false");
assertThat("true or $boo").simplifiesTo("true");
assertThat("$boo or true").simplifiesTo("$boo or true"); // Can't simplify
assertThat("false or $boo").simplifiesTo("$boo");
assertThat("$boo or false").simplifiesTo("$boo or false");
assertThat("false or 1").simplifiesTo("1");
assertThat("1 or false").simplifiesTo("1");
assertThat("true or 1").simplifiesTo("true");
assertThat("1 or true").simplifiesTo("1");
}
@Test
public void testSimplifyConditionalOp() {
assertThat("true ? 111 : 222").simplifiesTo("111");
assertThat("false ? 111 : 222").simplifiesTo("222");
assertThat("true ? 111 : $boo").simplifiesTo("111");
assertThat("false ? $boo : 222").simplifiesTo("222");
assertThat("$boo or true ? $boo and false : true")
.simplifiesTo("$boo or true ? $boo and false : true"); // Can't simplify
}
// -----------------------------------------------------------------------------------------------
// Helpers.
private static final Injector INJECTOR = Guice.createInjector(new SoyModule());
private static final ImmutableMap<String, ? extends SoyFunction> SOY_FUNCTIONS =
INJECTOR.getInstance(new Key<ImmutableMap<String, ? extends SoyFunction>>() {});
private static final class SimplifySubject extends Subject<SimplifySubject, String> {
private SimplifySubject(FailureStrategy failureStrategy, String s) {
super(failureStrategy, s);
}
private void simplifiesTo(String expected) {
ExprRootNode exprRoot =
new ExprRootNode(
new ExpressionParser(actual(), SourceLocation.UNKNOWN, SoyParsingContext.exploding())
.parseExpression());
new ResolveFunctionsVisitor(SOY_FUNCTIONS).exec(exprRoot);
INJECTOR.getInstance(SimplifyExprVisitor.class).exec(exprRoot);
Truth.assertThat(exprRoot.toSourceString()).isEqualTo(expected);
}
}
private static final SubjectFactory<SimplifySubject, String> FACTORY =
new SubjectFactory<SimplifySubject, String>() {
@Override
public SimplifySubject getSubject(FailureStrategy failureStrategy, String s) {
return new SimplifySubject(failureStrategy, s);
}
};
private static SimplifySubject assertThat(String input) {
return Truth.assertAbout(FACTORY).that(input);
}
}