Skip to content

Commit 4994379

Browse files
committed
class name testing
1 parent 021e568 commit 4994379

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed

tests/integration_test.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,51 @@ fn test_cyrillic() {
257257
assert_eq!(trace.frames[1].line, 7);
258258
}
259259

260+
#[test]
261+
fn test_class_name() {
262+
#[cfg(target_os = "macos")]
263+
{
264+
// We need root permissions here to run this on OSX
265+
if unsafe { libc::geteuid() } != 0 {
266+
return;
267+
}
268+
}
269+
270+
let config = Config {
271+
include_class_name: true,
272+
..Default::default()
273+
};
274+
275+
let mut runner = TestRunner::new(config, "./tests/scripts/class_name.py");
276+
let traces = runner.spy.get_stack_traces().unwrap();
277+
assert_eq!(traces.len(), 1);
278+
let trace = &traces[0];
279+
280+
let frames: Vec<&str> = trace
281+
.frames
282+
.iter()
283+
.rev()
284+
.map(|frame| frame.name.as_ref())
285+
.collect();
286+
287+
assert_eq!(
288+
frames,
289+
[
290+
"<module>",
291+
"normal_function",
292+
"normal_function_with_arg",
293+
"normal_function_with_non_arg_local_called_self",
294+
"normal_function_with_non_arg_local_called_cls",
295+
"object.normal_function_with_a_confusing_self_arg", // first arg confused with self - room for improvement
296+
"object.normal_function_with_a_confusing_cls_arg", // first arg confused with cls - room for improvement
297+
"SomeClass.class_method", // correctly recognised class
298+
"class_method_confusing_first_arg", // class arg name doesn't follow the convention - not recognised
299+
"SomeClass.normal_method", // correctly recognised class instance
300+
"normal_method_confusing_first_arg", // self arg name doesn't follow the convention - not recognised
301+
]
302+
);
303+
}
304+
260305
#[test]
261306
fn test_local_vars() {
262307
#[cfg(target_os = "macos")]
@@ -335,6 +380,57 @@ fn test_local_vars() {
335380
}
336381
}
337382

383+
#[test]
384+
fn test_local_vars_and_class_name() {
385+
#[cfg(target_os = "macos")]
386+
{
387+
// We need root permissions here to run this on OSX
388+
if unsafe { libc::geteuid() } != 0 {
389+
return;
390+
}
391+
}
392+
393+
let config = Config {
394+
dump_locals: 1,
395+
include_class_name: true,
396+
..Default::default()
397+
};
398+
let mut runner = TestRunner::new(config, "./tests/scripts/local_vars_and_class_name.py");
399+
let traces = runner.spy.get_stack_traces().unwrap();
400+
assert_eq!(traces.len(), 1);
401+
let trace = &traces[0];
402+
assert_eq!(trace.frames.len(), 2);
403+
404+
let fn_frame = &trace.frames[0];
405+
assert_eq!(fn_frame.name, "ClassName.local_variable_lookup");
406+
407+
let locals = fn_frame.locals.as_ref().unwrap();
408+
409+
assert_eq!(locals[0].name, "self");
410+
assert_eq!(locals[0].arg, true);
411+
assert!(locals[0]
412+
.repr
413+
.as_ref()
414+
.expect("class repr should be given")
415+
.starts_with("<ClassName at 0x"));
416+
417+
let remaining_locals: Vec<(&str, bool, Option<&str>)> = locals
418+
.iter()
419+
.skip(1)
420+
.map(|local| (local.name.as_ref(), local.arg, local.repr.as_deref()))
421+
.collect();
422+
423+
assert_eq!(
424+
remaining_locals,
425+
[
426+
("arg1", true, Some("\"foo\"",),),
427+
("local1", false, Some("\"a\"",),),
428+
("local2", false, Some("2.71828",),),
429+
("local3", false, Some("{}",),),
430+
]
431+
);
432+
}
433+
338434
#[cfg(not(target_os = "freebsd"))]
339435
#[test]
340436
fn test_subprocesses() {

tests/scripts/class_name.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""
2+
Test program demonstrating a call stack with a normal method, a staticmethod, and a classmethod.
3+
4+
It also includes functions where the first argument is named `self` but isn't an instance or `cls`
5+
but isn't a class.
6+
"""
7+
import sys
8+
import time
9+
10+
11+
def normal_function():
12+
normal_function_with_arg(object())
13+
14+
15+
def normal_function_with_arg(something):
16+
normal_function_with_non_arg_local_called_self()
17+
18+
19+
def normal_function_with_non_arg_local_called_self():
20+
self = object()
21+
normal_function_with_non_arg_local_called_cls()
22+
23+
24+
def normal_function_with_non_arg_local_called_cls():
25+
cls = object
26+
normal_function_with_a_confusing_self_arg(object())
27+
28+
29+
def normal_function_with_a_confusing_self_arg(self):
30+
normal_function_with_a_confusing_cls_arg(object)
31+
32+
33+
def normal_function_with_a_confusing_cls_arg(cls):
34+
SomeClass.class_method()
35+
36+
37+
class SomeClass:
38+
@classmethod
39+
def class_method(cls):
40+
cls.class_method_confusing_first_arg()
41+
42+
@classmethod
43+
def class_method_confusing_first_arg(self):
44+
assert isinstance(
45+
self, type
46+
), "test precondition: `self` should confusingly be a class"
47+
actual_instance = self()
48+
actual_instance.normal_method()
49+
50+
def normal_method(self):
51+
self.normal_method_confusing_first_arg()
52+
53+
def normal_method_confusing_first_arg(cls):
54+
time.sleep(100000)
55+
56+
57+
if __name__ == "__main__":
58+
normal_function()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Combining local variable and class name lookup testing."""
2+
import time
3+
4+
5+
class ClassName:
6+
def __str__(self):
7+
return "a"
8+
9+
def local_variable_lookup(self, arg1="foo"):
10+
local1 = "a"
11+
local2 = 2.71828
12+
local3 = {}
13+
time.sleep(100000)
14+
15+
16+
if __name__ == "__main__":
17+
ClassName().local_variable_lookup()

0 commit comments

Comments
 (0)