Skip to content

[move-function] Add tests that show that the debugger displays variables correctly given usage of the move function. #3986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lldb/test/API/lang/swift/variables/move_function/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SWIFT_SOURCES := main.swift

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
# TestSwiftMoveFunction.py
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
#
# ------------------------------------------------------------------------------
"""
Check that we properly show variables at various points of the CFG while
stepping with the move function.
"""
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
import os
import sys
import unittest2

def stderr_print(line):
sys.stderr.write(line + "\n")

class TestSwiftMoveFunctionType(TestBase):

mydir = TestBase.compute_mydir(__file__)

@skipUnlessPlatform(["macosx"])
@swiftTest
def test_swift_move_function(self):
"""Check that we properly show variables at various points of the CFG while
stepping with the move function.
"""
self.build()

self.exec_artifact = self.getBuildArtifact(self.exec_name)
self.target = self.dbg.CreateTarget(self.exec_artifact)
self.assertTrue(self.target, VALID_TARGET)

self.do_setup_breakpoints()

self.process = self.target.LaunchSimple(None, None, os.getcwd())
threads = lldbutil.get_threads_stopped_at_breakpoint(
self.process, self.breakpoints[0])
self.assertTrue(len(threads) == 1)
self.thread = threads[0]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might be able to achieve the same with

        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
            self, 'stop here', lldb.SBFileSpec('main.swift'))
       self.assertGreater(bkpt.GetNumLocations(), 6)


self.do_check_copyable_value_test()
self.do_check_copyable_var_test()
self.do_check_addressonly_value_test()
self.do_check_addressonly_var_test()
# ccf is conditional control flow
self.do_check_copyable_value_ccf_true()
self.do_check_copyable_value_ccf_false()

def setUp(self):
TestBase.setUp(self)
self.main_source = "main.swift"
self.main_source_spec = lldb.SBFileSpec(self.main_source)
self.exec_name = "a.out"

def add_breakpoints(self, name, num_breakpoints):
pattern = 'Set breakpoint {} here {}'
for i in range(num_breakpoints):
pat = pattern.format(name, i+1)
brk = self.target.BreakpointCreateBySourceRegex(
pat, self.main_source_spec)
self.assertTrue(brk.GetNumLocations() > 0, VALID_BREAKPOINT)
yield brk

def do_setup_breakpoints(self):
self.breakpoints = []

self.breakpoints.extend(
self.add_breakpoints('copyableValueTest', 3))
self.breakpoints.extend(
self.add_breakpoints('addressOnlyValueTest', 3))
self.breakpoints.extend(
self.add_breakpoints('copyableVarTest', 4))
self.breakpoints.extend(
self.add_breakpoints('addressOnlyVarTest', 4))
self.breakpoints.extend(
self.add_breakpoints('copyableValueCCFTrueTest',
5))
self.breakpoints.extend(
self.add_breakpoints('copyableValueCCFFalseTest',
3))

def do_check_copyable_value_test(self):
frame = self.thread.frames[0]
self.assertTrue(frame.IsValid(), "Couldn't get a frame.")

# We haven't defined varK yet.
varK = frame.FindVariable('k')

self.assertTrue(varK.value == None, "varK initialized too early?!")

# Go to break point 2. k should be valid.
self.runCmd('continue')
self.assertTrue(varK.unsigned > 0, "varK not initialized?!")

# Go to breakpoint 3. k should no longer be valid.
self.runCmd('continue')
self.assertTrue(varK.value == None, "K is live but was moved?!")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.assertEqual() will print the value the the comparison fails for easier debugging.

see lldb/third_party/Python/module/unittest2/unittest2/case.py for a full list


# Run so we hit the next breakpoint to jump to the next test's
# breakpoint.
self.runCmd('continue')

def do_check_copyable_var_test(self):
frame = self.thread.frames[0]
self.assertTrue(frame.IsValid(), "Couldn't get a frame.")

# We haven't defined varK yet.
varK = frame.FindVariable('k')
self.assertTrue(varK.value == None, "varK initialized too early?!")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for the tests!

Note there are many specific assertion functions, including assertIsNone, assertEqual, assertGreater, etc. They help produce better error messages if a test does fail.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I'll send a follow on commit. I also noticed an fr v that snuck in there that is not necessary.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to create work, I mean only to mention it for future.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kastiglione I had other things I needed to do and it inspired me to port some tests.


# Go to break point 2. k should be valid.
self.runCmd('continue')
self.assertTrue(varK.unsigned > 0, "varK not initialized?!")

# Go to breakpoint 3. We invalidated k
self.runCmd('continue')
self.assertTrue(varK.value == None, "K is live but was moved?!")

# Go to the last breakpoint and make sure that k is reinitialized
# properly.
self.runCmd('continue')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you store the process above, you could do process.Continue() here, which is slightly nicer than parsing a string command.

self.assertTrue(varK.unsigned > 0, "varK not initialized")

# Run so we hit the next breakpoint to go to the next test.
self.runCmd('continue')

def do_check_addressonly_value_test(self):
frame = self.thread.frames[0]
self.assertTrue(frame.IsValid(), "Couldn't get a frame.")

# We haven't defined varK or varM yet... so we shouldn't have a summary.
varK = frame.FindVariable('k')

# Go to break point 2. k should be valid and m should not be. Since M is
# a dbg.declare it is hard to test robustly that it is not initialized
# so we don't do so. We have an additional llvm.dbg.addr test where we
# move the other variable and show the correct behavior with
# llvm.dbg.declare.
self.runCmd('continue')

self.assertTrue(varK.unsigned > 0, "var not initialized?!")

# Go to breakpoint 3.
self.runCmd('continue')
self.assertTrue(varK.unsigned == 0,
"dbg thinks varK is live despite move?!")

# Run so we hit the next breakpoint as part of the next test.
self.runCmd('continue')

def do_check_addressonly_var_test(self):
frame = self.thread.frames[0]
self.assertTrue(frame.IsValid(), "Couldn't get a frame.")

varK = frame.FindVariable('k')

# Go to break point 2. k should be valid.
self.runCmd('continue')
self.assertTrue(varK.unsigned > 0, "varK not initialized?!")

# Go to breakpoint 3. K was invalidated.
self.runCmd('continue')
self.assertTrue(varK.value == None, "K is live but was moved?!")

# Go to the last breakpoint and make sure that k is reinitialized
# properly.
self.runCmd('continue')
self.assertTrue(varK.unsigned > 0, "varK not initialized")

# Run so we hit the next breakpoint as part of the next test.
self.runCmd('continue')

def do_check_copyable_value_ccf_true(self):
frame = self.thread.frames[0]
self.assertTrue(frame.IsValid(), "Couldn't get a frame.")
varK = frame.FindVariable('k')

# Check at our start point that we do not have any state for varK and
# then continue to our next breakpoint.
self.assertTrue(varK.value == None, "varK should not have a value?!")
self.runCmd('continue')

# At this breakpoint, k should be defined since we are going to do
# something with it.
self.assertTrue(varK.value != None, "varK should have a value?!")
self.runCmd('continue')

# At this breakpoint, we are now in the conditional control flow part of
# the loop. Make sure that we can see k still.
self.assertTrue(varK.value != None, "varK should have a value?!")
self.runCmd('continue')

# Ok, we just performed the move. k should not be no longer initialized.
self.runCmd('fr v')
self.assertTrue(varK.value == None, "varK should not have a value?!")
self.runCmd('continue')

# Finally we left the conditional control flow part of the function. k
# should still be None.
self.assertTrue(varK.value == None, "varK should not have a value!")

def do_check_copyable_value_ccf_false(self):
pass
107 changes: 107 additions & 0 deletions lldb/test/API/lang/swift/variables/move_function/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// main.swift
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
// -----------------------------------------------------------------------------

public class Klass {
public func doSomething() {}
}

public protocol P {
static var value: P { get }
func doSomething()
}

extension Klass : P {
public static var value: P { Klass() }
}

var trueBoolValue : Bool { true }
var falseBoolValue : Bool { false }

//////////////////
// Simple Tests //
//////////////////

public func copyableValueTest() {
print("stop here") // Set breakpoint copyableValueTest here 1
let k = Klass()
k.doSomething()
let m = _move(k) // Set breakpoint copyableValueTest here 2
m.doSomething() // Set breakpoint copyableValueTest here 3
}

public func copyableVarTest() {
print("stop here") // Set breakpoint copyableVarTest here 1
var k = Klass()
k.doSomething()
let m = _move(k) // Set breakpoint copyableVarTest here 2
m.doSomething()
k = Klass() // Set breakpoint copyableVarTest here 3
k.doSomething() // Set breakpoint copyableVarTest here 4
print("stop here")
}

public func addressOnlyValueTest<T : P>(_ x: T) {
print("stop here") // Set breakpoint addressOnlyValueTest here 1
let k = x
k.doSomething()
let m = _move(k) // Set breakpoint addressOnlyValueTest here 2
m.doSomething() // Set breakpoint addressOnlyValueTest here 3
}

public func addressOnlyVarTest<T : P>(_ x: T) {
print("stop here") // Set breakpoint addressOnlyVarTest here 1
var k = x
k.doSomething()
let m = _move(k) // Set breakpoint addressOnlyVarTest here 2
m.doSomething()
k = x // Set breakpoint addressOnlyVarTest here 3
k.doSomething() // Set breakpoint addressOnlyVarTest here 4
}

////////////////////////////////////
// Conditional Control Flow Tests //
////////////////////////////////////

public func copyableValueCCFTrueTest() {
let k = Klass() // Set breakpoint copyableValueCCFTrueTest here 1
k.doSomething() // Set breakpoint copyableValueCCFTrueTest here 2
if trueBoolValue {
let m = _move(k) // Set breakpoint copyableValueCCFTrueTest here 3
m.doSomething() // Set breakpoint copyableValueCCFTrueTest here 4
}
// Set breakpoint copyableValueCCFTrueTest here 5
}

public func copyableValueCCFFalseTest() {
let k = Klass() // Set breakpoint copyableValueCCFFalseTest here 1
k.doSomething()
if falseBoolValue { // Set breakpoint copyableValueCCFFalseTest here 2
let m = _move(k)
m.doSomething()
}
// Set breakpoint copyableValueCCFFalseTest here 3
}

//////////////////////////
// Top Level Entrypoint //
//////////////////////////

func main() {
copyableValueTest()
copyableVarTest()
addressOnlyValueTest(Klass())
addressOnlyVarTest(Klass())
copyableValueCCFTrueTest()
copyableValueCCFFalseTest()
}

main()