casaconfig 1.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,860 @@
1
+ import unittest
2
+ import os, shutil, stat, sys, subprocess, time
3
+ from datetime import date, timedelta
4
+ import site
5
+ sitepackages = site.getsitepackages()[0]
6
+
7
+ import casaconfig
8
+
9
+ class casaconfig_test(unittest.TestCase):
10
+
11
+ def setUp(self):
12
+ if os.path.isfile(os.path.expanduser("~/.casa/config.py")):
13
+ os.replace(os.path.expanduser("~/.casa/config.py"), os.path.expanduser("~/.casa/config.py.user"))
14
+
15
+ self.test_configpath = os.path.join(os.path.expanduser("~/.casa/"),"config.py")
16
+ self.test_siteconfigpath = os.path.join(os.getcwd(),'testsiteconfig.py')
17
+
18
+ # testmeasures is used for measures-only tests
19
+ # testrundata is used for full data install tests (at least casarundata is installed, which includes measures)
20
+ # emptymeasures is used to test failure modes
21
+
22
+ self.testMeasPath = os.path.join(os.getcwd(),'testmeasures')
23
+ self.testRundataPath = os.path.join(os.getcwd(),'testrundata')
24
+ self.emptyPath = os.path.join(os.getcwd(), 'emptymeasures')
25
+
26
+ # this is set when needed and used if set, only query available versions once
27
+ self.meas_avail = None
28
+
29
+ # just in case something has left them populated
30
+ self.rmTestDirs()
31
+
32
+ def tearDown(self):
33
+ for f in [self.test_configpath, self.test_siteconfigpath]:
34
+ if os.path.isfile(f):
35
+ os.remove(f)
36
+
37
+ self.rmTestDirs();
38
+
39
+ if os.path.isfile(os.path.expanduser("~/.casa/config.py.user")):
40
+ os.replace(os.path.expanduser("~/.casa/config.py.user"), os.path.expanduser("~/.casa/config.py"))
41
+
42
+ def rmTestDirs(self):
43
+ if os.path.exists(self.testMeasPath):
44
+ shutil.rmtree(self.testMeasPath)
45
+
46
+ if os.path.exists(self.testRundataPath):
47
+ # make sure this has write permissions, they can get lost if a test fails badly
48
+ pstat = os.stat(self.testRundataPath).st_mode
49
+ yes_write = stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH
50
+ pstat = pstat | yes_write
51
+ os.chmod(self.testRundataPath,pstat)
52
+ shutil.rmtree(self.testRundataPath)
53
+
54
+ if os.path.exists(self.emptyPath):
55
+ shutil.rmtree(self.emptyPath)
56
+
57
+ def get_meas_avail(self) :
58
+ # this caches the list of available measures so measures_available() should only be called once for use by all tests
59
+ if self.meas_avail is None:
60
+ self.meas_avail = casaconfig.measures_available()
61
+ return self.meas_avail
62
+
63
+ def populate_testmeasures(self):
64
+ # ensures that there's some casaconfig populated measures at self.testMeasPath
65
+ # if there's a known version there it leave it as is
66
+ # if the data info is None then it populates it with the most recent measures and extracts the observatory table
67
+ # the data info version can not be illegal or unknown (something unexpected is already there)
68
+ # this function works as a test, but it happens on demand in an attempt to limit the calls to update_measures
69
+
70
+ dataInfo = casaconfig.get_data_info(self.testMeasPath, type='measures')
71
+ if dataInfo is not None:
72
+ version = dataInfo['version']
73
+ self.assertTrue(not (version=="unknown" or version=="illegal"), "unexpected measures version in populate_testmeasures at %s : %s" % (self.testMeasPath,version))
74
+
75
+ # it seems to be a valid measures installation, leave as is
76
+ return
77
+
78
+ # install the most recent available
79
+ # since this is new measures install at path it needs to extract the Observatory table, which requires force to be True
80
+ measVers = self.get_meas_avail()[-1]
81
+ casaconfig.measures_update(self.testMeasPath, version=measVers, force=True, use_astron_obs_table=True)
82
+
83
+ # check version (this makes this function a test, but it's used as needed elsewhere)
84
+ dataInfo = casaconfig.get_data_info(self.testMeasPath, type='measures')
85
+ self.assertTrue(dataInfo['version']==measVers,"unexpected version installed by populate_testmeasures at %s : %s != %s" % (self.testMeasPath, dataInfo['version'], measVers))
86
+
87
+ def populate_testrundata(self):
88
+ # ensures that there's some casaconfig populated casarundata at self.testMeasPath
89
+ # if there's a known version there it leave it as is
90
+ # if the data info is None then it populates it with the most recent casarundata
91
+ # the data info version can not be illegal or unknown (something unexpected is already there)
92
+ # this function works as a test, but it happens on demand in an attempt to limit the calls to pull_data
93
+
94
+ dataInfo = casaconfig.get_data_info(self.testRundataPath, type='casarundata')
95
+ if dataInfo is not None:
96
+ version = dataInfo['version']
97
+ self.assertTrue(not (version=="unknown" or version=="illegal"), "unexpected casarundata version in populate_testrundata at %s : %s" % (self.testRundataPath,version))
98
+
99
+ # it seems to be a valid casarundata installation, leave as is
100
+ return
101
+
102
+ # make sure it has write permissions, they can get lost if a test fails badly
103
+ if os.path.exists(self.testRundataPath):
104
+ pstat = os.stat(self.testRundataPath).st_mode
105
+ yes_write = stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH
106
+ pstat = pstat | yes_write
107
+ os.chmod(self.testRundataPath,pstat)
108
+
109
+ # watch for failed test attempts that tweak the readme files : restore the original readme files if they are found
110
+ if os.path.exists(os.path.join(self.testRundataPath,'readme.txt.orig')):
111
+ os.replace(os.path.join(self.testRundataPath,'readme.txt.orig'), os.path.join(self.testRundataPath,'readme.txt'))
112
+ if os.path.exists(os.path.join(self.testRundataPath,'geodetic/readme.txt.orig')):
113
+ os.replace(os.path.join(self.testRundataPath,'geodetic/readme.txt.orig'), os.path.join(self.testRundataPath,'geodetic/readme.txt.orig'))
114
+
115
+ # install the most recent available
116
+ # this query is cheap, don't cache it
117
+ rundataVers = casaconfig.data_available()[-1]
118
+ casaconfig.pull_data(self.testRundataPath, version=rundataVers)
119
+
120
+ # check version (this makes this function a test, but it's used as needed elsewhere)
121
+ dataInfo = casaconfig.get_data_info(self.testRundataPath, type='casarundata')
122
+ self.assertTrue(dataInfo['version']==rundataVers, "unexpected version installed by populate_testrundata at %s : %s != %s" % (self.testRundataPath, dataInfo['version'], rundataVers))
123
+
124
+ def test_file_exists(self):
125
+ '''Test Default config.py exists in casaconfig module'''
126
+ self.assertTrue(os.path.isfile('{}/casaconfig/config.py'.format(sitepackages)))
127
+
128
+ @unittest.skipIf(not os.path.exists(os.path.join(sitepackages,'casatools')), "casatools not found")
129
+ def test_import_casatools_bad_measurespath(self):
130
+ '''Test that import casatools will return ImportError with measurespath set to an empty directory that cannot be written to'''
131
+ # Due to casatools / casaconfig caching, run the import of casatools and read stdout/stderr for ImportError
132
+ # after first creating an empty measurespath directory with the write permissions turned off
133
+ # and then creating a test config file using the path to that directory as measurespath
134
+
135
+ if (not os.path.exists(self.emptyPath)):
136
+ os.mkdir(self.emptyPath)
137
+
138
+ # the current permissions
139
+ pstat = stat.S_IMODE(os.stat(self.emptyPath).st_mode)
140
+ # a bitmask that's the opposite of all of the write permission bits
141
+ no_write = ~stat.S_IWUSR & ~stat.S_IWGRP & ~stat.S_IWOTH
142
+ # remove the write permissions
143
+ pstat = pstat & no_write
144
+ os.chmod(self.emptyPath,pstat)
145
+
146
+ f = open(self.test_configpath,"w")
147
+ f.write("# Test Config File\n")
148
+ f.write("measurespath='%s'\n" % self.emptyPath)
149
+ f.close()
150
+
151
+ # ensure that any site config is not used
152
+ proc = subprocess.Popen('{} -c "import casatools" --nositeconfig'.format(sys.executable), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
153
+ (output, _) = proc.communicate()
154
+
155
+ p_status = proc.wait()
156
+ ref = True if "NotWritable" in str(output) else False
157
+ self.assertTrue(ref, "NotWritable Not Found")
158
+
159
+ def test_casaconfig_measures_available(self):
160
+ '''Test That Today or Yesterday measures data is returned'''
161
+
162
+ today = date.today()
163
+ yesterday = today - timedelta(days=1)
164
+ measuresdata_today = "WSRT_Measures_{}-160001.ztar".format(today.strftime("%Y%m%d"))
165
+ measuresdata_yesterday= "WSRT_Measures_{}-160001.ztar".format(yesterday.strftime("%Y%m%d"))
166
+
167
+ self.assertTrue(any(elem in self.get_meas_avail() for elem in [measuresdata_today, measuresdata_yesterday]))
168
+
169
+ def test_casaconfig_measures_update(self):
170
+ '''Test downgrade to upgrade measures data to location'''
171
+
172
+ # make sure something is already there
173
+ self.populate_testmeasures()
174
+ versInstalled = casaconfig.get_data_info(path=self.testMeasPath, type='measures')['version']
175
+
176
+ # find the most recent version not installed at testPath
177
+ vers = None
178
+ for vers in reversed(self.get_meas_avail()):
179
+ if vers != versInstalled:
180
+ break
181
+
182
+ # install that one
183
+ casaconfig.measures_update(self.testMeasPath, version=vers, logger=None)
184
+
185
+ # make sure that was just installed
186
+ newVers = casaconfig.get_data_info(path=self.testMeasPath, type='measures')['version']
187
+
188
+ self.assertTrue(newVers == vers)
189
+
190
+ @unittest.skipIf(not os.path.exists(os.path.join(sitepackages,'casatools')), "casatools not found")
191
+ def test_read_measurespath_from_user_config(self):
192
+ '''Test casaconfig downloads specific measures data to location and casatools reads that data location'''
193
+
194
+ # this requires that there be the full casarundata already installed
195
+ self.populate_testrundata()
196
+
197
+ # use the most recent version
198
+ vers = self.get_meas_avail()[-1]
199
+
200
+ # if that version is the one already installed then go back to the previous measures version
201
+ if (casaconfig.get_data_info(path=self.testRundataPath, type='measures')['version'] == vers) :
202
+ vers = self.get_meas_avail()[-2]
203
+
204
+ # start a fresh process with a test config file setting measurespath to the testrundata
205
+ # directory; in that process, import casaconfig and update the measures data to vers
206
+ # then import casatools and verify that utils measurespath agrees with casaconfig measurespath
207
+
208
+ fc = open(self.test_configpath,"w")
209
+ fc.write("# Test Config File\n")
210
+ fc.write('measurespath = "{}"\n'.format(self.testRundataPath))
211
+ fc.close()
212
+
213
+ # the test script to execute via the "-c" option in the process
214
+ # the string is enclosed in double quotes when used, so only single quotes should appear within the string
215
+ test_string = ''
216
+ test_string += "import casaconfig; "
217
+ test_string += "casaconfig.measures_update(path='{}',version='{}',force=False,logger=None); ".format(self.testRundataPath,vers)
218
+ test_string += "import casatools; "
219
+ test_string += "assert('{}' == casatools.utils.utils().measurespath())".format(self.testRundataPath)
220
+
221
+ # ensure that no site config file is used
222
+ proc = subprocess.Popen('{} -c "{}" --nositeconfig'.format(sys.executable,test_string), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
223
+ (output, _) = proc.communicate()
224
+
225
+ p_status = proc.wait()
226
+
227
+ ref = False if "AssertionError" in str(output) else True
228
+ self.assertTrue(ref, "AssertionError seen in output : expected utils().measurespath() was not seen")
229
+
230
+ # final check that the expected version is now installed
231
+ installedVers = casaconfig.get_data_info(path=self.testRundataPath,type='measures')['version']
232
+ self.assertTrue(installedVers == vers, "expected version was not installed : %s != %s" % (installedVers, vers))
233
+
234
+ @unittest.skipIf(not os.path.exists(os.path.join(sitepackages,'casatools')), "casatools not found")
235
+ def test_auto_update_measures(self):
236
+ '''Test Automatic Measures Updates to measurespath'''
237
+
238
+ # this requires that there be the full casarundata already installed
239
+ self.populate_testrundata()
240
+
241
+ # make sure the installed version is not the most recent one
242
+ latestVers = self.get_meas_avail()[-1]
243
+ versInstalled = casaconfig.get_data_info(self.testRundataPath,type='measures')['version']
244
+ if (versInstalled == latestVers) :
245
+ # force an install to the version before the most recent
246
+ casaconfig.measures_update(self.testRundataPath, version=self.get_meas_avail()[-2], force=True, logger=None)
247
+ # double check that that worked
248
+ versInstalled = casaconfig.get_data_info(self.testRundataPath,type='measures')['version']
249
+ self.assertTrue(versInstalled == self.get_meas_avail()[-2],"downgrade of measures to %s failed at %s, installed is %s" % (self.get_meas_avail()[-2], self.testRundataPath, versInstalled))
250
+
251
+ # make sure the timestamp the measures readme files is more than 24 hrs old
252
+ measuresReadmePath = os.path.join(self.testRundataPath,'geodetic/readme.txt')
253
+ olderTime = time.time()-2.*24*60*60
254
+ os.utime(measuresReadmePath,(olderTime,olderTime))
255
+
256
+ f = open(self.test_configpath,"w")
257
+ f.write("# Test Config File\n")
258
+ f.write('measurespath = "{}"\n'.format(self.testRundataPath))
259
+ f.write('measures_auto_update = True\n')
260
+ f.write('data_auto_update = False\n')
261
+ f.close()
262
+
263
+ # start a new casatools, which should update the measures to the most recent version
264
+ # make sure no site config file is used
265
+ proc = subprocess.Popen('{} -c "import casatools" --nositeconfig'.format(sys.executable), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
266
+ (output, _) = proc.communicate()
267
+
268
+ p_status = proc.wait()
269
+
270
+ # output should contain the latest version string
271
+ ref = self.get_meas_avail()[-1] in str(output)
272
+ self.assertTrue(ref, "Update Failed")
273
+
274
+ @unittest.skipIf(not os.path.exists(os.path.join(sitepackages,'casatools')), "casatools not found")
275
+ def test_auto_install_data(self):
276
+ '''Test auto install of all data to measurespath on casatools startup'''
277
+
278
+ # make sure that testrundata does not exist
279
+ if os.path.exists(self.testRundataPath):
280
+ shutil.rmtree(self.testRundataPath)
281
+
282
+ # set the user's config file to use testRundataPath as measurespath, with auto updates on
283
+ fc = open(self.test_configpath,"w")
284
+ fc.write("# Test Config File\n")
285
+ fc.write('measurespath = "{}"\n'.format(self.testRundataPath))
286
+ fc.write('data_auto_update = True\n')
287
+ fc.write('measures_auto_update = True\n')
288
+ fc.close()
289
+
290
+ # it should fail if this attempt is made when that location does not exist
291
+ # ensure that no site config file is used
292
+ proc = subprocess.Popen('{} -c "import casatools" --nositeconfig'.format(sys.executable), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
293
+ (output, _) = proc.communicate()
294
+
295
+ p_status = proc.wait()
296
+ ref = True if "AutoUpdatesNotAllowed" in str(output) else False
297
+ self.assertTrue(ref, "AutoUpdatesNotAllowed not found")
298
+
299
+ # create testRundataPath and try again
300
+ os.mkdir(self.testRundataPath)
301
+ proc = subprocess.Popen('{} -c "import casatools" --nositeconfig'.format(sys.executable), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
302
+ (output, _) = proc.communicate()
303
+
304
+ p_status = proc.wait()
305
+ ref = True if "ImportError" not in str(output) else False
306
+ self.assertTrue(ref, "ImportError Found")
307
+
308
+ # final check that the expected versions were found
309
+ # if the most recent rundata is < 1 day old then measuresdata will also be < 1 day old the most recent measures on astron
310
+ # may be newer, also, do not rely on the cached measures versions to check here
311
+ dataInfo = casaconfig.get_data_info(self.testRundataPath)
312
+ expectedDataVersion = casaconfig.data_available()[-1]
313
+ availMeasures = casaconfig.measures_available()
314
+ expectedMeasVersion = availMeasures[-1]
315
+ ref = (dataInfo['casarundata']['version'] == expectedDataVersion) and (dataInfo['measures']['version'] == expectedMeasVersion)
316
+ if not ref:
317
+ # try the penultimate version
318
+ expectedMeasVersion = availMeasures[-2]
319
+ ref = (dataInfo['casarundata']['version'] == expectedDataVersion) and (dataInfo['measures']['version'] == expectedMeasVersion)
320
+
321
+ self.assertTrue(ref, "Expected versions not installed")
322
+
323
+ def test_daily_update(self):
324
+ '''test that updates do not happen if the installed data is less than 1 day old and do happen when they are older'''
325
+
326
+ # start with an empty testrundata
327
+ if os.path.exists(self.testRundataPath):
328
+ shutil.rmtree(self.testRundataPath)
329
+
330
+ # populate it with an older version of casarundata
331
+ oldVersion = casaconfig.data_available()[-2]
332
+ casaconfig.pull_data(self.testRundataPath, version=oldVersion)
333
+
334
+ # get the versions installed
335
+ dataInfo = casaconfig.get_data_info(self.testRundataPath)
336
+ rundataVers = dataInfo['casarundata']['version']
337
+ rundataAge = dataInfo['casarundata']['age']
338
+ measVers = dataInfo['measures']['version']
339
+ measAge = dataInfo['measures']['age']
340
+
341
+ self.assertTrue(oldVersion == rundataVers, "old version was not installed as expected")
342
+ self.assertTrue((rundataAge < 1.0) and (measAge < 1.0), "recent installed old versions do not have the expected recent age")
343
+
344
+ # updates should do nothing
345
+ casaconfig.data_update(self.testRundataPath)
346
+ casaconfig.measures_update(self.testRundataPath)
347
+
348
+ # versions should be unchanged
349
+ dataInfo = casaconfig.get_data_info(self.testRundataPath)
350
+ checkRundataVers = dataInfo['casarundata']['version']
351
+ checkMeasVers = dataInfo['measures']['version']
352
+
353
+ self.assertTrue((checkRundataVers == rundataVers) and (checkMeasVers == measVers), "unexpected update of recently installed data")
354
+
355
+ # back date measures and try to update
356
+ measuresReadmePath = os.path.join(self.testRundataPath,'geodetic/readme.txt')
357
+ olderTime = time.time()-2.*24*60*60
358
+ os.utime(measuresReadmePath,(olderTime,olderTime))
359
+
360
+ # measures should update, rundata should not
361
+ casaconfig.data_update(self.testRundataPath)
362
+ casaconfig.measures_update(self.testRundataPath)
363
+
364
+ dataInfo = casaconfig.get_data_info(self.testRundataPath)
365
+ checkRundataVers = dataInfo['casarundata']['version']
366
+ checkRundataAge = dataInfo['casarundata']['age']
367
+ checkMeasVers = dataInfo['measures']['version']
368
+ checkMeasAge = dataInfo['measures']['age']
369
+
370
+ self.assertTrue((checkRundataVers == rundataVers) and (checkMeasVers != measVers), "versions are not as expected after a measures update")
371
+
372
+ # backdate the rundata
373
+ rundataReadmePath = os.path.join(self.testRundataPath,'readme.txt')
374
+ os.utime(rundataReadmePath,(olderTime,olderTime))
375
+
376
+ # data should update now
377
+ casaconfig.data_update(self.testRundataPath)
378
+ # measures should also update now, unless the casaconfig that was just installed was built very recently (within the last day)
379
+ # first, check the installed measures version, if that IS the most recent version then artifically install the previous one
380
+ measDataVers = casaconfig.get_data_info(self.testRundataPath, type='measures')['version']
381
+ if measDataVers == self.get_meas_avail()[-1] :
382
+ casaconfig.measures_update(self.testRundataPath,version=self.get_meas_avail()[-2])
383
+ # and backdate the measures data to be sure it updates
384
+ os.utime(measuresReadmePath,(olderTime,olderTime))
385
+
386
+ # now we should expect measures_update to install the most recent version
387
+ casaconfig.measures_update(self.testRundataPath)
388
+
389
+ dataInfo = casaconfig.get_data_info(self.testRundataPath)
390
+ checkRundataVers = dataInfo['casarundata']['version']
391
+ checkMeasVers = dataInfo['measures']['version']
392
+
393
+ # IF a new measures tarball was made available while this test was running then the updated measure here may be one more than the previous
394
+ # update, so long as this is the most recent measures this test is OK (do not rely on the cached values for this check)
395
+ expectedMeasVers = casaconfig.measures_available()[-1]
396
+ self.assertTrue((checkRundataVers != rundataVers) and (checkMeasVers == expectedMeasVers), "versions are not as expected after a data update")
397
+
398
+ def do_config_check(self, expectedDict, noconfig, nositeconfig):
399
+ '''Launch a separate python to load the config files, using --noconfig --nositeconfig as requested, the expectedDict contains expected values'''
400
+
401
+ # the test script produces a dictionary of key,value where the keys are
402
+ # loaded, failed, measurespath, measures_auto_update, and data_auto_update
403
+ # all of the values are strings
404
+ # loaded and failed are len(load_success()) and len(load_failure)
405
+ # measurespath, measures_auto_update, data_auto_update are those values
406
+
407
+ # returns a non-empty string if any of the expected values aren't found in the parsed output of from python subprocess
408
+
409
+ # used by test_config_import
410
+
411
+ msgs = ""
412
+
413
+ # test script to execute via the "-c" option in the process
414
+ # the string is enclosed in double quotes when used, so only single quotes should appear within the string
415
+ test_string = ''
416
+ test_string += "from casaconfig import config; "
417
+ test_string += "print('failures :%s ' % config.load_failure()); "
418
+ test_string += "print('loaded : %s' % len(config.load_success())); "
419
+ test_string += "print('failed : %s' % len(config.load_failure())); "
420
+ test_string += "print('measurespath : %s' % config.measurespath); "
421
+ test_string += "print('measures_auto_update : %s' % config.measures_auto_update); "
422
+ test_string += "print('data_auto_update : %s' % config.data_auto_update); "
423
+
424
+ args = ""
425
+ if noconfig:
426
+ args += "--noconfig "
427
+ if nositeconfig:
428
+ args += "--nositeconfig "
429
+
430
+ proc = subprocess.Popen('{} -c "{}" {}'.format(sys.executable,test_string,args), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
431
+ (output, _) = proc.communicate()
432
+
433
+ p_status = proc.wait()
434
+
435
+ resultsDict = {}
436
+ for l in output.decode('utf-8').splitlines():
437
+ parts = l.split(":")
438
+ if (len(parts) != 2): continue
439
+
440
+ resultsDict[parts[0].strip()] = parts[1].strip()
441
+
442
+ for k in expectedDict:
443
+ if k not in resultsDict:
444
+ msgs += 'expected result for "{}" not found; '.format(k)
445
+ else:
446
+ if expectedDict[k] != resultsDict[k]:
447
+ msgs += 'unexpected result for "{}" : "{}" vs "{}"; '.format(k, expectedDict[k], resultsDict[k])
448
+ return msgs
449
+
450
+
451
+ def test_config_import(self):
452
+ '''Tests of the config import'''
453
+
454
+ # the user's config file
455
+ f = open(self.test_configpath,"w")
456
+ f.write("# Test Config File\n")
457
+ f.write('measurespath = "{}"\n'.format(self.testRundataPath))
458
+ f.close()
459
+
460
+ # the test site config file
461
+ f = open(self.test_siteconfigpath,"w")
462
+ f.write("#Test Siteconfig File\n")
463
+ f.write('measurespath = "/path/doesnot/exist"\n')
464
+ f.write('measures_auto_update = False\n')
465
+ f.write('data_auto_update = False\n')
466
+ f.close()
467
+
468
+
469
+ # set the casasiteconfig env value
470
+ os.environ['CASASITECONFIG'] = self.test_siteconfigpath
471
+
472
+ expectedDict = {"loaded":"3",
473
+ "failed":"0",
474
+ "measurespath":self.testRundataPath,
475
+ "measures_auto_update":"False",
476
+ "data_auto_update":"False"}
477
+
478
+ # test with both, 2 files loaded, no errors, user's measurespath, site's auto update values
479
+
480
+ msgs = self.do_config_check(expectedDict, noconfig=False, nositeconfig=False)
481
+ self.assertTrue(len(msgs)==0, "failed : config import with both user and site config files : " + msgs)
482
+
483
+ # test with just the defaults
484
+
485
+ expectedDict["loaded"] = "1"
486
+ expectedDict["measurespath"] = os.path.abspath(os.path.expanduser('~/.casa/data'))
487
+ expectedDict["measures_auto_update"] = "True"
488
+ expectedDict["data_auto_update"] = "True"
489
+ msgs = self.do_config_check(expectedDict, noconfig=True, nositeconfig=True)
490
+ self.assertTrue(len(msgs)==0, "failed : config import of only defaults : " + msgs)
491
+
492
+ # test with user + defaults, no site
493
+ expectedDict["loaded"] = "2"
494
+ expectedDict["measurespath"] = self.testRundataPath
495
+ msgs = self.do_config_check(expectedDict, noconfig=False, nositeconfig=True)
496
+ self.assertTrue(len(msgs)==0, "failed : config import ignoring site : " + msgs)
497
+
498
+ # test with an error in the site config, should report the error and move on, leaving just the user + default expected values
499
+
500
+ f = open(self.test_siteconfigpath,"w")
501
+ f.write("#Test Siteconfig File with error\n")
502
+ # the value whatever does not exist
503
+ f.write('measurespath = whatever\n')
504
+ f.write('measures_auto_update = False\n')
505
+ f.write('data_auto_update = False\n')
506
+ f.close()
507
+
508
+ # expect 1 failure, with the user+defaults being the final values
509
+ expectedDict["failed"] = "1"
510
+
511
+ msgs = self.do_config_check(expectedDict, noconfig=False, nositeconfig=False)
512
+ self.assertTrue(len(msgs)==0, "failed : config import with error in site : " + msgs)
513
+
514
+ # unset CASASITECONFIG to ignore the site config file and try again, the problem site config should not be seen
515
+ os.environ.pop('CASASITECONFIG')
516
+
517
+ # the failure is now gone
518
+ expectedDict["failed"] = "0"
519
+
520
+ # but there may be a real site config file in a standard location
521
+ # auto updates are assumed to be on if there is a site config file
522
+ has_site_config = os.path.exists("/opt/casa/siteconfig.py") or os.path.exists("/home/casa/casasiteconfig.py")
523
+ if has_site_config:
524
+ expectedDict["loaded"] = "3"
525
+ expectedDict["measures_auto_update"] = "False"
526
+ expectedDict["data_auto_update"] = "False"
527
+
528
+ msgs = self.do_config_check(expectedDict, noconfig=False, nositeconfig=False)
529
+ self.assertTrue(len(msgs)==0, "failed : config import with CASASITECONFIG unset : " + msgs)
530
+
531
+ def test_exceptions_no_data(self):
532
+ '''test that exceptions that do not require any data happen when expected'''
533
+ from casaconfig.private.get_data_lock import get_data_lock
534
+
535
+ # the tests expect this to not exist, it would be insane if it does, but check anyway
536
+ self.assertFalse(os.path.exists('/this/does/not/exist/'),"/this/does/not/exist/ shouldn't exist, but apparently it does")
537
+
538
+ # AutoUpdatesNotAllowed : path does not exist or is not owned by the user
539
+ exceptionSeen = False
540
+ try:
541
+ # path does not exist - measures_update
542
+ casaconfig.measures_update(path='/this/does/not/exist/',auto_update_rules=True)
543
+ except casaconfig.AutoUpdatesNotAllowed:
544
+ exceptionSeen = True
545
+ except Exception as exc:
546
+ print("unexpected exception seen when testing for AutoUpdatesNotAllowed in measures_update using path that should not exist")
547
+ print(str(exc))
548
+
549
+ self.assertTrue(exceptionSeen,"AutoUpdatesNotAllowed not seen as expected in testing measures_update using path that should not exist")
550
+
551
+ exceptionSeen = False
552
+ try:
553
+ # path does not exist - data_update
554
+ casaconfig.data_update(path='/this/does/not/exist/',auto_update_rules=True)
555
+ except casaconfig.AutoUpdatesNotAllowed:
556
+ exceptionSeen = True
557
+ except Exception as exc:
558
+ print("unexpected exception seen when testing for AutoUpdatesNotAllowed in data_update using path that should not exist")
559
+ print(str(exc))
560
+ self.assertTrue(exceptionSeen,"AutoUpdatesNotAllowed not seen as expected in testing data_update using path that should not exist")
561
+
562
+ # path is not owned by the user, /tmp should be useful in most case, but just in case, skip this if that's not a different user
563
+ if (os.stat('/tmp').st_uid == os.getuid()):
564
+ print("skipping AutoUpdatesNotAllowed test for path not owned by the user, /tmp is owned by this user")
565
+ else:
566
+ exceptionSeen = False
567
+ try:
568
+ casaconfig.measures_update(path='/tmp', auto_update_rules=True)
569
+ except casaconfig.AutoUpdatesNotAllowed:
570
+ exceptionSeen = True
571
+ except Exception as exc:
572
+ print("unexpected exception seen when testing for AutoUpdatesNotAllowed in measures_update using path not owned by user")
573
+ print(str(exc))
574
+ self.assertTrue(exceptionSeen,"AutoUpdatesNotAllowed not seen as expected in testing measures_update using path not owned by user")
575
+
576
+ exceptionSeen = False
577
+ try:
578
+ casaconfig.data_update(path='/tmp', auto_update_rules=True)
579
+ except casaconfig.AutoUpdatesNotAllowed:
580
+ exceptionSeen = True
581
+ except Exception as exc:
582
+ print("unexpected exception seen when testing for AutoUpdatesNotAllowed in data_update using path not owned by user")
583
+ print(str(exc))
584
+ self.assertTrue(exceptionSeen,"AutoUpdatesNotAllowed not seen as expected in testing data_update using path not owned by user")
585
+
586
+ # BadLock
587
+ # path to lock file does not exist
588
+ exceptionSeen = False
589
+ try:
590
+ fd = get_data_lock('/this/does/not/exist', 'test_exceptions')
591
+ if fd is not None and not fd.close:
592
+ # this shouldn't happen, but release the lock if it does
593
+ fd.close()
594
+ except casaconfig.BadLock as exc:
595
+ exceptionSeen = True
596
+ except Exception as exc:
597
+ print("unexpected exception seen when testing for BadLock when path does not exist")
598
+ print(str(exc))
599
+ self.assertTrue(exceptionSeen,"BadLock not seen as expected in testing path does not exist")
600
+
601
+ # lock file is not empty
602
+ exceptionSeen = False
603
+ fd = None
604
+ try:
605
+ # create a non-empty lock file in the current directory
606
+ cwd = os.getcwd()
607
+ f = open(os.path.join(cwd,'data_update.lock'),'w')
608
+ f.write("This file is not empty\n")
609
+ f.close()
610
+ fd = get_data_lock(cwd, 'test_exceptions')
611
+ if fd is not None and not fd.close:
612
+ # shouldn't happen, but release the lock if it does
613
+ fd.close()
614
+ except casaconfig.BadLock as exc:
615
+ exceptionSeen = True
616
+ except Exception as exc:
617
+ print("unexpected exception seen when testing for BadLock and lock file is not empty")
618
+ print(str(exc))
619
+ self.assertTrue(exceptionSeen,"BadLock not seen as expected when lock file is not empty")
620
+ # clean up
621
+ os.remove(os.path.join(cwd,'data_update.lock'))
622
+
623
+ # NoReadme
624
+
625
+ # This should work on any non-open path that isn't a measurespath. I think the cwd will
626
+ # work just fine for that purposes.
627
+
628
+ # data_update NoReadme
629
+ try:
630
+ exceptionSeen = False
631
+ # this check happens before the age is determined, so no need to backdate the readme.txt file here
632
+ casaconfig.data_update(os.getcwd())
633
+ except casaconfig.NoReadme as exc:
634
+ exceptionSeen = True
635
+ except Exception as exc:
636
+ print("unexpected exception seen when testing for NoReadme in data_update")
637
+ print(str(exc))
638
+ self.assertTrue(exceptionSeen, "NoReadme not seen from data_update")
639
+
640
+ # measures_update NoReadme
641
+ try:
642
+ exceptionSeen = False
643
+ # this check happens before the age is determined, so no need to backdate the readme.txt file here
644
+ casaconfig.measures_update(os.getcwd())
645
+ except casaconfig.NoReadme as exc:
646
+ exceptionSeen = True
647
+ except Exception as exc:
648
+ print("unexpected exception seen when testing for NoReadme in measures_update")
649
+ print(str(exc))
650
+ self.assertTrue(exceptionSeen, "NoReadme not seen from measures_update")
651
+
652
+ # NotWritable : path is not writable by the user
653
+ # use the emptyPath
654
+ if (not os.path.exists(self.emptyPath)):
655
+ os.mkdir(self.emptyPath)
656
+ # the current permissions
657
+ pstat = stat.S_IMODE(os.stat(self.emptyPath).st_mode)
658
+ # a bitmask that's the opposite of all of the write permission bits
659
+ no_write = ~stat.S_IWUSR & ~stat.S_IWGRP & ~stat.S_IWOTH
660
+ # remove the write permissions
661
+ pstat = pstat & no_write
662
+ os.chmod(self.emptyPath,pstat)
663
+
664
+ # pull_data NotWritable
665
+ try:
666
+ exceptionSeen = False
667
+ casaconfig.pull_data(self.emptyPath)
668
+ except casaconfig.NotWritable as exc:
669
+ exceptionSeen = True
670
+ except Exception as exc:
671
+ print("unexpected exception seen when testing for NotWritable in pull_data")
672
+ print(str(exc))
673
+ self.assertTrue(exceptionSeen, "NotWritable not seen from pull_data")
674
+
675
+ # UnsetMeasurespath : measurespath is None
676
+
677
+ # test script, set measurespath to None after config import, it will be used by the measures_update call
678
+
679
+ test_string_all = ''
680
+ test_string_all += "from casaconfig import config; "
681
+ test_string_all += "config.measurespath = None; "
682
+ test_string_all += "import casaconfig; "
683
+
684
+ # measures_update
685
+
686
+ test_string = test_string_all
687
+ test_string += "casaconfig.measures_update(); "
688
+ # ensure no site config
689
+ proc = subprocess.Popen('{} -c "{}" --nositeconfig'.format(sys.executable,test_string), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
690
+ (output, _) = proc.communicate()
691
+
692
+ p_status = proc.wait()
693
+
694
+ ref = True if "UnsetMeasurespath" in str(output) else False
695
+ self.assertTrue(ref, "UnsetMeasurespath not seen in output for measures_update and measurespath=None")
696
+
697
+ # pull_data
698
+
699
+ test_string = test_string_all
700
+ test_string += "casaconfig.pull_data(); "
701
+ # ensure no site config
702
+ proc = subprocess.Popen('{} -c "{}" --nositeconfig'.format(sys.executable,test_string), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
703
+ (output, _) = proc.communicate()
704
+
705
+ p_status = proc.wait()
706
+
707
+ ref = True if "UnsetMeasurespath" in str(output) else False
708
+ self.assertTrue(ref, "UnsetMeasurespath not seen in output for pull_data and measurespath=None")
709
+
710
+ # data_update
711
+
712
+ test_string = test_string_all
713
+ test_string += "casaconfig.data_update(); "
714
+ proc = subprocess.Popen('{} -c "{}" --nositeconfig'.format(sys.executable,test_string), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
715
+ (output, _) = proc.communicate()
716
+
717
+ p_status = proc.wait()
718
+
719
+ ref = True if "UnsetMeasurespath" in str(output) else False
720
+ self.assertTrue(ref, "UnsetMeasurespath not seen in output for data_update and measurespath=None")
721
+
722
+ # get_data_info
723
+
724
+ test_string = test_string_all
725
+ test_string += "di=casaconfig.get_data_info(); "
726
+ proc = subprocess.Popen('{} -c "{}" --nositeconfig'.format(sys.executable,test_string), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
727
+ (output, _) = proc.communicate()
728
+
729
+ p_status = proc.wait()
730
+
731
+ ref = True if "UnsetMeasurespath" in str(output) else False
732
+ self.assertTrue(ref, "UnsetMeasurespath not seen in output for get_data_info and measurespath=None")
733
+
734
+ # do_auto_updates
735
+
736
+ test_string = test_string_all
737
+ test_string += "casaconfig.do_auto_updates(config); "
738
+ proc = subprocess.Popen('{} -c "{}" --nositeconfig'.format(sys.executable,test_string), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
739
+ (output, _) = proc.communicate()
740
+
741
+ p_status = proc.wait()
742
+
743
+ ref = True if "UnsetMeasurespath" in str(output) else False
744
+ self.assertTrue(ref, "UnsetMeasurespath not seen in output for do_auto_updates and measurespath=None")
745
+
746
+
747
+ def test_exceptions_with_data(self):
748
+ '''test that exceptions that require data happen when expected'''
749
+
750
+ # these tests requires an already installed set of data
751
+ self.populate_testrundata()
752
+
753
+ # BadReadme
754
+
755
+ # create a bad data readme.txt file from the valid one
756
+ dataReadmePath = os.path.join(self.testRundataPath,'readme.txt')
757
+ # read in the valid contents
758
+ with open(dataReadmePath, 'r') as fid:
759
+ readmeLines = fid.readlines()
760
+ # rename it to preserve it
761
+ os.replace(dataReadmePath, os.path.join(self.testRundataPath,'readme.txt.orig'))
762
+
763
+ # create a readme that does not include the manifest, use just the first 3 lines
764
+ with open(dataReadmePath, 'w') as fid:
765
+ fid.writelines(readmeLines[:3])
766
+
767
+ # data_update badreadme test
768
+ try:
769
+ exceptionSeen = False
770
+ # this check happens before the age is determined, so no need to backdate the readme.txt file here
771
+ casaconfig.data_update(self.testRundataPath)
772
+ except casaconfig.BadReadme as exc:
773
+ exceptionSeen = True
774
+ except Exception as exc:
775
+ print("unexpected exception seen when testing for BadRadme in data_update")
776
+ print(str(exc))
777
+ self.assertTrue(exceptionSeen, "BadReadme not seen from data_update")
778
+
779
+ # pull_data badreadme test
780
+ try:
781
+ exceptionSeen = False
782
+ # this check happens before the age is determined, so no need to backdate the readme.txt file here
783
+ casaconfig.pull_data(self.testRundataPath)
784
+ except casaconfig.BadReadme as exc:
785
+ exceptionSeen = True
786
+ except Exception as exc:
787
+ print("unexpected exception seen when testing for BadRadme in pull_data")
788
+ print(str(exc))
789
+ self.assertTrue(exceptionSeen, "BadReadme not seen from pull_data")
790
+
791
+ # restore original data readme.txt
792
+ os.replace(os.path.join(self.testRundataPath,'readme.txt.orig'), dataReadmePath)
793
+
794
+ # do something similar for the measures readme.txt and measures_update
795
+
796
+ # create a bad data readme.txt file from the valid one
797
+ measReadmePath = os.path.join(self.testRundataPath,'geodetic/readme.txt')
798
+ # read in the valid contents
799
+ with open(measReadmePath, 'r') as fid:
800
+ readmeLines = fid.readlines()
801
+ # rename it to preserve it
802
+ os.replace(measReadmePath, os.path.join(self.testRundataPath,'geodetic/readme.txt.orig'))
803
+
804
+ # create a readme with garbage in the the 2nd line
805
+ readmeLines[1] = "this is not right"
806
+ with open(measReadmePath, 'w') as fid:
807
+ fid.writelines(readmeLines[:3])
808
+
809
+ # measures_update badreadme test
810
+ try:
811
+ exceptionSeen = False
812
+ # this check happens before the age is determined, so no need to backdate the readme.txt file here
813
+ casaconfig.measures_update(self.testRundataPath)
814
+ except casaconfig.BadReadme as exc:
815
+ print(str(exc))
816
+ exceptionSeen = True
817
+ except Exception as exc:
818
+ print("unexpected exception seen when testing for BadRadme in measures_update")
819
+ print(str(exc))
820
+ self.assertTrue(exceptionSeen, "BadReadme not seen from measures_update")
821
+
822
+ # restore original measures readme.txt
823
+ os.replace(os.path.join(self.testRundataPath,'geodetic/readme.txt.orig'), measReadmePath)
824
+
825
+ # NotWritable with measures_update requires that there already be measures data there
826
+
827
+ # get the current permissions of the testRundataPath
828
+ orig_pstat = stat.S_IMODE(os.stat(self.testRundataPath).st_mode)
829
+ print('orig_pstat = %s' % orig_pstat)
830
+ # a bitmask that's the opposite of all of the write permission bits
831
+ no_write = ~stat.S_IWUSR & ~stat.S_IWGRP & ~stat.S_IWOTH
832
+ # remove the write permissions
833
+ pstat = orig_pstat & no_write
834
+ print('pstat = %s' % pstat)
835
+ os.chmod(self.testRundataPath,pstat)
836
+
837
+
838
+ # measures_update NotWritable
839
+ try:
840
+ exceptionSeen = False
841
+ # force and update to test this exception
842
+ casaconfig.measures_update(self.testRundataPath, force=True)
843
+ except casaconfig.NotWritable as exc:
844
+ exceptionSeen = True
845
+ except Exception as exc:
846
+ print("unexpected exception seen when testing for NotWritable in measures_update")
847
+ print(str(exc))
848
+ import traceback
849
+ traceback.print_exc()
850
+
851
+ # reset to original permissions before anything else is checked
852
+ os.chmod(self.testRundataPath,orig_pstat)
853
+
854
+ self.assertTrue(exceptionSeen, "NotWritable not seen from measures_update")
855
+
856
+
857
+
858
+ if __name__ == '__main__':
859
+
860
+ unittest.main()