22import os
33import sys
44
5- from hypothesis import given
6- from hypothesis import strategies as st
75import pytest
86
97from ddtrace import Pin
10- from ddtrace .contrib .pytest .plugin import _json_encode
118from ddtrace .ext import test
129from tests .utils import TracerTestCase
1310
@@ -106,11 +103,36 @@ def test_ini(ddspan):
106103 assert len (spans ) == 1
107104
108105 def test_parameterize_case (self ):
109- """Test parametrize case."""
106+ """Test parametrize case with simple objects ."""
110107 py_file = self .testdir .makepyfile (
111108 """
112109 import pytest
113110
111+
112+ @pytest.mark.parametrize('item', [1, 2, 3, 4, pytest.param([1, 2, 3], marks=pytest.mark.skip)])
113+ class Test1(object):
114+ def test_1(self, item):
115+ assert item in {1, 2, 3}
116+ """
117+ )
118+ file_name = os .path .basename (py_file .strpath )
119+ rec = self .inline_run ("--ddtrace" , file_name )
120+ rec .assertoutcome (passed = 3 , failed = 1 , skipped = 1 )
121+ spans = self .pop_spans ()
122+
123+ expected_params = [1 , 2 , 3 , 4 , [1 , 2 , 3 ]]
124+ assert len (spans ) == 5
125+ for i in range (len (expected_params )):
126+ extracted_params = json .loads (spans [i ].meta [test .PARAMETERS ])
127+ assert extracted_params == {"arguments" : {"item" : expected_params [i ]}, "metadata" : {}}
128+
129+ def test_parameterize_case_complex_objects (self ):
130+ """Test parametrize case with complex objects."""
131+ py_file = self .testdir .makepyfile (
132+ """
133+ from mock import MagicMock
134+ import pytest
135+
114136 class A:
115137 def __init__(self, name, value):
116138 self.name = name
@@ -119,19 +141,19 @@ def __init__(self, name, value):
119141 def item_param():
120142 return 42
121143
144+ circular_reference = A("circular_reference", A("child", None))
145+ circular_reference.value.value = circular_reference
146+
122147 @pytest.mark.parametrize(
123- 'item',
124- [
125- 1,
126- 2,
127- 3,
128- 4,
129- pytest.param(A("test_name", "value"), marks=pytest.mark.skip),
130- pytest.param(A("test_name", A("inner_name", "value")), marks=pytest.mark.skip),
131- pytest.param({"a": A("test_name", "value"), "b": [1, 2, 3]}, marks=pytest.mark.skip),
132- pytest.param([1, 2, 3], marks=pytest.mark.skip),
133- pytest.param(item_param, marks=pytest.mark.skip)
134- ]
148+ 'item',
149+ [
150+ pytest.param(A("test_name", "value"), marks=pytest.mark.skip),
151+ pytest.param(A("test_name", A("inner_name", "value")), marks=pytest.mark.skip),
152+ pytest.param(item_param, marks=pytest.mark.skip),
153+ pytest.param({"a": A("test_name", "value"), "b": [1, 2, 3]}, marks=pytest.mark.skip),
154+ pytest.param(MagicMock(value=MagicMock()), marks=pytest.mark.skip),
155+ pytest.param(circular_reference, marks=pytest.mark.skip),
156+ ]
135157 )
136158 class Test1(object):
137159 def test_1(self, item):
@@ -140,24 +162,22 @@ def test_1(self, item):
140162 )
141163 file_name = os .path .basename (py_file .strpath )
142164 rec = self .inline_run ("--ddtrace" , file_name )
143- rec .assertoutcome (passed = 3 , failed = 1 , skipped = 5 )
165+ rec .assertoutcome (skipped = 6 )
144166 spans = self .pop_spans ()
145167
146- expected_params = [
147- 1 ,
148- 2 ,
149- 3 ,
150- 4 ,
151- { "name" : "test_name" , "value" : "value" } ,
152- { "name " : "test_name" , "value" : { "name" : "inner_name" , "value" : "value" }} ,
153- { "a" : { "name" : "test_name" , "value" : "value" }, "b" : [ 1 , 2 , 3 ]} ,
154- [ 1 , 2 , 3 ] ,
168+ # Since object will have arbitrary addresses, only need to ensure that
169+ # the params string contains most of the string representation of the object.
170+ expected_params_contains = [
171+ "test_parameterize_case_complex_objects.A" ,
172+ "test_parameterize_case_complex_objects.A" ,
173+ "<function item_param at 0x" ,
174+ '"a ": "<test_parameterize_case_complex_objects.A' ,
175+ "<MagicMock id=" ,
176+ "test_parameterize_case_complex_objects.A" ,
155177 ]
156- assert len (spans ) == 9
157- for i in range (len (spans ) - 1 ):
158- extracted_params = json .loads (spans [i ].meta [test .PARAMETERS ])
159- assert extracted_params == {"arguments" : {"item" : expected_params [i ]}, "metadata" : {}}
160- assert "<function item_param at 0x" in json .loads (spans [8 ].meta [test .PARAMETERS ])["arguments" ]["item" ]
178+ assert len (spans ) == 6
179+ for i in range (len (expected_params_contains )):
180+ assert expected_params_contains [i ] in spans [i ].meta [test .PARAMETERS ]
161181
162182 def test_skip (self ):
163183 """Test parametrize case."""
@@ -329,68 +349,3 @@ def test_service(ddspan):
329349 file_name = os .path .basename (py_file .strpath )
330350 rec = self .subprocess_run ("--ddtrace" , file_name )
331351 assert 0 == rec .ret
332-
333-
334- class A (object ):
335- def __init__ (self , name , value ):
336- self .name = name
337- self .value = value
338-
339-
340- simple_types = [st .none (), st .booleans (), st .text (), st .integers (), st .floats (allow_infinity = False , allow_nan = False )]
341- complex_types = [st .functions (), st .dates (), st .decimals (), st .builds (A , name = st .text (), value = st .integers ())]
342-
343-
344- @given (
345- st .dictionaries (
346- st .text (),
347- st .one_of (
348- st .lists (st .one_of (* simple_types )), st .dictionaries (st .text (), st .one_of (* simple_types )), * simple_types
349- ),
350- )
351- )
352- def test_custom_json_encoding_simple_types (obj ):
353- """Ensures the _json.encode helper encodes simple objects."""
354- encoded = _json_encode (obj )
355- decoded = json .loads (encoded )
356- assert obj == decoded
357-
358-
359- @given (
360- st .dictionaries (
361- st .text (),
362- st .one_of (
363- st .lists (st .one_of (* complex_types )), st .dictionaries (st .text (), st .one_of (* complex_types )), * complex_types
364- ),
365- )
366- )
367- def test_custom_json_encoding_python_objects (obj ):
368- """Ensures the _json_encode helper encodes complex objects into dicts of inner values or a string representation."""
369- encoded = _json_encode (obj )
370- obj = json .loads (
371- json .dumps (obj , default = lambda x : getattr (x , "__dict__" , None ) if getattr (x , "__dict__" , None ) else repr (x ))
372- )
373- decoded = json .loads (encoded )
374- assert obj == decoded
375-
376-
377- def test_custom_json_encoding_side_effects ():
378- """Ensures the _json_encode helper encodes objects with side effects (getattr, repr) without raising exceptions."""
379- dict_side_effect = Exception ("side effect __dict__" )
380- repr_side_effect = Exception ("side effect __repr__" )
381-
382- class B (object ):
383- def __getattribute__ (self , item ):
384- if item == "__dict__" :
385- raise dict_side_effect
386- raise AttributeError ()
387-
388- class C (object ):
389- def __repr__ (self ):
390- raise repr_side_effect
391-
392- obj = {"b" : B (), "c" : C ()}
393- encoded = _json_encode (obj )
394- decoded = json .loads (encoded )
395- assert decoded ["b" ] == repr (dict_side_effect )
396- assert decoded ["c" ] == repr (repr_side_effect )
0 commit comments