blob: 358c897efead7f489768cbb420fcf676a3090855 [file] [log] [blame]
// 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 com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
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.StarlarkThread.LexicalFrame;
import com.google.devtools.build.lib.syntax.StarlarkThread.ReadyToPause;
import com.google.devtools.build.lib.syntax.StarlarkThread.Stepping;
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 StarlarkThread}s implementation of {@link StarlarkThread}. */
@RunWith(JUnit4.class)
public class StarlarkThreadDebuggingTest {
private static StarlarkThread newStarlarkThread() {
Mutability mutability = Mutability.create("test");
return StarlarkThread.builder(mutability).useDefaultSemantics().build();
}
/** Enter a dummy function scope with the given name, and the current environment's globals. */
private static void enterFunctionScope(
StarlarkThread thread, String functionName, Location location) {
FuncallExpression ast = new FuncallExpression(Identifier.of("test"), ImmutableList.of());
ast.setLocation(location);
thread.enterScope(
new BaseFunction(FunctionSignature.ANY) {
@Override
public String getName() {
return functionName;
}
},
LexicalFrame.create(thread.mutability()),
ast,
thread.getGlobals());
}
@Test
public void testListFramesFromGlobalFrame() throws Exception {
StarlarkThread thread = newStarlarkThread();
thread.update("a", 1);
thread.update("b", 2);
thread.update("c", 3);
ImmutableList<DebugFrame> frames = thread.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 {
StarlarkThread thread = newStarlarkThread();
thread.update("a", 1);
thread.update("b", 2);
thread.update("c", 3);
Location funcallLocation =
Location.fromPathAndStartColumn(
PathFragment.create("foo/bar"), 0, 0, new LineAndColumn(12, 0));
enterFunctionScope(thread, "function", funcallLocation);
thread.update("a", 4); // shadow parent frame var
thread.update("y", 5);
thread.update("z", 6);
ImmutableList<DebugFrame> frames = thread.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() {
StarlarkThread thread = newStarlarkThread();
ReadyToPause predicate = thread.stepControl(Stepping.INTO);
enterFunctionScope(thread, "function", Location.BUILTIN);
assertThat(predicate.test(thread)).isTrue();
}
@Test
public void testStepIntoFallsBackToStepOver() {
// test that when stepping into, we'll fall back to stopping at the next statement in the
// current frame
StarlarkThread thread = newStarlarkThread();
ReadyToPause predicate = thread.stepControl(Stepping.INTO);
assertThat(predicate.test(thread)).isTrue();
}
@Test
public void testStepIntoFallsBackToStepOut() {
// test that when stepping into, we'll fall back to stopping when exiting the current frame
StarlarkThread thread = newStarlarkThread();
enterFunctionScope(thread, "function", Location.BUILTIN);
ReadyToPause predicate = thread.stepControl(Stepping.INTO);
thread.exitScope();
assertThat(predicate.test(thread)).isTrue();
}
@Test
public void testStepOverFunction() {
StarlarkThread thread = newStarlarkThread();
ReadyToPause predicate = thread.stepControl(Stepping.OVER);
enterFunctionScope(thread, "function", Location.BUILTIN);
assertThat(predicate.test(thread)).isFalse();
thread.exitScope();
assertThat(predicate.test(thread)).isTrue();
}
@Test
public void testStepOverFallsBackToStepOut() {
// test that when stepping over, we'll fall back to stopping when exiting the current frame
StarlarkThread thread = newStarlarkThread();
enterFunctionScope(thread, "function", Location.BUILTIN);
ReadyToPause predicate = thread.stepControl(Stepping.OVER);
thread.exitScope();
assertThat(predicate.test(thread)).isTrue();
}
@Test
public void testStepOutOfInnerFrame() {
StarlarkThread thread = newStarlarkThread();
enterFunctionScope(thread, "function", Location.BUILTIN);
ReadyToPause predicate = thread.stepControl(Stepping.OUT);
assertThat(predicate.test(thread)).isFalse();
thread.exitScope();
assertThat(predicate.test(thread)).isTrue();
}
@Test
public void testStepOutOfOutermostFrame() {
StarlarkThread thread = newStarlarkThread();
assertThat(thread.stepControl(Stepping.OUT)).isNull();
}
@Test
public void testStepControlWithNoSteppingReturnsNull() {
StarlarkThread thread = newStarlarkThread();
assertThat(thread.stepControl(Stepping.NONE)).isNull();
}
@Test
public void testEvaluateVariableInScope() throws Exception {
StarlarkThread thread = newStarlarkThread();
thread.update("a", 1);
Object a = thread.debugEval(Expression.parse(ParserInput.fromLines("a")));
assertThat(a).isEqualTo(1);
}
@Test
public void testEvaluateVariableNotInScopeFails() throws Exception {
StarlarkThread thread = newStarlarkThread();
thread.update("a", 1);
EvalException e =
assertThrows(
EvalException.class,
() -> thread.debugEval(Expression.parse(ParserInput.fromLines("b"))));
assertThat(e).hasMessageThat().isEqualTo("name 'b' is not defined");
}
@Test
public void testEvaluateExpressionOnVariableInScope() throws Exception {
StarlarkThread thread = newStarlarkThread();
thread.update("a", "string");
assertThat(thread.debugEval(Expression.parse(ParserInput.fromLines("a.startswith('str')"))))
.isEqualTo(true);
EvalUtils.exec(
EvalUtils.parseAndValidateSkylark(ParserInput.fromLines("a = 1"), thread), thread);
assertThat(thread.debugEval(Expression.parse(ParserInput.fromLines("a")))).isEqualTo(1);
}
}