Lukacs Berki | 14328eb | 2015-10-21 10:47:26 +0000 | [diff] [blame] | 1 | // Copyright 2006 The Bazel Authors. All Rights Reserved. |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package com.google.devtools.build.lib.syntax; |
| 16 | |
Ulf Adams | 795895a | 2015-03-06 15:58:35 +0000 | [diff] [blame] | 17 | import static com.google.common.truth.Truth.assertThat; |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 18 | import static org.junit.Assert.assertEquals; |
| 19 | import static org.junit.Assert.fail; |
| 20 | |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 21 | import com.google.common.collect.Sets; |
Han-Wen Nienhuys | ceae8c5 | 2015-09-22 16:24:45 +0000 | [diff] [blame] | 22 | import com.google.devtools.build.lib.syntax.util.EvaluationTestCase; |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 23 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 24 | import org.junit.Test; |
| 25 | import org.junit.runner.RunWith; |
| 26 | import org.junit.runners.JUnit4; |
| 27 | |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 28 | /** |
| 29 | * Tests of Environment. |
| 30 | */ |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 31 | @RunWith(JUnit4.class) |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 32 | public class EnvironmentTest extends EvaluationTestCase { |
| 33 | |
| 34 | @Override |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 35 | public Environment newEnvironment() { |
| 36 | return newBuildEnvironment(); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 37 | } |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 38 | |
| 39 | // Test the API directly |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 40 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 41 | public void testLookupAndUpdate() throws Exception { |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 42 | try { |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 43 | lookup("foo"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 44 | fail(); |
| 45 | } catch (Environment.NoSuchVariableException e) { |
Ulf Adams | 795895a | 2015-03-06 15:58:35 +0000 | [diff] [blame] | 46 | assertThat(e).hasMessage("no such variable: foo"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 47 | } |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 48 | update("foo", "bar"); |
| 49 | assertEquals("bar", lookup("foo")); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 50 | } |
| 51 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 52 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 53 | public void testLookupWithDefault() throws Exception { |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 54 | assertEquals(21, getEnvironment().lookup("VERSION", 21)); |
| 55 | update("VERSION", 42); |
| 56 | assertEquals(42, getEnvironment().lookup("VERSION", 21)); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 57 | } |
| 58 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 59 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 60 | public void testDoubleUpdateSucceeds() throws Exception { |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 61 | update("VERSION", 42); |
| 62 | assertEquals(42, lookup("VERSION")); |
| 63 | update("VERSION", 43); |
| 64 | assertEquals(43, lookup("VERSION")); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 65 | } |
| 66 | |
| 67 | // Test assign through interpreter, lookup through API: |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 68 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 69 | public void testAssign() throws Exception { |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 70 | try { |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 71 | lookup("foo"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 72 | fail(); |
| 73 | } catch (Environment.NoSuchVariableException e) { |
Ulf Adams | 795895a | 2015-03-06 15:58:35 +0000 | [diff] [blame] | 74 | assertThat(e).hasMessage("no such variable: foo"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 75 | } |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 76 | eval("foo = 'bar'"); |
| 77 | assertEquals("bar", lookup("foo")); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 78 | } |
| 79 | |
| 80 | // Test update through API, reference through interpreter: |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 81 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 82 | public void testReference() throws Exception { |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 83 | try { |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 84 | eval("foo"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 85 | fail(); |
| 86 | } catch (EvalException e) { |
Ulf Adams | 795895a | 2015-03-06 15:58:35 +0000 | [diff] [blame] | 87 | assertThat(e).hasMessage("name 'foo' is not defined"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 88 | } |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 89 | update("foo", "bar"); |
| 90 | assertEquals("bar", eval("foo")); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | // Test assign and reference through interpreter: |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 94 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 95 | public void testAssignAndReference() throws Exception { |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 96 | try { |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 97 | eval("foo"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 98 | fail(); |
| 99 | } catch (EvalException e) { |
Ulf Adams | 795895a | 2015-03-06 15:58:35 +0000 | [diff] [blame] | 100 | assertThat(e).hasMessage("name 'foo' is not defined"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 101 | } |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 102 | eval("foo = 'bar'"); |
| 103 | assertEquals("bar", eval("foo")); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 104 | } |
| 105 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 106 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 107 | public void testGetVariableNames() throws Exception { |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 108 | Environment outerEnv; |
| 109 | Environment innerEnv; |
| 110 | try (Mutability mut = Mutability.create("outer")) { |
| 111 | outerEnv = Environment.builder(mut) |
| 112 | .setGlobals(Environment.BUILD).build() |
| 113 | .update("foo", "bar") |
| 114 | .update("wiz", 3); |
| 115 | } |
| 116 | try (Mutability mut = Mutability.create("inner")) { |
| 117 | innerEnv = Environment.builder(mut) |
| 118 | .setGlobals(outerEnv.getGlobals()).build() |
| 119 | .update("foo", "bat") |
| 120 | .update("quux", 42); |
| 121 | } |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 122 | |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 123 | assertEquals(Sets.newHashSet("foo", "wiz", |
| 124 | "False", "None", "True", |
| 125 | "-", "bool", "dict", "enumerate", "int", "len", "list", |
Florian Weikert | d5e3350 | 2015-12-14 12:06:10 +0000 | [diff] [blame] | 126 | "range", "repr", "reversed", "select", "sorted", "str", "zip"), |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 127 | outerEnv.getVariableNames()); |
| 128 | assertEquals(Sets.newHashSet("foo", "wiz", "quux", |
| 129 | "False", "None", "True", |
| 130 | "-", "bool", "dict", "enumerate", "int", "len", "list", |
Florian Weikert | d5e3350 | 2015-12-14 12:06:10 +0000 | [diff] [blame] | 131 | "range", "repr", "reversed", "select", "sorted", "str", "zip"), |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 132 | innerEnv.getVariableNames()); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 133 | } |
| 134 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 135 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 136 | public void testToString() throws Exception { |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 137 | update("subject", new StringLiteral("Hello, 'world'.", '\'')); |
| 138 | update("from", new StringLiteral("Java", '"')); |
Francois-Rene Rideau | bd0c7bb | 2015-09-21 16:54:19 +0000 | [diff] [blame] | 139 | assertThat(getEnvironment().toString()).isEqualTo("<Environment[test]>"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 140 | } |
| 141 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 142 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 143 | public void testBindToNullThrowsException() throws Exception { |
| 144 | try { |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 145 | update("some_name", null); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 146 | fail(); |
| 147 | } catch (NullPointerException e) { |
Ulf Adams | 795895a | 2015-03-06 15:58:35 +0000 | [diff] [blame] | 148 | assertThat(e).hasMessage("update(value == null)"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 149 | } |
| 150 | } |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 151 | |
| 152 | @Test |
| 153 | public void testFrozen() throws Exception { |
| 154 | Environment env; |
| 155 | try (Mutability mutability = Mutability.create("testFrozen")) { |
| 156 | env = Environment.builder(mutability) |
| 157 | .setGlobals(Environment.BUILD).setEventHandler(Environment.FAIL_FAST_HANDLER).build(); |
| 158 | env.update("x", 1); |
| 159 | assertEquals(env.lookup("x"), 1); |
| 160 | env.update("y", 2); |
| 161 | assertEquals(env.lookup("y"), 2); |
| 162 | assertEquals(env.lookup("x"), 1); |
| 163 | env.update("x", 3); |
| 164 | assertEquals(env.lookup("x"), 3); |
| 165 | } |
| 166 | try { |
| 167 | // This update to an existing variable should fail because the environment was frozen. |
| 168 | env.update("x", 4); |
| 169 | throw new Exception("failed to fail"); // not an AssertionError like fail() |
| 170 | } catch (AssertionError e) { |
| 171 | assertThat(e).hasMessage("Can't update x to 4 in frozen environment"); |
| 172 | } |
| 173 | try { |
| 174 | // This update to a new variable should also fail because the environment was frozen. |
| 175 | env.update("newvar", 5); |
| 176 | throw new Exception("failed to fail"); // not an AssertionError like fail() |
| 177 | } catch (AssertionError e) { |
| 178 | assertThat(e).hasMessage("Can't update newvar to 5 in frozen environment"); |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | @Test |
| 183 | public void testReadOnly() throws Exception { |
| 184 | Environment env = newSkylarkEnvironment() |
| 185 | .setup("special_var", 42) |
| 186 | .update("global_var", 666); |
| 187 | |
| 188 | // We don't even get a runtime exception trying to modify these, |
| 189 | // because we get compile-time exceptions even before we reach runtime! |
| 190 | try { |
| 191 | env.eval("special_var = 41"); |
| 192 | throw new AssertionError("failed to fail"); |
| 193 | } catch (IllegalArgumentException e) { |
| 194 | assertThat(e).hasMessage("ERROR 1:1: Variable special_var is read only"); |
| 195 | } |
| 196 | |
| 197 | try { |
| 198 | env.eval("def foo(x): x += global_var; global_var = 36; return x", "foo(1)"); |
| 199 | throw new AssertionError("failed to fail"); |
| 200 | } catch (EvalExceptionWithStackTrace e) { |
| 201 | assertThat(e.getMessage()).contains("Variable 'global_var' is referenced before assignment. " |
| 202 | + "The variable is defined in the global scope."); |
| 203 | } |
| 204 | } |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 205 | } |