@@ -21,20 +21,6 @@ def curses_init():
21
21
except ModuleNotFoundError :
22
22
logging .warning ("curses module not found, not setting up a default terminal – tests may fail" )
23
23
24
- def keep_reading (spawn ):
25
- "The output from background processes must be read to avoid blocking them."
26
- while spawn .isalive ():
27
- try :
28
- data = spawn .read_nonblocking (size = 1024 , timeout = 0.1 )
29
- if not data :
30
- return
31
- except pexpect .TIMEOUT :
32
- continue
33
- except pexpect .EOF :
34
- return
35
- except OSError :
36
- return
37
-
38
24
39
25
class Prefixer :
40
26
def __init__ (self , wrapped , prefix ):
@@ -60,40 +46,51 @@ def __getattr__(self, name):
60
46
return getattr (self .__wrapped , name )
61
47
62
48
63
- class Exporter :
64
- def __init__ (self , config , cwd ):
49
+ class LabgridComponent :
50
+ def __init__ (self , cwd ):
65
51
self .cwd = str (cwd )
66
- self .config = config
67
52
self .spawn = None
68
53
self .reader = None
69
54
70
- def start (self ):
71
- assert self .spawn is None
72
- assert self .reader is None
55
+ def stop (self ):
56
+ logging .info ("stopping {self.__class__.__name__} pid=%s" , self .spawn .pid )
57
+
58
+ # let coverage write its data:
59
+ # https://coverage.readthedocs.io/en/latest/subprocess.html#process-termination
60
+ self .spawn .kill (SIGTERM )
61
+ if not self .spawn .closed :
62
+ self .spawn .expect (pexpect .EOF )
63
+ self .spawn .wait ()
64
+ assert not self .spawn .isalive ()
73
65
74
- self .spawn = pexpect .spawn (
75
- f'{ sys .executable } -m labgrid.remote.exporter --name testhost { self .config } ' ,
76
- logfile = Prefixer (sys .stdout .buffer , 'exporter' ),
77
- cwd = self .cwd )
78
- try :
79
- self .spawn .expect ('exporter name: testhost' )
80
- self .spawn .expect ('connected to exporter' )
81
- except Exception as e :
82
- raise Exception (f"exporter startup failed with { self .spawn .before } " ) from e
66
+ self .spawn = None
67
+ self .stop_reader ()
68
+
69
+ @staticmethod
70
+ def keep_reading (spawn ):
71
+ "The output from background processes must be read to avoid blocking them."
72
+ while spawn .isalive ():
73
+ try :
74
+ data = spawn .read_nonblocking (size = 1024 , timeout = 0.1 )
75
+ if not data :
76
+ return
77
+ except pexpect .TIMEOUT :
78
+ continue
79
+ except pexpect .EOF :
80
+ return
81
+ except OSError :
82
+ return
83
83
84
+ def start_reader (self ):
84
85
self .reader = threading .Thread (
85
- target = keep_reading ,
86
- name = f'exporter -reader-{ self .pid } ' ,
86
+ target = LabgridComponent . keep_reading ,
87
+ name = f'{ self . __class__ . __name__ } -reader-{ self .pid } ' ,
87
88
args = (self .spawn ,), daemon = True )
88
89
self .reader .start ()
89
90
90
- def stop (self ):
91
- logging .info ("stopping exporter pid=%s" , self .spawn .pid )
92
- self .spawn .close (force = True )
93
- assert not self .spawn .isalive ()
91
+ def stop_reader (self ):
94
92
self .reader .join ()
95
93
96
- self .spawn = None
97
94
self .reader = None
98
95
99
96
def isalive (self ):
@@ -108,6 +105,28 @@ def pid(self):
108
105
return self .spawn .pid
109
106
110
107
108
+ class Exporter (LabgridComponent ):
109
+ def __init__ (self , config , cwd ):
110
+ super ().__init__ (cwd )
111
+ self .config = config
112
+
113
+ def start (self ):
114
+ assert self .spawn is None
115
+ assert self .reader is None
116
+
117
+ self .spawn = pexpect .spawn (
118
+ f'labgrid-exporter --name testhost { self .config } ' ,
119
+ logfile = Prefixer (sys .stdout .buffer , 'exporter' ),
120
+ cwd = self .cwd )
121
+ try :
122
+ self .spawn .expect ('exporter name: testhost' )
123
+ self .spawn .expect ('connected to exporter' )
124
+ except Exception as e :
125
+ raise Exception (f"exporter startup failed with { self .spawn .before } " ) from e
126
+
127
+ self .start_reader ()
128
+
129
+
111
130
@pytest .fixture (scope = 'function' )
112
131
def target ():
113
132
return Target ('Test' )
0 commit comments