Coverage for tests/test_exprParserYacc.py : 10%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22"""Simple unit test for exprParser subpackage module.
23"""
25import unittest
27import astropy.time
29from lsst.daf.butler.registry.queries.exprParser import exprTree, TreeVisitor, ParserYacc, ParseError
30from lsst.daf.butler.registry.queries.exprParser.parserYacc import _parseTimeString
33class _Visitor(TreeVisitor):
34 """Trivial implementation of TreeVisitor.
35 """
36 def visitNumericLiteral(self, value, node):
37 return f"N({value})"
39 def visitStringLiteral(self, value, node):
40 return f"S({value})"
42 def visitTimeLiteral(self, value, node):
43 return f"T({value})"
45 def visitRangeLiteral(self, start, stop, stride, node):
46 if stride is None:
47 return f"R({start}..{stop})"
48 else:
49 return f"R({start}..{stop}:{stride})"
51 def visitIdentifier(self, name, node):
52 return f"ID({name})"
54 def visitUnaryOp(self, operator, operand, node):
55 return f"U({operator} {operand})"
57 def visitBinaryOp(self, operator, lhs, rhs, node):
58 return f"B({lhs} {operator} {rhs})"
60 def visitIsIn(self, lhs, values, not_in, node):
61 values = ", ".join([str(val) for val in values])
62 if not_in:
63 return f"!IN({lhs} ({values}))"
64 else:
65 return f"IN({lhs} ({values}))"
67 def visitParens(self, expression, node):
68 return f"P({expression})"
71class ParserLexTestCase(unittest.TestCase):
72 """A test case for ParserYacc
73 """
75 def setUp(self):
76 pass
78 def tearDown(self):
79 pass
81 def testInstantiate(self):
82 """Tests for making ParserLex instances
83 """
84 parser = ParserYacc() # noqa: F841
86 def testEmpty(self):
87 """Tests for empty expression
88 """
89 parser = ParserYacc()
91 # empty expression is allowed, returns None
92 tree = parser.parse("")
93 self.assertIsNone(tree)
95 def testParseLiteral(self):
96 """Tests for literals (strings/numbers)
97 """
98 parser = ParserYacc()
100 tree = parser.parse('1')
101 self.assertIsInstance(tree, exprTree.NumericLiteral)
102 self.assertEqual(tree.value, '1')
104 tree = parser.parse('.5e-2')
105 self.assertIsInstance(tree, exprTree.NumericLiteral)
106 self.assertEqual(tree.value, '.5e-2')
108 tree = parser.parse("'string'")
109 self.assertIsInstance(tree, exprTree.StringLiteral)
110 self.assertEqual(tree.value, 'string')
112 tree = parser.parse("10..20")
113 self.assertIsInstance(tree, exprTree.RangeLiteral)
114 self.assertEqual(tree.start, 10)
115 self.assertEqual(tree.stop, 20)
116 self.assertEqual(tree.stride, None)
118 tree = parser.parse("-10 .. 10:5")
119 self.assertIsInstance(tree, exprTree.RangeLiteral)
120 self.assertEqual(tree.start, -10)
121 self.assertEqual(tree.stop, 10)
122 self.assertEqual(tree.stride, 5)
124 # more extensive tests of time parsing is below
125 tree = parser.parse("T'51544.0'")
126 self.assertIsInstance(tree, exprTree.TimeLiteral)
127 self.assertEqual(tree.value, astropy.time.Time(51544.0, format="mjd", scale="tai"))
129 tree = parser.parse("T'2020-03-30T12:20:33'")
130 self.assertIsInstance(tree, exprTree.TimeLiteral)
131 self.assertEqual(tree.value, astropy.time.Time("2020-03-30T12:20:33", format="isot", scale="utc"))
133 def testParseIdentifiers(self):
134 """Tests for identifiers
135 """
136 parser = ParserYacc()
138 tree = parser.parse('a')
139 self.assertIsInstance(tree, exprTree.Identifier)
140 self.assertEqual(tree.name, 'a')
142 tree = parser.parse('a.b')
143 self.assertIsInstance(tree, exprTree.Identifier)
144 self.assertEqual(tree.name, 'a.b')
146 def testParseParens(self):
147 """Tests for identifiers
148 """
149 parser = ParserYacc()
151 tree = parser.parse('(a)')
152 self.assertIsInstance(tree, exprTree.Parens)
153 self.assertIsInstance(tree.expr, exprTree.Identifier)
154 self.assertEqual(tree.expr.name, 'a')
156 def testUnaryOps(self):
157 """Tests for unary plus and minus
158 """
159 parser = ParserYacc()
161 tree = parser.parse('+a')
162 self.assertIsInstance(tree, exprTree.UnaryOp)
163 self.assertEqual(tree.op, '+')
164 self.assertIsInstance(tree.operand, exprTree.Identifier)
165 self.assertEqual(tree.operand.name, 'a')
167 tree = parser.parse('- x.y')
168 self.assertIsInstance(tree, exprTree.UnaryOp)
169 self.assertEqual(tree.op, '-')
170 self.assertIsInstance(tree.operand, exprTree.Identifier)
171 self.assertEqual(tree.operand.name, 'x.y')
173 def testBinaryOps(self):
174 """Tests for binary operators
175 """
176 parser = ParserYacc()
178 tree = parser.parse('a + b')
179 self.assertIsInstance(tree, exprTree.BinaryOp)
180 self.assertEqual(tree.op, '+')
181 self.assertIsInstance(tree.lhs, exprTree.Identifier)
182 self.assertIsInstance(tree.rhs, exprTree.Identifier)
183 self.assertEqual(tree.lhs.name, 'a')
184 self.assertEqual(tree.rhs.name, 'b')
186 tree = parser.parse('a - 2')
187 self.assertIsInstance(tree, exprTree.BinaryOp)
188 self.assertEqual(tree.op, '-')
189 self.assertIsInstance(tree.lhs, exprTree.Identifier)
190 self.assertIsInstance(tree.rhs, exprTree.NumericLiteral)
191 self.assertEqual(tree.lhs.name, 'a')
192 self.assertEqual(tree.rhs.value, '2')
194 tree = parser.parse('2 * 2')
195 self.assertIsInstance(tree, exprTree.BinaryOp)
196 self.assertEqual(tree.op, '*')
197 self.assertIsInstance(tree.lhs, exprTree.NumericLiteral)
198 self.assertIsInstance(tree.rhs, exprTree.NumericLiteral)
199 self.assertEqual(tree.lhs.value, '2')
200 self.assertEqual(tree.rhs.value, '2')
202 tree = parser.parse('1.e5/2')
203 self.assertIsInstance(tree, exprTree.BinaryOp)
204 self.assertEqual(tree.op, '/')
205 self.assertIsInstance(tree.lhs, exprTree.NumericLiteral)
206 self.assertIsInstance(tree.rhs, exprTree.NumericLiteral)
207 self.assertEqual(tree.lhs.value, '1.e5')
208 self.assertEqual(tree.rhs.value, '2')
210 tree = parser.parse('333%76')
211 self.assertIsInstance(tree, exprTree.BinaryOp)
212 self.assertEqual(tree.op, '%')
213 self.assertIsInstance(tree.lhs, exprTree.NumericLiteral)
214 self.assertIsInstance(tree.rhs, exprTree.NumericLiteral)
215 self.assertEqual(tree.lhs.value, '333')
216 self.assertEqual(tree.rhs.value, '76')
218 # tests for overlaps operator
219 tree = parser.parse("region1 OVERLAPS region2")
220 self.assertIsInstance(tree, exprTree.BinaryOp)
221 self.assertEqual(tree.op, "OVERLAPS")
222 self.assertIsInstance(tree.lhs, exprTree.Identifier)
223 self.assertIsInstance(tree.rhs, exprTree.Identifier)
225 # time ranges with literals
226 tree = parser.parse("(T'2020-01-01', T'2020-01-02') overlaps (T'2020-01-01', T'2020-01-02')")
227 self.assertIsInstance(tree, exprTree.BinaryOp)
228 self.assertEqual(tree.op, "OVERLAPS")
229 self.assertIsInstance(tree.lhs, exprTree.TupleNode)
230 self.assertIsInstance(tree.rhs, exprTree.TupleNode)
232 # but syntax allows anything, it's visitor responsibility to decide
233 # what are the right operands
234 tree = parser.parse("x+y Overlaps function(x-y)")
235 self.assertIsInstance(tree, exprTree.BinaryOp)
236 self.assertEqual(tree.op, "OVERLAPS")
237 self.assertIsInstance(tree.lhs, exprTree.BinaryOp)
238 self.assertIsInstance(tree.rhs, exprTree.FunctionCall)
240 def testIsIn(self):
241 """Tests for IN
242 """
243 parser = ParserYacc()
245 tree = parser.parse("a in (1,2,'X')")
246 self.assertIsInstance(tree, exprTree.IsIn)
247 self.assertFalse(tree.not_in)
248 self.assertIsInstance(tree.lhs, exprTree.Identifier)
249 self.assertEqual(tree.lhs.name, 'a')
250 self.assertIsInstance(tree.values, list)
251 self.assertEqual(len(tree.values), 3)
252 self.assertIsInstance(tree.values[0], exprTree.NumericLiteral)
253 self.assertEqual(tree.values[0].value, '1')
254 self.assertIsInstance(tree.values[1], exprTree.NumericLiteral)
255 self.assertEqual(tree.values[1].value, '2')
256 self.assertIsInstance(tree.values[2], exprTree.StringLiteral)
257 self.assertEqual(tree.values[2].value, 'X')
259 tree = parser.parse("10 not in (1000, 2000..3000:100)")
260 self.assertIsInstance(tree, exprTree.IsIn)
261 self.assertTrue(tree.not_in)
262 self.assertIsInstance(tree.lhs, exprTree.NumericLiteral)
263 self.assertEqual(tree.lhs.value, '10')
264 self.assertIsInstance(tree.values, list)
265 self.assertEqual(len(tree.values), 2)
266 self.assertIsInstance(tree.values[0], exprTree.NumericLiteral)
267 self.assertEqual(tree.values[0].value, '1000')
268 self.assertIsInstance(tree.values[1], exprTree.RangeLiteral)
269 self.assertEqual(tree.values[1].start, 2000)
270 self.assertEqual(tree.values[1].stop, 3000)
271 self.assertEqual(tree.values[1].stride, 100)
273 tree = parser.parse("10 in (-1000, -2000)")
274 self.assertIsInstance(tree, exprTree.IsIn)
275 self.assertFalse(tree.not_in)
276 self.assertIsInstance(tree.lhs, exprTree.NumericLiteral)
277 self.assertEqual(tree.lhs.value, '10')
278 self.assertIsInstance(tree.values, list)
279 self.assertEqual(len(tree.values), 2)
280 self.assertIsInstance(tree.values[0], exprTree.NumericLiteral)
281 self.assertEqual(tree.values[0].value, '-1000')
282 self.assertIsInstance(tree.values[1], exprTree.NumericLiteral)
283 self.assertEqual(tree.values[1].value, '-2000')
285 # test for time contained in time range, all literals
286 tree = parser.parse("T'2020-01-01' in (T'2020-01-01', T'2020-01-02')")
287 self.assertIsInstance(tree, exprTree.IsIn)
288 self.assertFalse(tree.not_in)
289 self.assertIsInstance(tree.lhs, exprTree.TimeLiteral)
290 self.assertEqual(len(tree.values), 2)
291 self.assertIsInstance(tree.values[0], exprTree.TimeLiteral)
292 self.assertIsInstance(tree.values[1], exprTree.TimeLiteral)
294 # test for time range contained in time range
295 tree = parser.parse("(T'2020-01-01', t1) in (T'2020-01-01', t2)")
296 self.assertIsInstance(tree, exprTree.IsIn)
297 self.assertFalse(tree.not_in)
298 self.assertIsInstance(tree.lhs, exprTree.TupleNode)
299 self.assertEqual(len(tree.values), 2)
300 self.assertIsInstance(tree.values[0], exprTree.TimeLiteral)
301 self.assertIsInstance(tree.values[1], exprTree.Identifier)
303 # test for point in region (we don't have region syntax yet, use
304 # identifier)
305 tree = parser.parse("point(1, 2) in (region1)")
306 self.assertIsInstance(tree, exprTree.IsIn)
307 self.assertFalse(tree.not_in)
308 self.assertIsInstance(tree.lhs, exprTree.PointNode)
309 self.assertEqual(len(tree.values), 1)
310 self.assertIsInstance(tree.values[0], exprTree.Identifier)
312 # parens on right hand side are required
313 with self.assertRaises(ParseError):
314 parser.parse("point(1, 2) in region1")
316 # and we don't support full expressions in RHS list
317 with self.assertRaises(ParseError):
318 parser.parse("point(1, 2) in (x + y)")
320 def testCompareOps(self):
321 """Tests for comparison operators
322 """
323 parser = ParserYacc()
325 for op in ('=', '!=', '<', '<=', '>', '>='):
326 tree = parser.parse('a {} 10'.format(op))
327 self.assertIsInstance(tree, exprTree.BinaryOp)
328 self.assertEqual(tree.op, op)
329 self.assertIsInstance(tree.lhs, exprTree.Identifier)
330 self.assertIsInstance(tree.rhs, exprTree.NumericLiteral)
331 self.assertEqual(tree.lhs.name, 'a')
332 self.assertEqual(tree.rhs.value, '10')
334 def testBoolOps(self):
335 """Tests for boolean operators
336 """
337 parser = ParserYacc()
339 for op in ('OR', 'AND'):
340 tree = parser.parse('a {} b'.format(op))
341 self.assertIsInstance(tree, exprTree.BinaryOp)
342 self.assertEqual(tree.op, op)
343 self.assertIsInstance(tree.lhs, exprTree.Identifier)
344 self.assertIsInstance(tree.rhs, exprTree.Identifier)
345 self.assertEqual(tree.lhs.name, 'a')
346 self.assertEqual(tree.rhs.name, 'b')
348 tree = parser.parse('NOT b')
349 self.assertIsInstance(tree, exprTree.UnaryOp)
350 self.assertEqual(tree.op, 'NOT')
351 self.assertIsInstance(tree.operand, exprTree.Identifier)
352 self.assertEqual(tree.operand.name, 'b')
354 def testFunctionCall(self):
355 """Tests for function calls
356 """
357 parser = ParserYacc()
359 tree = parser.parse("f()")
360 self.assertIsInstance(tree, exprTree.FunctionCall)
361 self.assertEqual(tree.name, "f")
362 self.assertEqual(tree.args, [])
364 tree = parser.parse("f1(a)")
365 self.assertIsInstance(tree, exprTree.FunctionCall)
366 self.assertEqual(tree.name, "f1")
367 self.assertEqual(len(tree.args), 1)
368 self.assertIsInstance(tree.args[0], exprTree.Identifier)
369 self.assertEqual(tree.args[0].name, "a")
371 tree = parser.parse("anything_goes('a', x+y, ((a AND b) or (C = D)), NOT T < 42., Z IN (1,2,3,4))")
372 self.assertIsInstance(tree, exprTree.FunctionCall)
373 self.assertEqual(tree.name, "anything_goes")
374 self.assertEqual(len(tree.args), 5)
375 self.assertIsInstance(tree.args[0], exprTree.StringLiteral)
376 self.assertIsInstance(tree.args[1], exprTree.BinaryOp)
377 self.assertIsInstance(tree.args[2], exprTree.Parens)
378 self.assertIsInstance(tree.args[3], exprTree.UnaryOp)
379 self.assertIsInstance(tree.args[4], exprTree.IsIn)
381 with self.assertRaises(ParseError):
382 parser.parse("f.ff()")
384 def testPointNode(self):
385 """Tests for POINT() function
386 """
387 parser = ParserYacc()
389 # POINT function makes special node type
390 tree = parser.parse("POINT(Object.ra, 0.0)")
391 self.assertIsInstance(tree, exprTree.PointNode)
392 self.assertIsInstance(tree.ra, exprTree.Identifier)
393 self.assertEqual(tree.ra.name, "Object.ra")
394 self.assertIsInstance(tree.dec, exprTree.NumericLiteral)
395 self.assertEqual(tree.dec.value, "0.0")
397 # it is not case sensitive
398 tree = parser.parse("Point(1, 1)")
399 self.assertIsInstance(tree, exprTree.PointNode)
401 def testTupleNode(self):
402 """Tests for tuple
403 """
404 parser = ParserYacc()
406 # test with simple identifier and literal
407 tree = parser.parse("(Object.ra, 0.0)")
408 self.assertIsInstance(tree, exprTree.TupleNode)
409 self.assertEqual(len(tree.items), 2)
410 self.assertIsInstance(tree.items[0], exprTree.Identifier)
411 self.assertEqual(tree.items[0].name, "Object.ra")
412 self.assertIsInstance(tree.items[1], exprTree.NumericLiteral)
413 self.assertEqual(tree.items[1].value, "0.0")
415 # any expression can appear in tuple
416 tree = parser.parse("(x+y, ((a AND b) or (C = D)))")
417 self.assertIsInstance(tree, exprTree.TupleNode)
418 self.assertEqual(len(tree.items), 2)
419 self.assertIsInstance(tree.items[0], exprTree.BinaryOp)
420 self.assertIsInstance(tree.items[1], exprTree.Parens)
422 # only two items can appear in a tuple
423 with self.assertRaises(ParseError):
424 parser.parse("(1, 2, 3)")
426 def testExpression(self):
427 """Test for more or less complete expression"""
428 parser = ParserYacc()
430 expression = ("((instrument='HSC' AND detector != 9) OR instrument='CFHT') "
431 "AND tract=8766 AND patch.cell_x > 5 AND "
432 "patch.cell_y < 4 AND band='i'")
434 tree = parser.parse(expression)
435 self.assertIsInstance(tree, exprTree.BinaryOp)
436 self.assertEqual(tree.op, 'AND')
437 self.assertIsInstance(tree.lhs, exprTree.BinaryOp)
438 # AND is left-associative, so rhs operand will be the
439 # last sub-expressions
440 self.assertIsInstance(tree.rhs, exprTree.BinaryOp)
441 self.assertEqual(tree.rhs.op, '=')
442 self.assertIsInstance(tree.rhs.lhs, exprTree.Identifier)
443 self.assertEqual(tree.rhs.lhs.name, 'band')
444 self.assertIsInstance(tree.rhs.rhs, exprTree.StringLiteral)
445 self.assertEqual(tree.rhs.rhs.value, 'i')
447 def testSubstitution(self):
448 """Test for identifier substitution"""
449 # substitution is not recursive, so we can swap id2/id3
450 idMap = {
451 "id1": exprTree.StringLiteral("id1 value"),
452 "id2": exprTree.Identifier("id3"),
453 "id3": exprTree.Identifier("id2"),
454 "POINT": exprTree.StringLiteral("not used"),
455 "OR": exprTree.StringLiteral("not used"),
456 }
457 parser = ParserYacc(idMap=idMap)
459 expression = ("id1 = 'v'")
460 tree = parser.parse(expression)
461 self.assertIsInstance(tree, exprTree.BinaryOp)
462 self.assertEqual(tree.op, '=')
463 self.assertIsInstance(tree.lhs, exprTree.StringLiteral)
464 self.assertEqual(tree.lhs.value, "id1 value")
466 expression = ("id2 - id3")
467 tree = parser.parse(expression)
468 self.assertIsInstance(tree, exprTree.BinaryOp)
469 self.assertEqual(tree.op, '-')
470 self.assertIsInstance(tree.lhs, exprTree.Identifier)
471 self.assertEqual(tree.lhs.name, "id3")
472 self.assertIsInstance(tree.rhs, exprTree.Identifier)
473 self.assertEqual(tree.rhs.name, "id2")
475 # reserved words are not substituted
476 expression = ("id2 OR id3")
477 tree = parser.parse(expression)
478 self.assertIsInstance(tree, exprTree.BinaryOp)
479 self.assertEqual(tree.op, "OR")
481 # function names are not substituted
482 expression = ("POINT(1, 2)")
483 tree = parser.parse(expression)
484 self.assertIsInstance(tree, exprTree.PointNode)
486 def testException(self):
487 """Test for exceptional cases"""
489 def _assertExc(exc, expr, token, pos, lineno, posInLine):
490 """Check exception attribute values"""
491 self.assertEqual(exc.expression, expr)
492 self.assertEqual(exc.token, token)
493 self.assertEqual(exc.pos, pos)
494 self.assertEqual(exc.lineno, lineno)
495 self.assertEqual(exc.posInLine, posInLine)
497 parser = ParserYacc()
499 expression = "(1, 2, 3)"
500 with self.assertRaises(ParseError) as catcher:
501 parser.parse(expression)
502 _assertExc(catcher.exception, expression, ",", 5, 1, 5)
504 expression = "\n(1\n,\n 2, 3)"
505 with self.assertRaises(ParseError) as catcher:
506 parser.parse(expression)
507 _assertExc(catcher.exception, expression, ",", 8, 4, 2)
509 expression = "T'not-a-time'"
510 with self.assertRaises(ParseError) as catcher:
511 parser.parse(expression)
512 _assertExc(catcher.exception, expression, "not-a-time", 0, 1, 0)
514 def testStr(self):
515 """Test for formatting"""
516 parser = ParserYacc()
518 tree = parser.parse("(a+b)")
519 self.assertEqual(str(tree), '(a + b)')
521 tree = parser.parse("1 in (1,'x',3)")
522 self.assertEqual(str(tree), "1 IN (1, 'x', 3)")
524 tree = parser.parse("a not in (1,'x',3)")
525 self.assertEqual(str(tree), "a NOT IN (1, 'x', 3)")
527 tree = parser.parse("(A or B) And NoT (x+3 > y)")
528 self.assertEqual(str(tree), "(A OR B) AND NOT (x + 3 > y)")
530 tree = parser.parse("A in (100, 200..300:50)")
531 self.assertEqual(str(tree), "A IN (100, 200..300:50)")
533 def testVisit(self):
534 """Test for visitor methods"""
536 # test should cover all visit* methods
537 parser = ParserYacc()
538 visitor = _Visitor()
540 tree = parser.parse("(a+b)")
541 result = tree.visit(visitor)
542 self.assertEqual(result, "P(B(ID(a) + ID(b)))")
544 tree = parser.parse("(A or B) and not (x + 3 > y)")
545 result = tree.visit(visitor)
546 self.assertEqual(result, "B(P(B(ID(A) OR ID(B))) AND U(NOT P(B(B(ID(x) + N(3)) > ID(y)))))")
548 tree = parser.parse("x in (1,2) AND y NOT IN (1.1, .25, 1e2) OR z in ('a', 'b')")
549 result = tree.visit(visitor)
550 self.assertEqual(result, "B(B(IN(ID(x) (N(1), N(2))) AND !IN(ID(y) (N(1.1), N(.25), N(1e2))))"
551 " OR IN(ID(z) (S(a), S(b))))")
553 tree = parser.parse("x in (1,2,5..15) AND y NOT IN (-100..100:10)")
554 result = tree.visit(visitor)
555 self.assertEqual(result, "B(IN(ID(x) (N(1), N(2), R(5..15))) AND !IN(ID(y) (R(-100..100:10))))")
557 tree = parser.parse("time > T'2020-03-30'")
558 result = tree.visit(visitor)
559 self.assertEqual(result, "B(ID(time) > T(2020-03-30 00:00:00.000))")
561 def testParseTimeStr(self):
562 """Test for _parseTimeString method"""
564 # few expected failures
565 bad_times = [
566 "",
567 " ",
568 "123.456e10", # no exponents
569 "mjd-dj/123.456", # format can only have word chars
570 "123.456/mai-tai", # scale can only have word chars
571 "2020-03-01 00", # iso needs minutes if hour is given
572 "2020-03-01T", # isot needs hour:minute
573 "2020:100:12", # yday needs minutes if hour is given
574 "format/123456.00", # unknown format
575 "123456.00/unscale", # unknown scale
576 ]
577 for bad_time in bad_times:
578 with self.assertRaises(ValueError):
579 _parseTimeString(bad_time)
581 # each tuple is (string, value, format, scale)
582 tests = [
583 ("51544.0", 51544.0, "mjd", "tai"),
584 ("mjd/51544.0", 51544.0, "mjd", "tai"),
585 ("51544.0/tai", 51544.0, "mjd", "tai"),
586 ("mjd/51544.0/tai", 51544.0, "mjd", "tai"),
587 ("MJd/51544.0/TAi", 51544.0, "mjd", "tai"),
588 ("jd/2451544.5", 2451544.5, "jd", "tai"),
589 ("jd/2451544.5", 2451544.5, "jd", "tai"),
590 ("51544.0/utc", 51544.0, "mjd", "utc"),
591 ("unix/946684800.0", 946684800., "unix", "utc"),
592 ("cxcsec/63072064.184", 63072064.184, "cxcsec", "tt"),
593 ("2020-03-30", "2020-03-30 00:00:00.000", "iso", "utc"),
594 ("2020-03-30 12:20", "2020-03-30 12:20:00.000", "iso", "utc"),
595 ("2020-03-30 12:20:33.456789", "2020-03-30 12:20:33.457", "iso", "utc"),
596 ("2020-03-30T12:20", "2020-03-30T12:20:00.000", "isot", "utc"),
597 ("2020-03-30T12:20:33.456789", "2020-03-30T12:20:33.457", "isot", "utc"),
598 ("isot/2020-03-30", "2020-03-30T00:00:00.000", "isot", "utc"),
599 ("2020-03-30/tai", "2020-03-30 00:00:00.000", "iso", "tai"),
600 ("+02020-03-30", "2020-03-30T00:00:00.000", "fits", "utc"),
601 ("+02020-03-30T12:20:33", "2020-03-30T12:20:33.000", "fits", "utc"),
602 ("+02020-03-30T12:20:33.456789", "2020-03-30T12:20:33.457", "fits", "utc"),
603 ("fits/2020-03-30", "2020-03-30T00:00:00.000", "fits", "utc"),
604 ("2020:123", "2020:123:00:00:00.000", "yday", "utc"),
605 ("2020:123:12:20", "2020:123:12:20:00.000", "yday", "utc"),
606 ("2020:123:12:20:33.456789", "2020:123:12:20:33.457", "yday", "utc"),
607 ("yday/2020:123:12:20/tai", "2020:123:12:20:00.000", "yday", "tai"),
608 ]
609 for time_str, value, fmt, scale in tests:
610 time = _parseTimeString(time_str)
611 self.assertEqual(time.value, value)
612 self.assertEqual(time.format, fmt)
613 self.assertEqual(time.scale, scale)
616if __name__ == "__main__": 616 ↛ 617line 616 didn't jump to line 617, because the condition on line 616 was never true
617 unittest.main()