Skip to content

Commit 085f1e6

Browse files
committed
Python tests can now be debugged by running them as embedded tests within NUnit
1 parent 1f40564 commit 085f1e6

File tree

1 file changed

+79
-0
lines changed

1 file changed

+79
-0
lines changed

src/embed_tests/TestPythonTests.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using System.Text;
7+
8+
using NUnit.Framework;
9+
10+
using Python.Runtime;
11+
12+
namespace Python.EmbeddingTest
13+
{
14+
public class TestPythonTests
15+
{
16+
[OneTimeSetUp]
17+
public void SetUp()
18+
{
19+
PythonEngine.Initialize();
20+
}
21+
22+
[OneTimeTearDown]
23+
public void Dispose()
24+
{
25+
PythonEngine.Shutdown();
26+
}
27+
28+
/// <summary>
29+
/// Selects the Python tests to be run as embedded tests.
30+
/// </summary>
31+
/// <returns></returns>
32+
static IEnumerable<string[]> PythonTestCases()
33+
{
34+
// Add the test that you want to debug here.
35+
yield return new[] { "test_enum", "test_enum_standard_attrs" };
36+
yield return new[] { "test_generic", "test_missing_generic_type" };
37+
}
38+
39+
/// <summary>
40+
/// Runs a test in src/tests/*.py as an embedded test. This facilitates debugging.
41+
/// </summary>
42+
/// <param name="testFile">The file name without extension</param>
43+
/// <param name="testName">The name of the test method</param>
44+
[TestCaseSource(nameof(PythonTestCases))]
45+
public void TestPythonTest(string testFile, string testName)
46+
{
47+
// Find the tests directory
48+
string folder = typeof(TestPythonTests).Assembly.Location;
49+
while (Path.GetFileName(folder) != "src")
50+
{
51+
folder = Path.GetDirectoryName(folder);
52+
}
53+
folder = Path.Combine(folder, "tests");
54+
string path = Path.Combine(folder, testFile + ".py");
55+
if (!File.Exists(path)) throw new FileNotFoundException("Cannot find test file", path);
56+
57+
// We could use 'import' below, but importlib gives more helpful error messages than 'import'
58+
// https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
59+
// Because the Python tests sometimes have relative imports, the module name must be inside the tests package
60+
PythonEngine.Exec($@"
61+
import sys
62+
import os
63+
sys.path.append(os.path.dirname(r'{folder}'))
64+
sys.path.append(os.path.join(r'{folder}', 'fixtures'))
65+
import clr
66+
clr.AddReference('Python.Test')
67+
import tests
68+
module_name = 'tests.{testFile}'
69+
file_path = r'{path}'
70+
import importlib.util
71+
spec = importlib.util.spec_from_file_location(module_name, file_path)
72+
module = importlib.util.module_from_spec(spec)
73+
sys.modules[module_name] = module
74+
spec.loader.exec_module(module)
75+
module.{testName}()
76+
");
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)