Add more lldbplugin/libsosplugin tests (#5606)
authorIvan Baravy <i.baravy@samsung.com>
Tue, 25 Oct 2016 16:32:15 +0000 (20:32 +0400)
committerMike McLaughlin <mikem@microsoft.com>
Tue, 25 Oct 2016 16:32:15 +0000 (09:32 -0700)
* Add more libsosplugin tests, update framework

+ Introduce test.assert* functions in testutils.py
+ Print test results as a table with failed assertions highlighted
+ PEP8!

* Add more lldbplugin/libsosplugin tests

40 files changed:
src/ToolBox/SOS/tests/OnCrash.do [deleted file]
src/ToolBox/SOS/tests/README.md
src/ToolBox/SOS/tests/Test.cs [new file with mode: 0644]
src/ToolBox/SOS/tests/dumpil.py [deleted file]
src/ToolBox/SOS/tests/dumpmodule.py [deleted file]
src/ToolBox/SOS/tests/runprocess.py [deleted file]
src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_clrstack.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_clrthreads.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_clru.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dso.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dumpclass.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dumpheap.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dumpil.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dumplog.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dumpmd.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dumpmodule.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dumpmt.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dumpobj.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_dumpstack.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_eeheap.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_eestack.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_gcroot.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_histclear.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_histinit.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_histobj.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_histobjfind.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_histroot.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_ip2md.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_name2ee.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_pe.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_sos.py [new file with mode: 0644]
src/ToolBox/SOS/tests/t_cmd_soshelp.py [new file with mode: 0644]
src/ToolBox/SOS/tests/test_libsosplugin.py
src/ToolBox/SOS/tests/testutils.py

diff --git a/src/ToolBox/SOS/tests/OnCrash.do b/src/ToolBox/SOS/tests/OnCrash.do
deleted file mode 100644 (file)
index b1da861..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-script open("/tmp/flag_fail", "a").close()
-q
index ed4c8d0..1b9c1ea 100644 (file)
@@ -1,18 +1,28 @@
 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**
+**Test assembly**  
+Compile test assembly file using any C# compiler you have, for example:  
+- `gmcs test.cs`  
+- `corerun csc.exe /nologo /r:System.Private.CoreLib.dll test.cs`
+
+
+**Running tests**  
+Make sure that python's lldb module is accessible. To run the tests, use the following command:  
+`python2 test_libsosplugin.py --corerun=corerun --sosplugin=sosplugin --assembly=assembly --timeout=timeout`  
+- `lldb` is a path to `lldb` to run  
+- `clrdir` is a directory with `corerun` and sosplugin  
+- `assembly` is a compiled test assembly (e.g. Test.exe)  
+- `timeout` is a deadline for a single test (in seconds)  
+- `regex` is a regular expression matching tests to run  
+- `repeat` is a number of passes for each test
+
+
+
+Log files for both failed and passed tests are `*.log` and `*.log.2` for standard output and error correspondingly.
+
+
+**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:
 
@@ -25,9 +35,10 @@ An example of a scenario looks like this:
                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.
+ `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
+
+**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)
diff --git a/src/ToolBox/SOS/tests/Test.cs b/src/ToolBox/SOS/tests/Test.cs
new file mode 100644 (file)
index 0000000..e4ef76b
--- /dev/null
@@ -0,0 +1,94 @@
+using System;
+
+class Test
+{
+    static void LikelyInlined()
+    {
+        Console.WriteLine("I would like to be inlined");
+    }
+
+    static void UnlikelyInlined()
+    {
+        Console.Write("I");
+        Console.Write(" ");
+        Console.Write("w");
+        Console.Write("o");
+        Console.Write("u");
+        Console.Write("l");
+        Console.Write("d");
+        Console.Write(" ");
+        Console.Write("n");
+        Console.Write("o");
+        Console.Write("t");
+        Console.Write(" ");
+        Console.Write("l");
+        Console.Write("i");
+        Console.Write("k");
+        Console.Write("e");
+        Console.Write(" ");
+        Console.Write("t");
+        Console.Write("o");
+        Console.Write(" ");
+        Console.Write("b");
+        Console.Write("e");
+        Console.Write(" ");
+        Console.Write("i");
+        Console.Write("n");
+        Console.Write("l");
+        Console.Write("i");
+        Console.Write("n");
+        Console.Write("e");
+        Console.Write("d");
+        Console.Write("\n");
+    }
+
+    static void ClrU()
+    {
+        Console.WriteLine("test dumpclass");
+    }
+
+    static void DumpClass()
+    {
+        Console.WriteLine("test dumpclass");
+    }
+
+    static void DumpIL()
+    {
+        Console.WriteLine("test dumpil");
+    }
+
+    static void DumpMD()
+    {
+        Console.WriteLine("test dumpmd");
+    }
+
+    static void DumpModule()
+    {
+        Console.WriteLine("test dumpmodule");
+    }
+
+    static void DumpObject()
+    {
+        Console.WriteLine("test dumpobject");
+    }
+
+    static void DumpStackObjects()
+    {
+        Console.WriteLine("test dso");
+    }
+
+    static void Name2EE()
+    {
+        Console.WriteLine("test name2ee");
+    }
+
+
+    static int Main()
+    {
+        DumpIL();
+        LikelyInlined();
+        UnlikelyInlined();
+
+        return 0;
+    }
+}
diff --git a/src/ToolBox/SOS/tests/dumpil.py b/src/ToolBox/SOS/tests/dumpil.py
deleted file mode 100644 (file)
index 9539b61..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-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/ToolBox/SOS/tests/dumpmodule.py b/src/ToolBox/SOS/tests/dumpmodule.py
deleted file mode 100644 (file)
index 04a5764..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-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/ToolBox/SOS/tests/runprocess.py b/src/ToolBox/SOS/tests/runprocess.py
deleted file mode 100644 (file)
index d9367b3..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-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/ToolBox/SOS/tests/t_cmd_bpmd_clear.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_clear.py
new file mode 100644 (file)
index 0000000..814d114
--- /dev/null
@@ -0,0 +1,62 @@
+import lldb
+import re
+import testutils as test
+
+# bpmd -clearall
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    # Set breakpoint
+
+    ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(out_msg)
+    print(err_msg)
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # Output is not empty
+    # Should be at least 'Adding pending breakpoints...'
+    test.assertTrue(len(out_msg) > 0)
+
+    # Error message is empty
+    test.assertTrue(len(err_msg) == 0)
+
+    # Delete the first breakpoint
+
+    ci.HandleCommand("bpmd -clear 1", res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(out_msg)
+    print(err_msg)
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    match = re.search('Cleared', out_msg)
+    # Check for specific output
+    test.assertTrue(match)
+
+    # Error message is empty
+    test.assertEqual(err_msg, '')
+
+    process.Continue()
+    # Process must be exited
+    test.assertEqual(process.GetState(), lldb.eStateExited)
+
+    # The reason of this stop must be a breakpoint
+    test.assertEqual(process.GetSelectedThread().GetStopReason(),
+                     lldb.eStopReasonNone)
+
+    #
+
+    # Delete all breakpoints, continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_clearall.py
new file mode 100644 (file)
index 0000000..8da9239
--- /dev/null
@@ -0,0 +1,62 @@
+import lldb
+import re
+import testutils as test
+
+# bpmd -clearall
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    # Set breakpoint
+
+    ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(out_msg)
+    print(err_msg)
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # Output is not empty
+    # Should be at least 'Adding pending breakpoints...'
+    test.assertTrue(len(out_msg) > 0)
+
+    # Error message is empty
+    test.assertTrue(len(err_msg) == 0)
+
+    # Delete all breakpoints
+
+    ci.HandleCommand("bpmd -clearall", res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(out_msg)
+    print(err_msg)
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    match = re.search('All pending breakpoints cleared.', out_msg)
+    # Check for specific output
+    test.assertTrue(match)
+
+    # Error message is empty
+    test.assertEqual(err_msg, '')
+
+    process.Continue()
+    # Process must be exited
+    test.assertEqual(process.GetState(), lldb.eStateExited)
+
+    # The reason of this stop must be a breakpoint
+    test.assertEqual(process.GetSelectedThread().GetStopReason(),
+                     lldb.eStopReasonNone)
+
+    #
+
+    # Delete all breakpoints, continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_methoddesc.py
new file mode 100644 (file)
index 0000000..dfd7543
--- /dev/null
@@ -0,0 +1,45 @@
+import lldb
+import re
+import testutils as test
+
+# bpmd -md <MethodDesc pointer>
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    md_addr = test.get_methoddesc(debugger, assembly, "Test.UnlikelyInlined")
+
+    ci.HandleCommand("bpmd -md %s" % md_addr, res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(out_msg)
+    print(err_msg)
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # Output is not empty
+    # Should be at least 'Adding pending breakpoints...'
+    test.assertTrue(len(out_msg) > 0)
+
+    # Error message is empty
+    test.assertTrue(len(err_msg) == 0)
+
+    process.Continue()
+    # Process must be stopped at UnlinkelyInlined
+    test.assertEqual(process.GetState(), lldb.eStateStopped)
+
+    # The reason of this stop must be a breakpoint
+    test.assertEqual(process.GetSelectedThread().GetStopReason(),
+                     lldb.eStopReasonBreakpoint)
+
+    #
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function.py
new file mode 100644 (file)
index 0000000..e407ab3
--- /dev/null
@@ -0,0 +1,43 @@
+import lldb
+import re
+import testutils as test
+
+# bpmd <module name> <managed function name>
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined", res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(out_msg)
+    print(err_msg)
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # Output is not empty
+    # Should be at least 'Adding pending breakpoints...'
+    test.assertTrue(len(out_msg) > 0)
+
+    # Error message is empty
+    test.assertTrue(len(err_msg) == 0)
+
+    process.Continue()
+    # Process must be stopped at UnlinkelyInlined
+    test.assertEqual(process.GetState(), lldb.eStateStopped)
+
+    # The reason of this stop must be a breakpoint
+    test.assertEqual(process.GetSelectedThread().GetStopReason(),
+                     lldb.eStopReasonBreakpoint)
+
+    #
+
+    # Delete all breakpoints, continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_module_function_iloffset.py
new file mode 100644 (file)
index 0000000..91fb1cd
--- /dev/null
@@ -0,0 +1,43 @@
+import lldb
+import re
+import testutils as test
+
+# bpmd <module name> <managed function name> [<il offset>]
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("bpmd " + assembly + " Test.UnlikelyInlined 66", res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(out_msg)
+    print(err_msg)
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # Output is not empty
+    # Should be at least 'Adding pending breakpoints...'
+    test.assertTrue(len(out_msg) > 0)
+
+    # Error message is empty
+    test.assertTrue(len(err_msg) == 0)
+
+    process.Continue()
+    # Process must be stopped at UnlinkelyInlined
+    test.assertEqual(process.GetState(), lldb.eStateStopped)
+
+    # The reason of this stop must be a breakpoint
+    test.assertEqual(process.GetSelectedThread().GetStopReason(),
+                     lldb.eStopReasonBreakpoint)
+
+    #
+
+    # Delete all breakpoints, continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py b/src/ToolBox/SOS/tests/t_cmd_bpmd_nofuturemodule_module_function.py
new file mode 100644 (file)
index 0000000..64efad7
--- /dev/null
@@ -0,0 +1,48 @@
+import lldb
+import re
+import testutils as test
+
+# bpmd -nofuturemodule <module name> <managed function name>
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Process must be stopped here while libcoreclr loading.
+    # This test usually fails on release version of coreclr
+    # since we depend on 'LoadLibraryExW' symbol present.
+    test.assertEqual(process.GetState(), lldb.eStateStopped)
+
+    # The reason of this stop must be a breakpoint
+    test.assertEqual(process.GetSelectedThread().GetStopReason(),
+                     lldb.eStopReasonBreakpoint)
+
+    ci.HandleCommand("bpmd -nofuturemodule " + assembly + " Test.Main", res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(out_msg)
+    print(err_msg)
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # Output is not empty
+    # Should be at least 'Adding pending breakpoints...'
+    test.assertTrue(len(out_msg) == 0)
+
+    # Error message is empty
+    test.assertTrue(len(err_msg) == 0)
+
+    process.Continue()
+    # Process must be exited because of -nofuturemodule flag
+    test.assertEqual(process.GetState(), lldb.eStateExited)
+
+    # The reason of this stop must be a breakpoint
+    test.assertEqual(process.GetSelectedThread().GetStopReason(),
+                     lldb.eStopReasonNone)
+
+    #
+
+    # Delete all breakpoints, continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_clrstack.py b/src/ToolBox/SOS/tests/t_cmd_clrstack.py
new file mode 100644 (file)
index 0000000..28a1a8b
--- /dev/null
@@ -0,0 +1,36 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("clrstack", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('OS Thread Id', output)
+    # Specific string must be in the output
+    test.assertTrue(match)
+
+    match = re.search('Failed to start', output)
+    # Check if a fail was reported
+    test.assertFalse(match)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_clrthreads.py b/src/ToolBox/SOS/tests/t_cmd_clrthreads.py
new file mode 100644 (file)
index 0000000..ff731da
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("clrthreads", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("ThreadCount:"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_clru.py b/src/ToolBox/SOS/tests/t_cmd_clru.py
new file mode 100644 (file)
index 0000000..81a583e
--- /dev/null
@@ -0,0 +1,46 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("name2ee " + assembly + " Test.Main", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('JITTED Code Address:\s+([0-9a-fA-F]+)', output)
+    # Line matched
+    test.assertTrue(match)
+
+    groups = match.groups()
+    # Match has a single subgroup
+    test.assertEqual(len(groups), 1)
+
+    jit_addr = groups[0]
+    # Address must be a hex number
+    test.assertTrue(test.is_hexnum(jit_addr))
+
+    ci.HandleCommand("clru " + jit_addr, res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dso.py b/src/ToolBox/SOS/tests/t_cmd_dso.py
new file mode 100644 (file)
index 0000000..492204d
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dso", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("SP/REG"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpclass.py b/src/ToolBox/SOS/tests/t_cmd_dumpclass.py
new file mode 100644 (file)
index 0000000..6a69070
--- /dev/null
@@ -0,0 +1,68 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("name2ee " + assembly + " Test.Main", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output)
+    # Line matched
+    test.assertTrue(match)
+
+    groups = match.groups()
+    # Match has a single subgroup
+    test.assertEqual(len(groups), 1)
+
+    md_addr = groups[0]
+    # Address must be a hex number
+    test.assertTrue(test.is_hexnum(md_addr))
+
+    ci.HandleCommand("dumpmd " + md_addr, res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('Class:\s+([0-9a-fA-F]+)', output)
+    # Line matched
+    test.assertTrue(match)
+
+    groups = match.groups()
+    # Match has a single subgroup
+    test.assertEqual(len(groups), 1)
+
+    class_addr = groups[0]
+    # Address must be a hex number
+    test.assertTrue(test.is_hexnum(class_addr))
+
+    ci.HandleCommand("dumpmd " + class_addr, res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpheap.py b/src/ToolBox/SOS/tests/t_cmd_dumpheap.py
new file mode 100644 (file)
index 0000000..8546de7
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dumpheap", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("Address"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpil.py b/src/ToolBox/SOS/tests/t_cmd_dumpil.py
new file mode 100644 (file)
index 0000000..295cf19
--- /dev/null
@@ -0,0 +1,38 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    md_addr = test.get_methoddesc(debugger, assembly, "Test.DumpIL")
+
+    ci.HandleCommand("dumpil " + md_addr, res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    insts = res.GetOutput()
+    print(insts)
+    # Function must have some instructions
+    test.assertTrue(len(insts) > 0)
+
+    match = re.search(r'IL_\w{4}:\sldstr.*test\sdumpil.*' +
+                      r'IL_\w{4}:\scall.*System\.Console::WriteLine.*' +
+                      r'IL_\w{4}:\sret',
+                      insts.replace('\n', ' '))
+    # Must have ldstr, call and ret instructions
+    test.assertTrue(match)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dumplog.py b/src/ToolBox/SOS/tests/t_cmd_dumplog.py
new file mode 100644 (file)
index 0000000..ab33b66
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dumplog", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find(" dump "), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpmd.py b/src/ToolBox/SOS/tests/t_cmd_dumpmd.py
new file mode 100644 (file)
index 0000000..0340eb5
--- /dev/null
@@ -0,0 +1,46 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("name2ee " + assembly + " Test.Main", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output)
+    # Line matched
+    test.assertTrue(match)
+
+    groups = match.groups()
+    # Match has a single subgroup
+    test.assertEqual(len(groups), 1)
+
+    md_addr = groups[0]
+    # Address must be a hex number
+    test.assertTrue(test.is_hexnum(md_addr))
+
+    ci.HandleCommand("dumpmd " + md_addr, res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py b/src/ToolBox/SOS/tests/t_cmd_dumpmodule.py
new file mode 100644 (file)
index 0000000..2dd0048
--- /dev/null
@@ -0,0 +1,46 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("name2ee " + assembly + " Test.Main", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('Module:\s+([0-9a-fA-F]+)', output)
+    # Line matched
+    test.assertTrue(match)
+
+    groups = match.groups()
+    # Match has a single subgroup
+    test.assertEqual(len(groups), 1)
+
+    md_addr = groups[0]
+    # Address must be a hex number
+    test.assertTrue(test.is_hexnum(md_addr))
+
+    ci.HandleCommand("dumpmodule " + md_addr, res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpmt.py b/src/ToolBox/SOS/tests/t_cmd_dumpmt.py
new file mode 100644 (file)
index 0000000..059060b
--- /dev/null
@@ -0,0 +1,68 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("name2ee " + assembly + " Test.Main", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output)
+    # Line matched
+    test.assertTrue(match)
+
+    groups = match.groups()
+    # Match has a single subgroup
+    test.assertEqual(len(groups), 1)
+
+    md_addr = groups[0]
+    # Address must be a hex number
+    test.assertTrue(test.is_hexnum(md_addr))
+
+    ci.HandleCommand("dumpmd " + md_addr, res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('MethodTable:\s+([0-9a-fA-F]+)', output)
+    # Line matched
+    test.assertTrue(match)
+
+    groups = match.groups()
+    # Match has a single subgroup
+    test.assertEqual(len(groups), 1)
+
+    mt_addr = groups[0]
+    # Address must be a hex number
+    test.assertTrue(test.is_hexnum(mt_addr))
+
+    ci.HandleCommand("dumpmt " + mt_addr, res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpobj.py b/src/ToolBox/SOS/tests/t_cmd_dumpobj.py
new file mode 100644 (file)
index 0000000..93f32cd
--- /dev/null
@@ -0,0 +1,69 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dso", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Get all objects
+    objects = []
+    for line in output.split('\n'):
+        match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line)
+        # Not all lines list objects
+        if match:
+            groups = match.groups()
+            # Match has exactly two subgroups
+            test.assertEqual(len(groups), 2)
+
+            obj_addr = groups[1]
+            # Address must be a hex number
+            test.assertTrue(test.is_hexnum(obj_addr))
+
+            objects.append(obj_addr)
+
+    # There must be at least one object
+    test.assertTrue(len(objects) > 0)
+
+    for obj in objects:
+        ci.HandleCommand("dumpobj " + obj, res)
+        print(res.GetOutput())
+        print(res.GetError())
+        # Interpreter must have this command and able to run it
+        test.assertTrue(res.Succeeded())
+
+        output = res.GetOutput()
+        # Output is not empty
+        test.assertTrue(len(output) > 0)
+
+        match = re.search('Name:\s+\S+', output)
+        test.assertTrue(match)
+        match = re.search('MethodTable:\s+[0-9a-fA-F]+', output)
+        test.assertTrue(match)
+        match = re.search('EEClass:\s+[0-9a-fA-F]+', output)
+        test.assertTrue(match)
+        match = re.search('Size:\s+[0-9a-fA-F]+', output)
+        test.assertTrue(match)
+        match = re.search('Fields:', output)
+        test.assertTrue(match)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_dumpstack.py b/src/ToolBox/SOS/tests/t_cmd_dumpstack.py
new file mode 100644 (file)
index 0000000..3e15f8b
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dumpstack", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("Test.Main()"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_eeheap.py b/src/ToolBox/SOS/tests/t_cmd_eeheap.py
new file mode 100644 (file)
index 0000000..50d8977
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("eeheap", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("Loader Heap"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_eestack.py b/src/ToolBox/SOS/tests/t_cmd_eestack.py
new file mode 100644 (file)
index 0000000..bc36592
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("eestack", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("Current frame"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_gcroot.py b/src/ToolBox/SOS/tests/t_cmd_gcroot.py
new file mode 100644 (file)
index 0000000..e6b727c
--- /dev/null
@@ -0,0 +1,61 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dso", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Get all objects
+    objects = []
+    for line in output.split('\n'):
+        match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line)
+        # Not all lines list objects
+        if match:
+            groups = match.groups()
+            # Match has exactly two subgroups
+            test.assertEqual(len(groups), 2)
+
+            obj_addr = groups[1]
+            # Address must be a hex number
+            test.assertTrue(test.is_hexnum(obj_addr))
+
+            objects.append(obj_addr)
+
+    # There must be at least one object
+    test.assertTrue(len(objects) > 0)
+
+    for obj in objects:
+        ci.HandleCommand("gcroot " + obj, res)
+        print(res.GetOutput())
+        print(res.GetError())
+        # Interpreter must have this command and able to run it
+        test.assertTrue(res.Succeeded())
+
+        output = res.GetOutput()
+        # Output is not empty
+        test.assertTrue(len(output) > 0)
+
+        match = re.search('Found', output)
+        test.assertTrue(match)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_histclear.py b/src/ToolBox/SOS/tests/t_cmd_histclear.py
new file mode 100644 (file)
index 0000000..db29bd8
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("histclear", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("Completed successfully."), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_histinit.py b/src/ToolBox/SOS/tests/t_cmd_histinit.py
new file mode 100644 (file)
index 0000000..5119128
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("histinit", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("STRESS LOG:"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_histobj.py b/src/ToolBox/SOS/tests/t_cmd_histobj.py
new file mode 100644 (file)
index 0000000..c88bdac
--- /dev/null
@@ -0,0 +1,61 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dso", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Get all objects
+    objects = []
+    for line in output.split('\n'):
+        match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line)
+        # Not all lines list objects
+        if match:
+            groups = match.groups()
+            # Match has exactly two subgroups
+            test.assertEqual(len(groups), 2)
+
+            obj_addr = groups[1]
+            # Address must be a hex number
+            test.assertTrue(test.is_hexnum(obj_addr))
+
+            objects.append(obj_addr)
+
+    # There must be at least one object
+    test.assertTrue(len(objects) > 0)
+
+    for obj in objects:
+        ci.HandleCommand("histobj " + obj, res)
+        print(res.GetOutput())
+        print(res.GetError())
+        # Interpreter must have this command and able to run it
+        test.assertTrue(res.Succeeded())
+
+        output = res.GetOutput()
+        # Output is not empty
+        test.assertTrue(len(output) > 0)
+
+        match = re.search('GCCount', output)
+        test.assertTrue(match)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_histobjfind.py b/src/ToolBox/SOS/tests/t_cmd_histobjfind.py
new file mode 100644 (file)
index 0000000..ffe5dbf
--- /dev/null
@@ -0,0 +1,61 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dso", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Get all objects
+    objects = []
+    for line in output.split('\n'):
+        match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line)
+        # Not all lines list objects
+        if match:
+            groups = match.groups()
+            # Match has exactly two subgroups
+            test.assertEqual(len(groups), 2)
+
+            obj_addr = groups[1]
+            # Address must be a hex number
+            test.assertTrue(test.is_hexnum(obj_addr))
+
+            objects.append(obj_addr)
+
+    # There must be at least one object
+    test.assertTrue(len(objects) > 0)
+
+    for obj in objects:
+        ci.HandleCommand("histobjfind " + obj, res)
+        print(res.GetOutput())
+        print(res.GetError())
+        # Interpreter must have this command and able to run it
+        test.assertTrue(res.Succeeded())
+
+        output = res.GetOutput()
+        # Output is not empty
+        test.assertTrue(len(output) > 0)
+
+        match = re.search('GCCount', output)
+        test.assertTrue(match)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_histroot.py b/src/ToolBox/SOS/tests/t_cmd_histroot.py
new file mode 100644 (file)
index 0000000..7b73caa
--- /dev/null
@@ -0,0 +1,61 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dso", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Get all objects
+    objects = []
+    for line in output.split('\n'):
+        match = re.match('([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s', line)
+        # Not all lines list objects
+        if match:
+            groups = match.groups()
+            # Match has exactly two subgroups
+            test.assertEqual(len(groups), 2)
+
+            obj_addr = groups[1]
+            # Address must be a hex number
+            test.assertTrue(test.is_hexnum(obj_addr))
+
+            objects.append(obj_addr)
+
+    # There must be at least one object
+    test.assertTrue(len(objects) > 0)
+
+    for obj in objects:
+        ci.HandleCommand("histroot " + obj, res)
+        print(res.GetOutput())
+        print(res.GetError())
+        # Interpreter must have this command and able to run it
+        test.assertTrue(res.Succeeded())
+
+        output = res.GetOutput()
+        # Output is not empty
+        test.assertTrue(len(output) > 0)
+
+        match = re.search('GCCount', output)
+        test.assertTrue(match)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_ip2md.py b/src/ToolBox/SOS/tests/t_cmd_ip2md.py
new file mode 100644 (file)
index 0000000..1384c38
--- /dev/null
@@ -0,0 +1,53 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("name2ee " + assembly + " Test.Main", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('JITTED Code Address:\s+([0-9a-fA-F]+)', output)
+    # Line matched
+    test.assertTrue(match)
+
+    groups = match.groups()
+    # Match has a single subgroup
+    test.assertEqual(len(groups), 1)
+
+    jit_addr = groups[0]
+    # Address must be a hex number
+    test.assertTrue(test.is_hexnum(jit_addr))
+
+    ci.HandleCommand("ip2md " + jit_addr, res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("MethodDesc:"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_name2ee.py b/src/ToolBox/SOS/tests/t_cmd_name2ee.py
new file mode 100644 (file)
index 0000000..b617020
--- /dev/null
@@ -0,0 +1,46 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("name2ee " + assembly + " Test.Main", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    match = re.search('Module:\s+[0-9a-fA-F]+', output)
+    test.assertTrue(match)
+    match = re.search('Assembly:\s+\S+', output)
+    test.assertTrue(match)
+    match = re.search('Token:\s+[0-9a-fA-F]+', output)
+    test.assertTrue(match)
+    match = re.search('MethodDesc:\s+[0-9a-fA-F]+', output)
+    test.assertTrue(match)
+    match = re.search('Name:\s+\S+', output)
+    test.assertTrue(match)
+
+    process.Continue()
+    # Process must exit
+    test.assertEqual(process.GetState(), lldb.eStateExited)
+
+    # Process must exit with zero code
+    test.assertEqual(process.GetExitStatus(), 0)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_pe.py b/src/ToolBox/SOS/tests/t_cmd_pe.py
new file mode 100644 (file)
index 0000000..0a87014
--- /dev/null
@@ -0,0 +1,28 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("dso", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_sos.py b/src/ToolBox/SOS/tests/t_cmd_sos.py
new file mode 100644 (file)
index 0000000..b407491
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("sos", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("SOS"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
diff --git a/src/ToolBox/SOS/tests/t_cmd_soshelp.py b/src/ToolBox/SOS/tests/t_cmd_soshelp.py
new file mode 100644 (file)
index 0000000..8bb51da
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+import re
+import testutils as test
+
+
+def runScenario(assembly, debugger, target):
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+    ci = debugger.GetCommandInterpreter()
+
+    # Run debugger, wait until libcoreclr is loaded,
+    # set breakpoint at Test.Main and stop there
+    test.stop_in_main(debugger, assembly)
+
+    ci.HandleCommand("soshelp", res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    test.assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    test.assertTrue(len(output) > 0)
+
+    # Specific string must be in the output
+    test.assertNotEqual(output.find("soshelp <functionname>"), -1)
+
+    # TODO: test other use cases
+
+    # Continue current process and checks its exit code
+    test.exit_lldb(debugger, assembly)
index e4f59eb..e5a5906 100644 (file)
@@ -1,3 +1,4 @@
+from __future__ import print_function
 import unittest
 import argparse
 import re
@@ -5,80 +6,279 @@ import tempfile
 import subprocess
 import threading
 import os
-import os.path
 import sys
+import inspect
+
+lldb = ''
+clrdir = ''
+workdir = ''
+corerun = ''
+sosplugin = ''
+assembly = ''
+fail_flag = ''
+fail_flag_lldb = ''
+summary_file = ''
+timeout = 0
+regex = ''
+repeat = 0
+
+
+def runWithTimeout(cmd):
+    p = None
+
+    def run():
+        global p
+        p = subprocess.Popen(cmd, shell=True)
+        p.communicate()
+
+    thread = threading.Thread(target=run)
+    thread.start()
+
+    thread.join(timeout)
+    if thread.is_alive():
+        with open(summary_file, 'a+') as summary:
+            print('Timeout!', file=summary)
+        p.kill()
+        thread.join()
+
 
-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 do_test(self, command):
+        open(fail_flag, 'a').close()
+        try:
+            os.unlink(fail_flag_lldb)
+        except:
+            pass
+
+        cmd = (('%s -b ' % lldb) +
+               ("-k \"script open('%s', 'a').close()\" " % fail_flag_lldb) +
+               ("-k 'quit' ") +
+               ("--no-lldbinit ") +
+               ("-O \"plugin load %s \" " % sosplugin) +
+               ("-o \"script import testutils as test\" ") +
+               ("-o \"script test.fail_flag = '%s'\" " % fail_flag) +
+               ("-o \"script test.summary_file = '%s'\" " % summary_file) +
+               ("-o \"script test.run('%s', '%s')\" " % (assembly, command)) +
+               ("-o \"quit\" ") +
+               (" -- %s %s > %s.log 2> %s.log.2" % (corerun, assembly,
+                                                    command, command)))
+
+        runWithTimeout(cmd)
+        self.assertFalse(os.path.isfile(fail_flag))
+        self.assertFalse(os.path.isfile(fail_flag_lldb))
+
+        try:
+            os.unlink(fail_flag)
+        except:
+            pass
+        try:
+            os.unlink(fail_flag_lldb)
+        except:
+            pass
+
+    def t_cmd_bpmd_nofuturemodule_module_function(self):
+        self.do_test('t_cmd_bpmd_nofuturemodule_module_function')
+
+    def t_cmd_bpmd_module_function(self):
+        self.do_test('t_cmd_bpmd_module_function')
+
+    def t_cmd_bpmd_module_function_iloffset(self):
+        self.do_test('t_cmd_bpmd_module_function_iloffset')
+
+    def t_cmd_bpmd_methoddesc(self):
+        self.do_test('t_cmd_bpmd_methoddesc')
+
+    def t_cmd_bpmd_clearall(self):
+        self.do_test('t_cmd_bpmd_clearall')
+
+    def t_cmd_clrstack(self):
+        self.do_test('t_cmd_clrstack')
+
+    def t_cmd_clrthreads(self):
+        self.do_test('t_cmd_clrthreads')
+
+    def t_cmd_clru(self):
+        self.do_test('t_cmd_clru')
+
+    def t_cmd_dumpclass(self):
+        self.do_test('t_cmd_dumpclass')
+
+    def t_cmd_dumpheap(self):
+        self.do_test('t_cmd_dumpheap')
+
+    def t_cmd_dumpil(self):
+        self.do_test('t_cmd_dumpil')
+
+    def t_cmd_dumplog(self):
+        self.do_test('t_cmd_dumplog')
+
+    def t_cmd_dumpmd(self):
+        self.do_test('t_cmd_dumpmd')
+
+    def t_cmd_dumpmodule(self):
+        self.do_test('t_cmd_dumpmodule')
+
+    def t_cmd_dumpmt(self):
+        self.do_test('t_cmd_dumpmt')
+
+    def t_cmd_dumpobj(self):
+        self.do_test('t_cmd_dumpobj')
+
+    def t_cmd_dumpstack(self):
+        self.do_test('t_cmd_dumpstack')
+
+    def t_cmd_dso(self):
+        self.do_test('t_cmd_dso')
+
+    def t_cmd_eeheap(self):
+        self.do_test('t_cmd_eeheap')
 
-       def test_dumpmodule(self):
-               self.do_test("dumpmodule")
+    def t_cmd_eestack(self):
+        self.do_test('t_cmd_eestack')
+
+    def t_cmd_gcroot(self):
+        self.do_test('t_cmd_gcroot')
+
+    def t_cmd_ip2md(self):
+        self.do_test('t_cmd_ip2md')
+
+    def t_cmd_name2ee(self):
+        self.do_test('t_cmd_name2ee')
+
+    def t_cmd_pe(self):
+        self.do_test('t_cmd_pe')
+
+    def t_cmd_histclear(self):
+        self.do_test('t_cmd_histclear')
+
+    def t_cmd_histinit(self):
+        self.do_test('t_cmd_histinit')
+
+    def t_cmd_histobj(self):
+        self.do_test('t_cmd_histobj')
+
+    def t_cmd_histobjfind(self):
+        self.do_test('t_cmd_histobjfind')
+
+    def t_cmd_histroot(self):
+        self.do_test('t_cmd_histroot')
+
+    def t_cmd_sos(self):
+        self.do_test('t_cmd_sos')
+
+    def t_cmd_soshelp(self):
+        self.do_test('t_cmd_soshelp')
+
+
+def generate_report():
+    report = [{'name': 'TOTAL', True: 0, False: 0, 'completed': True}]
+    fail_messages = []
+
+    if not os.path.isfile(summary_file):
+        print('No summary file to process!')
+        return
+
+    with open(summary_file, 'r') as summary:
+        for line in summary:
+            if line.startswith('new_suite: '):
+                report.append({'name': line.split()[-1], True: 0, False: 0,
+                               'completed': False, 'timeout': False})
+            elif line.startswith('True'):
+                report[-1][True] += 1
+            elif line.startswith('False'):
+                report[-1][False] += 1
+            elif line.startswith('Completed!'):
+                report[-1]['completed'] = True
+            elif line.startswith('Timeout!'):
+                report[-1]['timeout'] = True
+            elif line.startswith('!!! '):
+                fail_messages.append(line.rstrip('\n'))
+
+    for suite in report[1:]:
+        report[0][True] += suite[True]
+        report[0][False] += suite[False]
+        report[0]['completed'] &= suite['completed']
+
+    for line in fail_messages:
+        print(line)
+
+    print()
+    print('=' * 79)
+    print('{:72} {:6}'.format('Test suite', 'Result'))
+    print('-' * 79)
+    for suite in report[1:]:
+        if suite['timeout']:
+            result = 'Timeout'
+        elif suite[False]:
+            result = 'Fail'
+        elif not suite['completed']:
+            result = 'Crash'
+        elif suite[True]:
+            result = 'Success'
+        else:
+            result = 'Please, report'
+        print('{:68} {:>10}'.format(suite['name'], result))
+    print('=' * 79)
 
-       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
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--lldb', default='lldb')
+    parser.add_argument('--clrdir', default='.')
+    parser.add_argument('--workdir', default='.')
+    parser.add_argument('--assembly', default='Test.exe')
+    parser.add_argument('--timeout', default=90)
+    parser.add_argument('--regex', default='t_cmd_')
+    parser.add_argument('--repeat', default=1)
+    parser.add_argument('unittest_args', nargs='*')
+
+    args = parser.parse_args()
+
+    lldb = args.lldb
+    clrdir = args.clrdir
+    workdir = args.workdir
+    assembly = args.assembly
+    timeout = int(args.timeout)
+    regex = args.regex
+    repeat = int(args.repeat)
+    print("lldb: %s" % lldb)
+    print("clrdir: %s" % clrdir)
+    print("workdir: %s" % workdir)
+    print("assembly: %s" % assembly)
+    print("timeout: %i" % timeout)
+    print("regex: %s" % regex)
+    print("repeat: %i" % repeat)
+
+    corerun = os.path.join(clrdir, 'corerun')
+    sosplugin = os.path.join(clrdir, 'libsosplugin.so')
+    if os.name != 'posix':
+        print('Not implemented: corerun.exe, sosplugin.dll?')
+        exit(1)
+
+    print("corerun: %s" % corerun)
+    print("sosplugin: %s" % sosplugin)
+
+    fail_flag = os.path.join(workdir, 'fail_flag')
+    fail_flag_lldb = os.path.join(workdir, 'fail_flag.lldb')
+
+    print("fail_flag: %s" % fail_flag)
+    print("fail_flag_lldb: %s" % fail_flag_lldb)
+
+    summary_file = os.path.join(workdir, 'summary')
+    print("summary_file: %s" % summary_file)
+
+    try:
+        os.unlink(summary_file)
+    except:
+        pass
+
+    sys.argv[1:] = args.unittest_args
+    suite = unittest.TestSuite()
+    all_tests = inspect.getmembers(TestSosCommands, predicate=inspect.ismethod)
+    for (test_name, test_func) in all_tests:
+        if re.match(regex, test_name):
+            suite.addTest(TestSosCommands(test_name))
+    unittest.TextTestRunner(verbosity=1).run(suite)
+
+    generate_report()
index 1ddb656..1f784b4 100644 (file)
+from __future__ import print_function
 import lldb
 import re
+import inspect
+import sys
+import os
+import importlib
+
+summary_file = ''
+fail_flag = ''
+
+failed = False
+
+
+def assertCommon(passed, fatal):
+    global failed
+    with open(summary_file, 'a+') as summary:
+        print(bool(passed), file=summary)
+        if (not passed):
+            failed = True
+            print('!!! test failed:', file=summary)
+            for s in inspect.stack()[2:]:
+                print("!!!  %s:%i" % (s[1], s[2]), file=summary)
+                print("!!! %s" % s[4][0], file=summary)
+                if re.match('\W*t_\w+\.py$', s[1]):
+                    break
+            print('!!! ', file=summary)
+
+            if fatal:
+                exit(1)
+
+
+def assertTrue(x, fatal=True):
+    passed = bool(x)
+    assertCommon(passed, fatal)
+
+
+def assertFalse(x, fatal=True):
+    passed = not bool(x)
+    assertCommon(passed, fatal)
+
+
+def assertEqual(x, y, fatal=True):
+    passed = (x == y)
+    if not passed:
+        print(str(x), ' != ', str(y))
+    assertCommon(passed, fatal)
+
+
+def assertNotEqual(x, y, fatal=True):
+    passed = (x != y)
+    if not passed:
+        print(str(x), ' == ', str(y))
+    assertCommon(passed, fatal)
+
 
 def checkResult(res):
-       if not res.Succeeded():
-               print(res.GetOutput())
-               print(res.GetError())
-               exit(1)
+    if not res.Succeeded():
+        print(res.GetOutput())
+        print(res.GetError())
+        exit(1)
+
+
+def is_hexnum(s):
+    try:
+        int(s, 16)
+        return True
+    except ValueError:
+        return False
+
 
 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
+    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:
+            addr = match.group(1)
+            break
+
+    print("Found addr: " + str(addr))
+    return addr
+
+
+def stop_in_main(debugger, assembly):
+    ci = debugger.GetCommandInterpreter()
+    target = debugger.GetSelectedTarget()
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+
+    # Process must be stopped here while libcoreclr loading.
+    # This test usually fails on release version of coreclr
+    # since we depend on 'LoadLibraryExW' symbol present.
+    assertEqual(process.GetState(), lldb.eStateStopped)
+
+    # The reason of this stop must be a breakpoint
+    assertEqual(process.GetSelectedThread().GetStopReason(),
+                lldb.eStopReasonBreakpoint)
+
+    ci.HandleCommand("bpmd " + assembly + " Test.Main", res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    assertTrue(res.Succeeded())
+
+    # Output is not empty
+    # Should be at least 'Adding pending breakpoints...'
+    assertTrue(len(out_msg) > 0)
+
+    # Error message is empty
+    assertTrue(len(err_msg) == 0)
+
+    process.Continue()
+    # Process must be stopped here if bpmd works at all
+    assertEqual(process.GetState(), lldb.eStateStopped)
+
+    # The reason of this stop must be a breakpoint
+    assertEqual(process.GetSelectedThread().GetStopReason(),
+                lldb.eStopReasonBreakpoint)
+
+
+def exit_lldb(debugger, assembly):
+    ci = debugger.GetCommandInterpreter()
+    target = debugger.GetSelectedTarget()
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+
+    ci.HandleCommand("breakpoint delete --force", res)
+    out_msg = res.GetOutput()
+    err_msg = res.GetError()
+    print(out_msg)
+    print(err_msg)
+    # Interpreter must have this command and able to run it
+    # assertTrue(res.Succeeded())
+
+    process.Continue()
+    # Process must exit
+    assertEqual(process.GetState(), lldb.eStateExited)
+
+    # Process must exit with zero code
+    assertEqual(process.GetExitStatus(), 0)
+
+
+def get_methoddesc(debugger, assembly, funcname):
+    ci = debugger.GetCommandInterpreter()
+    target = debugger.GetSelectedTarget()
+    process = target.GetProcess()
+    res = lldb.SBCommandReturnObject()
+
+    ci.HandleCommand("name2ee %s %s" % (assembly, funcname), res)
+    print(res.GetOutput())
+    print(res.GetError())
+    # Interpreter must have this command and able to run it
+    assertTrue(res.Succeeded())
+
+    output = res.GetOutput()
+    # Output is not empty
+    assertTrue(len(output) > 0)
+
+    match = re.search('MethodDesc:\s+([0-9a-fA-F]+)', output)
+    # Line matched
+    assertTrue(match)
+
+    groups = match.groups()
+    # Match has a single subgroup
+    assertEqual(len(groups), 1)
+
+    md_addr = groups[0]
+    # Address must be a hex number
+    assertTrue(is_hexnum(md_addr))
+
+    return md_addr
+
+
+def run(assembly, module):
+    with open(summary_file, 'a+') as summary:
+        print('new_suite: %s' % module, file=summary)
+
+    debugger = lldb.debugger
+
+    debugger.SetAsync(False)
+    target = lldb.target
+
+    debugger.HandleCommand("breakpoint set --one-shot --name coreclr_execute_assembly")
+    debugger.HandleCommand("process launch")
+
+    # run the scenario
+    print("starting scenario...")
+    i = importlib.import_module(module)
+    scenarioResult = i.runScenario(os.path.basename(assembly), debugger,
+                                   target)
+
+    if (target.GetProcess().GetExitStatus() == 0) and not failed:
+        os.unlink(fail_flag)
+
+    with open(summary_file, 'a+') as summary:
+        print('Completed!', file=summary)