diff options
author | 2018-05-23 12:32:07 -0700 | |
---|---|---|
committer | 2018-05-23 12:33:20 -0700 | |
commit | 29eafdfe329b300dc42fddafde87bddae2f07a4c (patch) | |
tree | 7b20b5796aeb5c32ce66c3160ebdb8f56d32106e /src/test/java/com | |
parent | 3e951fcb946b9f8efdef7a84a2fb0fe03ede010e (diff) |
Initial implementation of a Skylark debug server API.
I've pulled out the API for separate review. It includes all
hooks from blaze/skylark used by the debugger.
Debuggable thread contexts are currently declared in 3 places:
- BuildFileAST (top-level evaluation of BUILD files)
- SkylarkRuleConfiguredTargetUtil (rules)
- SkylarkAspectFactory (aspects)
The purpose of declaring these contexts is so that the debugger
can track currently-active threads (and stop tracking them when
the task is completed).
Details of the actual debugging server are in unknown commit.
PiperOrigin-RevId: 197770547
Diffstat (limited to 'src/test/java/com')
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/syntax/EnvironmentDebuggingTest.java | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentDebuggingTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentDebuggingTest.java new file mode 100644 index 0000000000..1e1add2620 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentDebuggingTest.java @@ -0,0 +1,220 @@ +// Copyright 2018 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.fail; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.events.Location; +import com.google.devtools.build.lib.events.Location.LineAndColumn; +import com.google.devtools.build.lib.syntax.Debuggable.ReadyToPause; +import com.google.devtools.build.lib.syntax.Debuggable.Stepping; +import com.google.devtools.build.lib.syntax.Environment.LexicalFrame; +import com.google.devtools.build.lib.vfs.PathFragment; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests of {@link Environment}s implementation of {@link Debuggable}. */ +@RunWith(JUnit4.class) +public class EnvironmentDebuggingTest { + + private static Environment newEnvironment() { + Mutability mutability = Mutability.create("test"); + return Environment.builder(mutability).useDefaultSemantics().build(); + } + + /** Enter a dummy function scope with the given name, and the current environment's globals. */ + private static void enterFunctionScope(Environment env, String functionName, Location location) { + FuncallExpression ast = new FuncallExpression(new Identifier("test"), ImmutableList.of()); + ast.setLocation(location); + env.enterScope( + new BaseFunction(functionName) {}, + LexicalFrame.create(env.mutability()), + ast, + env.getGlobals()); + } + + @Test + public void testListFramesFromGlobalFrame() throws Exception { + Environment env = newEnvironment(); + env.update("a", 1); + env.update("b", 2); + env.update("c", 3); + + ImmutableList<DebugFrame> frames = env.listFrames(Location.BUILTIN); + + assertThat(frames).hasSize(1); + assertThat(frames.get(0)) + .isEqualTo( + DebugFrame.builder() + .setFunctionName("<top level>") + .setLocation(Location.BUILTIN) + .setGlobalBindings(ImmutableMap.of("a", 1, "b", 2, "c", 3)) + .build()); + } + + @Test + public void testListFramesFromChildFrame() throws Exception { + Environment env = newEnvironment(); + env.update("a", 1); + env.update("b", 2); + env.update("c", 3); + Location funcallLocation = + Location.fromPathAndStartColumn( + PathFragment.create("foo/bar"), 0, 0, new LineAndColumn(12, 0)); + enterFunctionScope(env, "function", funcallLocation); + env.update("a", 4); // shadow parent frame var + env.update("y", 5); + env.update("z", 6); + + ImmutableList<DebugFrame> frames = env.listFrames(Location.BUILTIN); + + assertThat(frames).hasSize(2); + assertThat(frames.get(0)) + .isEqualTo( + DebugFrame.builder() + .setFunctionName("function") + .setLocation(Location.BUILTIN) + .setLexicalFrameBindings(ImmutableMap.of("a", 4, "y", 5, "z", 6)) + .setGlobalBindings(ImmutableMap.of("a", 1, "b", 2, "c", 3)) + .build()); + assertThat(frames.get(1)) + .isEqualTo( + DebugFrame.builder() + .setFunctionName("<top level>") + .setLocation(funcallLocation) + .setGlobalBindings(ImmutableMap.of("a", 1, "b", 2, "c", 3)) + .build()); + } + + @Test + public void testStepIntoFunction() { + Environment env = newEnvironment(); + + ReadyToPause predicate = env.stepControl(Stepping.INTO); + enterFunctionScope(env, "function", Location.BUILTIN); + + assertThat(predicate.test(env)).isTrue(); + } + + @Test + public void testStepIntoFallsBackToStepOver() { + // test that when stepping into, we'll fall back to stopping at the next statement in the + // current frame + Environment env = newEnvironment(); + + ReadyToPause predicate = env.stepControl(Stepping.INTO); + + assertThat(predicate.test(env)).isTrue(); + } + + @Test + public void testStepIntoFallsBackToStepOut() { + // test that when stepping into, we'll fall back to stopping when exiting the current frame + Environment env = newEnvironment(); + enterFunctionScope(env, "function", Location.BUILTIN); + + ReadyToPause predicate = env.stepControl(Stepping.INTO); + env.exitScope(); + + assertThat(predicate.test(env)).isTrue(); + } + + @Test + public void testStepOverFunction() { + Environment env = newEnvironment(); + + ReadyToPause predicate = env.stepControl(Stepping.OVER); + enterFunctionScope(env, "function", Location.BUILTIN); + + assertThat(predicate.test(env)).isFalse(); + env.exitScope(); + assertThat(predicate.test(env)).isTrue(); + } + + @Test + public void testStepOverFallsBackToStepOut() { + // test that when stepping over, we'll fall back to stopping when exiting the current frame + Environment env = newEnvironment(); + enterFunctionScope(env, "function", Location.BUILTIN); + + ReadyToPause predicate = env.stepControl(Stepping.OVER); + env.exitScope(); + + assertThat(predicate.test(env)).isTrue(); + } + + @Test + public void testStepOutOfInnerFrame() { + Environment env = newEnvironment(); + enterFunctionScope(env, "function", Location.BUILTIN); + + ReadyToPause predicate = env.stepControl(Stepping.OUT); + + assertThat(predicate.test(env)).isFalse(); + env.exitScope(); + assertThat(predicate.test(env)).isTrue(); + } + + @Test + public void testStepOutOfOutermostFrame() { + Environment env = newEnvironment(); + + assertThat(env.stepControl(Stepping.OUT)).isNull(); + } + + @Test + public void testStepControlWithNoSteppingReturnsNull() { + Environment env = newEnvironment(); + + assertThat(env.stepControl(Stepping.NONE)).isNull(); + } + + @Test + public void testEvaluateVariableInScope() throws Exception { + Environment env = newEnvironment(); + env.update("a", 1); + + Object result = env.evaluate("a"); + + assertThat(result).isEqualTo(1); + } + + @Test + public void testEvaluateVariableNotInScopeFails() throws Exception { + Environment env = newEnvironment(); + env.update("a", 1); + + try { + env.evaluate("b"); + fail(); + } catch (EvalException e) { + assertThat(e).hasMessageThat().isEqualTo("name 'b' is not defined"); + } + } + + @Test + public void testEvaluateExpressionOnVariableInScope() throws Exception { + Environment env = newEnvironment(); + env.update("a", "string"); + + Object result = env.evaluate("a.startswith(\"str\")"); + + assertThat(result).isEqualTo(Boolean.TRUE); + } +} |