Add missing .gitmirrorall file
authorMatt Ellis <matell@microsoft.com>
Wed, 18 May 2016 06:39:18 +0000 (23:39 -0700)
committerMatt Ellis <matell@microsoft.com>
Wed, 18 May 2016 06:39:18 +0000 (23:39 -0700)
[tfs-changeset: 1605815]

Commit migrated from https://github.com/dotnet/coreclr/commit/103b370789ed107639e77b2e47028e1621243d12

src/coreclr/src/ToolBox/SOS/tests/.gitmirrorall [new file with mode: 0644]
src/coreclr/src/ToolBox/SOS/tests/OnCrash.do [new file with mode: 0644]
src/coreclr/src/ToolBox/SOS/tests/README.md [new file with mode: 0644]
src/coreclr/src/ToolBox/SOS/tests/dumpil.py [new file with mode: 0644]
src/coreclr/src/ToolBox/SOS/tests/dumpmodule.py [new file with mode: 0644]
src/coreclr/src/ToolBox/SOS/tests/runprocess.py [new file with mode: 0644]
src/coreclr/src/ToolBox/SOS/tests/test_libsosplugin.py [new file with mode: 0644]
src/coreclr/src/ToolBox/SOS/tests/testutils.py [new file with mode: 0644]

diff --git a/src/coreclr/src/ToolBox/SOS/tests/.gitmirrorall b/src/coreclr/src/ToolBox/SOS/tests/.gitmirrorall
new file mode 100644 (file)
index 0000000..9ee5c57
--- /dev/null
@@ -0,0 +1 @@
+This folder will be mirrored by the Git-TFS Mirror recursively.
\ No newline at end of file
diff --git a/src/coreclr/src/ToolBox/SOS/tests/OnCrash.do b/src/coreclr/src/ToolBox/SOS/tests/OnCrash.do
new file mode 100644 (file)
index 0000000..b1da861
--- /dev/null
@@ -0,0 +1,2 @@
+script open("/tmp/flag_fail", "a").close()
+q
diff --git a/src/coreclr/src/ToolBox/SOS/tests/README.md b/src/coreclr/src/ToolBox/SOS/tests/README.md
new file mode 100644 (file)
index 0000000..ed4c8d0
--- /dev/null
@@ -0,0 +1,33 @@
+Testing libsosplugin
+=====================================
+
+**Test assembly**        
+The test asembly file must follow two rules:
+1. the test class must be named `Program` and
+2. it must have a static `Main` method.
+
+**Running tests**
+Make sure that python's lldb module is accessible. To run the tests, use the following command:
+`python test_libsosplugin.py --clr-args="/path/to/corerun [corerun_options] /path/to/test_assembly.exe"`
+`--clr-args` is a command that would normally be used to launch `corerun`
+This will run the test suite on the specified assembly.
+
+**Writing tests**
+Tests start with the `TestSosCommands` class defined in `test_libsosplugin.py`. To add a test to the suite, start with implementing a new method inside this class whose name begins with `test_`. Most new commands will require only one line of code in this method: `self.do_test("scenarioname")`. This command will launch a new `lldb` instance, which in turn will call the `runScenario` method from `scenarioname` module. `scenarioname` is the name of the python module that will be running the scenario inside `lldb` (found in `tests` folder alongside with `test_libsosplugin.py` and named `scenarioname.py`). 
+An example of a scenario looks like this:
+
+       import lldb
+       def runScenario(assemblyName, debugger, target):
+               process = target.GetProcess()
+
+               # do some work
+
+               process.Continue()
+               return True
+
+ `runScenario` method does all the work related to running the scenario: setting breakpoints, running SOS commands and examining their output. It should return a boolean value indicating a success or a failure.
+***Note:*** `testutils.py` defines some useful commands that can be reused in many scenarios.
+
+**Useful links**
+[Python scripting in LLDB](http://lldb.llvm.org/python-reference.html)
+[Python unittest framework](https://docs.python.org/2.7/library/unittest.html)
\ No newline at end of file
diff --git a/src/coreclr/src/ToolBox/SOS/tests/dumpil.py b/src/coreclr/src/ToolBox/SOS/tests/dumpil.py
new file mode 100644 (file)
index 0000000..9539b61
--- /dev/null
@@ -0,0 +1,26 @@
+import lldb
+import lldbutil
+import re
+import os
+import testutils
+
+def runScenario(assemblyName, debugger, target):
+       process = target.GetProcess()
+       res = lldb.SBCommandReturnObject()
+       ci = debugger.GetCommandInterpreter()
+
+       testutils.stop_in_main(ci, process, assemblyName)
+       addr = testutils.exec_and_find(ci, "name2ee " + assemblyName + " Program.Main", "MethodDesc:\s+([0-9a-fA-F]+)")
+
+       result = False
+       if addr is not None:
+               ci.HandleCommand("dumpil " + addr, res)
+               if res.Succeeded():
+                       result = True
+               else:
+                       print("DumpIL failed:")
+                       print(res.GetOutput())
+                       print(res.GetError())
+
+       process.Continue()
+       return result
diff --git a/src/coreclr/src/ToolBox/SOS/tests/dumpmodule.py b/src/coreclr/src/ToolBox/SOS/tests/dumpmodule.py
new file mode 100644 (file)
index 0000000..04a5764
--- /dev/null
@@ -0,0 +1,26 @@
+import lldb
+import lldbutil
+import re
+import os
+import testutils
+
+def runScenario(assemblyName, debugger, target):
+       process = target.GetProcess()
+       res = lldb.SBCommandReturnObject()
+       ci = debugger.GetCommandInterpreter()
+       
+       testutils.stop_in_main(ci, process, assemblyName)
+       addr = testutils.exec_and_find(ci, "name2ee " + assemblyName + " Program.Main", "Module:\s+([0-9a-fA-F]+)")
+
+       result = False
+       if addr is not None:
+               ci.HandleCommand("dumpmodule " + addr, res)
+               if res.Succeeded():
+                       result = True
+               else:
+                       print("DumpModule failed:")
+                       print(res.GetOutput())
+                       print(res.GetError())
+
+       process.Continue()
+       return result
\ No newline at end of file
diff --git a/src/coreclr/src/ToolBox/SOS/tests/runprocess.py b/src/coreclr/src/ToolBox/SOS/tests/runprocess.py
new file mode 100644 (file)
index 0000000..d9367b3
--- /dev/null
@@ -0,0 +1,34 @@
+import os
+import lldb
+import sys
+import importlib
+from test_libsosplugin import fail_flag
+
+def run(assemblyName, moduleName):
+       global fail_flag
+
+       print(fail_flag)
+       # set the flag, if it is not set
+       if not os.access(fail_flag, os.R_OK):
+               open(fail_flag, "a").close()
+
+
+       debugger = lldb.debugger
+
+       debugger.SetAsync(False)
+       target = lldb.target
+
+       debugger.HandleCommand("process launch -s")
+       debugger.HandleCommand("breakpoint set -n LoadLibraryExW")
+
+       target.GetProcess().Continue()
+
+       debugger.HandleCommand("breakpoint delete 1")
+       #run the scenario
+       print("starting scenario...")
+       i = importlib.import_module(moduleName)
+       scenarioResult = i.runScenario(os.path.basename(assemblyName), debugger, target)
+
+       # clear the failed flag if the exit status is OK
+       if scenarioResult is True and target.GetProcess().GetExitStatus() == 0:
+               os.unlink(fail_flag)
diff --git a/src/coreclr/src/ToolBox/SOS/tests/test_libsosplugin.py b/src/coreclr/src/ToolBox/SOS/tests/test_libsosplugin.py
new file mode 100644 (file)
index 0000000..e4f59eb
--- /dev/null
@@ -0,0 +1,84 @@
+import unittest
+import argparse
+import re
+import tempfile
+import subprocess
+import threading
+import os
+import os.path
+import sys
+
+assemblyName=''
+clrArgs=''
+fail_flag='/tmp/fail_flag'
+
+# helper functions
+
+def prepareScenarioFile(moduleName):
+       global assemblyName
+       #create a temporary scenario file
+       fd, scenarioFileName = tempfile.mkstemp()
+       scenarioFile = open(scenarioFileName, 'w')
+       scenarioFile.write('script from runprocess import run\n')
+       scenarioFile.write('script run("'+assemblyName+'", "'+moduleName+'")\n')
+       scenarioFile.write('quit\n')
+       scenarioFile.close()
+       os.close(fd)
+       return scenarioFileName
+
+def runWithTimeout(cmd, timeout):
+       d = {'process': None}
+       def run():
+               d['process'] = subprocess.Popen(cmd, shell=True)
+               d['process'].communicate()
+
+       thread = threading.Thread(target=run)
+       thread.start()
+
+       thread.join(timeout)
+       if thread.is_alive():
+               d['process'].terminate()
+               thread.join()
+
+# Test class
+class TestSosCommands(unittest.TestCase):
+
+       def do_test(self, command):
+               global clrArgs
+               global fail_flag
+               filename = prepareScenarioFile(command)
+               cmd = "lldb --source "+filename+" -b -K \"OnCrash.do\" -- "+clrArgs+" > "+command+".log 2>"+command+".log.2"
+               runWithTimeout(cmd, 120)
+               self.assertFalse(os.path.isfile(fail_flag))
+               os.unlink(filename)
+
+       def test_dumpmodule(self):
+               self.do_test("dumpmodule")
+
+       def test_dumpil(self):
+               self.do_test("dumpil")
+       
+
+if __name__ == '__main__':
+       parser = argparse.ArgumentParser()
+       parser.add_argument('--clr-args', default='')
+       parser.add_argument('unittest_args', nargs='*')
+
+       args = parser.parse_args()
+
+       clrArgs = args.clr_args
+       print("ClrArgs: " + clrArgs)
+       # find assembly name among lldb arguments
+       assembly_regexp = re.compile("([^\s]+\.exe)")
+       assemblyMatch = assembly_regexp.search(clrArgs)
+       if assemblyMatch is not None:
+               assemblyName = assemblyMatch.group(1)
+       else:
+               print("Assembly not recognized")
+               exit(1)
+
+       print("Assembly name: "+assemblyName)
+       sys.argv[1:] = args.unittest_args
+       suite = unittest.TestLoader().loadTestsFromTestCase(TestSosCommands)
+       unittest.TextTestRunner(verbosity=2).run(suite)
+       os.unlink(fail_flag)
\ No newline at end of file
diff --git a/src/coreclr/src/ToolBox/SOS/tests/testutils.py b/src/coreclr/src/ToolBox/SOS/tests/testutils.py
new file mode 100644 (file)
index 0000000..1ddb656
--- /dev/null
@@ -0,0 +1,40 @@
+import lldb
+import re
+
+def checkResult(res):
+       if not res.Succeeded():
+               print(res.GetOutput())
+               print(res.GetError())
+               exit(1)
+
+def exec_and_find(commandInterpreter, cmd, regexp):
+       res = lldb.SBCommandReturnObject()
+       commandInterpreter.HandleCommand(cmd, res)
+       checkResult(res)
+
+       expr = re.compile(regexp)
+       addr = None
+
+       print(res.GetOutput())
+       lines = res.GetOutput().splitlines()
+       for line in lines:
+               match = expr.match(line)
+               if match is not None:
+                       addr = match.group(1)
+                       break
+
+       print("Found addr: " + str(addr))
+       return addr
+
+def stop_in_main(commandInterpreter, process, assemblyName):
+       res = lldb.SBCommandReturnObject()
+       commandInterpreter.HandleCommand("bpmd " + assemblyName + " Program.Main", res)
+       checkResult(res)
+       print(res.GetOutput())
+       print(res.GetError())
+       res.Clear()
+
+
+       # Use Python API to continue the process.  The listening thread should be
+       # able to receive the state changed events.
+       process.Continue()
\ No newline at end of file