@@ -19,9 +19,13 @@ def __init__(self, payload):
1919
2020 def pytest_runtest_logstart (self , nodeid , location ):
2121 """pytest_runtest_logstart hook callback"""
22- logger .debug ('Enter pytest_runtest_logstart for %s' , nodeid )
23- if not self .payload .is_started ():
22+ logger .debug ('hook=pytest_runtest_logstart nodeid=%s' , nodeid )
23+
24+ if self .payload .is_started ():
25+ logger .debug ('-> already started_at=%s(monotonic)' , self .payload .started_at )
26+ else :
2427 self .payload = self .payload .started ()
28+ logger .debug ('-> started_at=%s(monotonic)' , self .payload .started_at )
2529
2630 chunks = nodeid .split ("::" )
2731
@@ -36,48 +40,26 @@ def pytest_runtest_logstart(self, nodeid, location):
3640
3741 def pytest_runtest_logreport (self , report ):
3842 """pytest_runtest_logreport hook callback to get test outcome after test call"""
43+ logger .debug ('hook=pytest_runtest_logreport nodeid=%s when=%s' , report .nodeid , report .when )
3944
4045 # This hook is called three times during the lifecycle of a test:
4146 # after the setup phase, the call phase, and the teardown phase.
42- # Since we want to capture the outcome from the call phase,
43- # we only proceed when this hook is triggered following the call phase.
47+ # We capture outcomes from the call phase, or setup phase if it failed
48+ # (since setup failures prevent the call phase from running) .
4449 # See: https://github.com/buildkite/test-collector-python/pull/45
45- if report .when != 'call' :
46- return
47-
48- nodeid = report .nodeid
49- test_data = self .in_flight .get (nodeid )
50- logger .debug ('Enter pytest_runtest_logreport for %s' , nodeid )
51-
52- if test_data :
53- if report .passed :
54- test_data = test_data .passed ()
55-
56- if report .failed :
57- failure_reason , failure_expanded = failure_reasons (longrepr = report .longrepr )
58- test_data = test_data .failed (
59- failure_reason = failure_reason ,
60- failure_expanded = failure_expanded
61- )
62-
63- if report .skipped :
64- test_data = test_data .skipped ()
65-
66- # TestData is immutable.
67- # We need to replace the test_data in `in_flight` with updated test_data,
68- # so we can get the correct result when we process it during the teardown hook.
69- self .in_flight [nodeid ] = test_data
50+ if report .when == 'call' or (report .when == 'setup' and report .failed ):
51+ self .update_test_result (report )
7052
7153 # This hook only runs in xdist worker thread, not controller thread.
7254 # We used to rely on pytest_runtest_teardown, but somehow xdist will ignore it
7355 # in both controller and worker thread.
7456 def pytest_runtest_makereport (self , item , call ):
7557 """pytest_runtest_hook hook callback to mark test as finished and add it to the payload"""
58+ logger .debug ('hook=pytest_runtest_makereport nodeid=%s when=%s' , item .nodeid , call .when )
7659
7760 if call .when != 'teardown' :
7861 return
7962
80- logger .debug ('Enter pytest_runtest_makereport for %s' , item .nodeid )
8163 test_data = self .in_flight .get (item .nodeid )
8264
8365 if test_data :
@@ -97,7 +79,8 @@ def pytest_runtest_makereport(self, item, call):
9779 # In that case, it's necessary for this hook to work as a fallback mechanism.
9880 def pytest_runtest_logfinish (self , nodeid , location ): # pylint: disable=unused-argument
9981 """pytest_runtest_logfinish hook always runs in the very end"""
100- logger .debug ('Enter pytest_runtest_logfinish for %s' , nodeid )
82+ logger .debug ('hook=pytest_runtest_logfinish nodeid=%s' , nodeid )
83+
10184 if self .finalize_test (nodeid ):
10285 # This is expected to happen in xdist controller thread.
10386 # Where it would skip many pytest_runtest_xxx hooks
@@ -106,16 +89,44 @@ def pytest_runtest_logfinish(self, nodeid, location): # pylint: disable=unused-
10689 'Falling back to pytest_runtest_logfinish'
10790 )
10891
92+ def update_test_result (self , report ):
93+ test_data = self .in_flight .get (report .nodeid )
94+
95+ if test_data :
96+ if report .passed :
97+ logger .debug ('-> test passed' )
98+ test_data = test_data .passed ()
99+
100+ if report .failed :
101+ failure_reason , failure_expanded = failure_reasons (longrepr = report .longrepr )
102+ logger .debug ('-> test failed: %s' , failure_reason )
103+ test_data = test_data .failed (
104+ failure_reason = failure_reason ,
105+ failure_expanded = failure_expanded
106+ )
107+
108+ if report .skipped :
109+ logger .debug ('-> test skipped' )
110+ test_data = test_data .skipped ()
111+
112+ # TestData is immutable.
113+ # We need to replace the test_data in `in_flight` with updated test_data,
114+ # so we can get the correct result when we process it during the teardown hook.
115+ self .in_flight [report .nodeid ] = test_data
116+
117+
109118 def finalize_test (self , nodeid ):
110119 """ Attempting to move test data for a nodeid to payload area for upload """
111- logger .debug ('Entering finalize_test for %s' , nodeid )
120+ logger .debug ('-> finalize_test nodeid= %s' , nodeid )
112121 test_data = self .in_flight .get (nodeid )
113- if test_data :
114- del self .in_flight [nodeid ]
115- test_data = test_data .finish ()
116- self .payload = self .payload .push_test_data (test_data )
117- return True
118- return False
122+ if not test_data :
123+ logger .debug ('-> finalize_test: not in flight: %s' , nodeid )
124+ return False
125+ del self .in_flight [nodeid ]
126+ test_data = test_data .finish ()
127+ logger .debug ('-> finalize_test nodeid=%s duration=%s' , nodeid , test_data .history .duration )
128+ self .payload = self .payload .push_test_data (test_data )
129+ return True
119130
120131 def save_payload_as_json (self , path , merge = False ):
121132 """Save payload into a json file, merging with existing data if merge is True"""
0 commit comments