comparison weather_server/logfile_test.py @ 24:20c8ec56e447

logfile: Pull logfile thread out of Logger. This enables automatic garbage collection of Logger instances, since a running thread no longer has a reference to a Logger's self. It separates exclusive management of logfile state into the _writer_thread function, which now opens the file and writes it until it is told to stop by receiving the poison pill.
author Paul Fisher <paul@pfish.zone>
date Sun, 10 Nov 2019 23:07:11 -0500
parents beb42c835c52
children 9bc3687e1e5e
comparison
equal deleted inserted replaced
23:88249e451566 24:20c8ec56e447
1 import contextlib 1 import contextlib
2 import datetime 2 import datetime
3 import os.path
3 import pathlib 4 import pathlib
4 import tempfile 5 import tempfile
6 import threading
5 import unittest 7 import unittest
6 8
7 import bson 9 import bson
8 import pytz 10 import pytz
9 11
10 from . import common 12 from . import common
11 from . import logfile 13 from . import logfile
12 from . import types
13 14
14 15
15 def ts(n): 16 def ts(n):
16 return datetime.datetime.utcfromtimestamp(n).replace(tzinfo=pytz.UTC) 17 return datetime.datetime.utcfromtimestamp(n).replace(tzinfo=pytz.UTC)
17 18
32 def test_empty(self): 33 def test_empty(self):
33 lg = logfile.Logger( 34 lg = logfile.Logger(
34 str(self.log_path), sample_field='x') 35 str(self.log_path), sample_field='x')
35 with contextlib.closing(lg) as logger: 36 with contextlib.closing(lg) as logger:
36 self.assertEqual(logger.data, ()) 37 self.assertEqual(logger.data, ())
38
39 def test_fails_to_open(self):
40 with self.assertRaises(OSError):
41 logfile.Logger(
42 os.path.join(
43 self.temp_dir.name,
44 'nonexistent-directory',
45 'bogus-filename'),
46 sample_field='unimportant')
47
48 def test_del(self):
49 lg = logfile.Logger(
50 str(self.log_path), sample_field='x')
51 del lg
37 52
38 def test_loading(self): 53 def test_loading(self):
39 with self.log_path.open('wb') as outfile: 54 with self.log_path.open('wb') as outfile:
40 outfile.write(common.bson_encode(dict( 55 outfile.write(common.bson_encode(dict(
41 sample_time=ts(123), 56 sample_time=ts(123),
70 str(self.log_path), 85 str(self.log_path),
71 sample_field='sample_time', 86 sample_field='sample_time',
72 )) as logger: 87 )) as logger:
73 logger.write_rows([ 88 logger.write_rows([
74 # Ignored, since it's older than the newest entry. 89 # Ignored, since it's older than the newest entry.
75 types.Reading(ts(100), 999, 666, ts(101)).as_dict(), 90 dict(
76 types.Reading(ts(125), 333, 777, ts(200)).as_dict(), 91 sample_time=ts(100),
92 temp_c=999,
93 rh_pct=666,
94 ingest_time=ts(101),
95 ),
96 dict(
97 sample_time=ts(125),
98 temp_c=333,
99 rh_pct=777,
100 ingest_time=ts(200),
101 ),
77 ]) 102 ])
78 self.assertEqual( 103 self.assertEqual(
79 logger.data, 104 logger.data,
80 ( 105 (
81 types.Reading(ts(123), 420, 69, ts(125)).as_dict(), 106 dict(
82 types.Reading(ts(125), 333, 777, ts(200)).as_dict(), 107 sample_time=ts(123),
108 temp_c=420,
109 rh_pct=69,
110 ingest_time=ts(125),
111 ),
112 dict(
113 sample_time=ts(125),
114 temp_c=333,
115 rh_pct=777,
116 ingest_time=ts(200),
117 ),
83 ) 118 )
84 ) 119 )
85 120
86 self.assertEqual(self.read_bsons(), [ 121 self.assertEqual(self.read_bsons(), [
87 dict( 122 dict(
102 with contextlib.closing(logfile.Logger( 137 with contextlib.closing(logfile.Logger(
103 str(self.log_path), 138 str(self.log_path),
104 sample_field='sample_time', 139 sample_field='sample_time',
105 )) as logger: 140 )) as logger:
106 logger.write_rows([ 141 logger.write_rows([
107 types.Reading(ts(100), 999, 666, ts(101)).as_dict(), 142 dict(
108 types.Reading(ts(125), 333, 777, ts(200)).as_dict(), 143 sample_time=ts(100),
144 temp_c=999,
145 rh_pct=666,
146 ingest_time=ts(101),
147 ),
148 dict(
149 sample_time=ts(125),
150 temp_c=333,
151 rh_pct=777,
152 ingest_time=ts(200),
153 ),
109 ]) 154 ])
110 with self.log_path.open('ab') as outfile: 155 with self.log_path.open('ab') as outfile:
111 outfile.write(common.bson_encode(dict( 156 outfile.write(common.bson_encode(dict(
112 sample_time=ts(1024), 157 sample_time=ts(1024),
113 temp_c=256, 158 temp_c=256,
114 rh_pct=128, 159 rh_pct=128,
115 ingest_time=ts(4096), 160 ingest_time=ts(4096),
116 ))) 161 )))
117 outfile.flush() 162 outfile.flush()
118 self.assertEqual(logger.data, ( 163 self.assertEqual(logger.data, (
119 types.Reading(ts(100), 999, 666, ts(101)).as_dict(), 164 dict(
120 types.Reading(ts(125), 333, 777, ts(200)).as_dict(), 165 sample_time=ts(100),
121 types.Reading(ts(1024), 256, 128, ts(4096)).as_dict(), 166 temp_c=999,
167 rh_pct=666,
168 ingest_time=ts(101),
169 ),
170 dict(
171 sample_time=ts(125),
172 temp_c=333,
173 rh_pct=777,
174 ingest_time=ts(200),
175 ),
176 dict(
177 sample_time=ts(1024),
178 temp_c=256,
179 rh_pct=128,
180 ingest_time=ts(4096),
181 ),
122 )) 182 ))
123 183
124 def read_bsons(self): 184 def read_bsons(self):
125 with self.log_path.open('rb') as infile: 185 with self.log_path.open('rb') as infile:
126 return bson.decode_all( 186 return bson.decode_all(