// Copyright 2014 The Bazel Authors. 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.devtools.build.lib.syntax;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
import com.google.devtools.build.lib.testutil.TestMode;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Test of evaluation behavior. (Implicitly uses lexer + parser.)
*/
@RunWith(JUnit4.class)
public class EvaluationTest extends EvaluationTestCase {
@Before
public final void setBuildMode() throws Exception {
super.setMode(TestMode.BUILD);
}
/**
* Creates a new instance of {@code ModalTestCase}.
*
*
If a test uses this method, it allows potential subclasses to run the very same test in a
* different mode in subclasses
*/
protected ModalTestCase newTest() {
return new BuildTest();
}
@Test
public void testExprs() throws Exception {
newTest()
.testStatement("'%sx' % 'foo' + 'bar1'", "fooxbar1")
.testStatement("('%sx' % 'foo') + 'bar2'", "fooxbar2")
.testStatement("'%sx' % ('foo' + 'bar3')", "foobar3x")
.testStatement("123 + 456", 579)
.testStatement("456 - 123", 333)
.testStatement("8 % 3", 2)
.testIfErrorContains("unsupported operand type(s) for %: 'int' and 'string'", "3 % 'foo'");
}
@Test
public void testListExprs() throws Exception {
newTest().testExactOrder("[1, 2, 3]", 1, 2, 3).testExactOrder("(1, 2, 3)", 1, 2, 3);
}
@Test
public void testStringFormatMultipleArgs() throws Exception {
newTest().testStatement("'%sY%s' % ('X', 'Z')", "XYZ");
}
@Test
public void testAndOr() throws Exception {
new BuildTest()
.testStatement("8 or 9", 8)
.testStatement("0 or 9", 9)
.testStatement("8 and 9", 9)
.testStatement("0 and 9", 0)
.testStatement("1 and 2 or 3", 2)
.testStatement("0 and 2 or 3", 3)
.testStatement("1 and 0 or 3", 3)
.testStatement("1 or 2 and 3", 1)
.testStatement("0 or 2 and 3", 3)
.testStatement("0 or 0 and 3", 0)
.testStatement("1 or 0 and 3", 1)
.testStatement("None and 1", Runtime.NONE)
.testStatement("\"\" or 9", 9)
.testStatement("\"abc\" or 9", "abc")
// check that 'foo' is not evaluated
.testStatement("8 or foo", 8)
.testStatement("0 and foo", 0);
new SkylarkTest()
.testIfErrorContains("name 'google' is not defined", "0 and google")
.testIfErrorContains("name 'google' is not defined", "8 or google");
}
@Test
public void testNot() throws Exception {
newTest().testStatement("not 1", false).testStatement("not ''", true);
}
@Test
public void testNotWithLogicOperators() throws Exception {
newTest()
.testStatement("not (0 and 0)", true)
.testStatement("not (1 or 0)", false)
.testStatement("0 and not 0", 0)
.testStatement("not 0 and 0", 0)
.testStatement("1 and not 0", true)
.testStatement("not 0 or 0", true)
.testStatement("not 1 or 0", 0)
.testStatement("not 1 or 1", 1);
}
@Test
public void testNotWithArithmeticOperators() throws Exception {
newTest().testStatement("not 0 + 0", true).testStatement("not 2 - 1", false);
}
@Test
public void testNotWithCollections() throws Exception {
newTest().testStatement("not []", true).testStatement("not {'a' : 1}", false);
}
@Test
public void testEquality() throws Exception {
newTest()
.testStatement("1 == 1", true)
.testStatement("1 == 2", false)
.testStatement("'hello' == 'hel' + 'lo'", true)
.testStatement("'hello' == 'bye'", false)
.testStatement("None == None", true)
.testStatement("[1, 2] == [1, 2]", true)
.testStatement("[1, 2] == [2, 1]", false);
}
@Test
public void testInequality() throws Exception {
newTest()
.testStatement("1 != 1", false)
.testStatement("1 != 2", true)
.testStatement("'hello' != 'hel' + 'lo'", false)
.testStatement("'hello' != 'bye'", true)
.testStatement("[1, 2] != [1, 2]", false)
.testStatement("[1, 2] != [2, 1]", true);
}
@Test
public void testEqualityPrecedence() throws Exception {
newTest()
.testStatement("1 + 3 == 2 + 2", true)
.testStatement("not 1 == 2", true)
.testStatement("not 1 != 2", false)
.testStatement("2 and 3 == 3 or 1", true)
.testStatement("2 or 3 == 3 and 1", 2);
}
@Test
public void testLessThan() throws Exception {
newTest()
.testStatement("1 <= 1", true)
.testStatement("1 < 1", false)
.testStatement("'a' <= 'b'", true)
.testStatement("'c' < 'a'", false);
}
@Test
public void testGreaterThan() throws Exception {
newTest()
.testStatement("1 >= 1", true)
.testStatement("1 > 1", false)
.testStatement("'a' >= 'b'", false)
.testStatement("'c' > 'a'", true);
}
@Test
public void testConditionalExpressions() throws Exception {
newTest()
.testStatement("1 if True else 2", 1)
.testStatement("1 if False else 2", 2)
.testStatement("1 + 2 if 3 + 4 else 5 + 6", 3);
setFailFast(false);
parseExpression("1 if 2");
assertContainsError(
"missing else clause in conditional expression or semicolon before if");
}
@Test
public void testListComparison() throws Exception {
newTest()
.testStatement("[] < [1]", true)
.testStatement("[1] < [1, 1]", true)
.testStatement("[1, 1] < [1, 2]", true)
.testStatement("[1, 2] < [1, 2, 3]", true)
.testStatement("[1, 2, 3] <= [1, 2, 3]", true)
.testStatement("['a', 'b'] > ['a']", true)
.testStatement("['a', 'b'] >= ['a']", true)
.testStatement("['a', 'b'] < ['a']", false)
.testStatement("['a', 'b'] <= ['a']", false)
.testStatement("('a', 'b') > ('a', 'b')", false)
.testStatement("('a', 'b') >= ('a', 'b')", true)
.testStatement("('a', 'b') < ('a', 'b')", false)
.testStatement("('a', 'b') <= ('a', 'b')", true)
.testStatement("[[1, 1]] > [[1, 1], []]", false)
.testStatement("[[1, 1]] < [[1, 1], []]", true);
}
@Test
public void testSumFunction() throws Exception {
BaseFunction sum = new BaseFunction("sum") {
@Override
public Object call(List