Coverage for cogapp/test_cogapp.py: 29.50%
854 statements
« prev ^ index » next coverage.py v7.5.0a1.dev1, created at 2024-04-15 15:50 -0400
« prev ^ index » next coverage.py v7.5.0a1.dev1, created at 2024-04-15 15:50 -0400
1""" Test cogapp.
2"""
4import io
5import os
6import os.path
7import random
8import re
9import shutil
10import stat
11import sys
12import tempfile
13import threading
14from unittest import TestCase
16from .cogapp import Cog, CogOptions, CogGenerator
17from .cogapp import CogError, CogUsageError, CogGeneratedError, CogUserException
18from .cogapp import usage, __version__, main
19from .makefiles import makeFiles
20from .whiteutils import reindentBlock
23class CogTestsInMemory(TestCase):
24 """ Test cases for cogapp.Cog()
25 """
27 def testNoCog(self):
28 strings = [
29 '',
30 ' ',
31 ' \t \t \tx',
32 'hello',
33 'the cat\nin the\nhat.',
34 'Horton\n\tHears A\n\t\tWho'
35 ]
36 for s in strings:
37 self.assertEqual(Cog().processString(s), s)
39 def testSimple(self):
40 infile = """\
41 Some text.
42 //[[[cog
43 import cog
44 cog.outl("This is line one\\n")
45 cog.outl("This is line two")
46 //]]]
47 gobbledegook.
48 //[[[end]]]
49 epilogue.
50 """
52 outfile = """\
53 Some text.
54 //[[[cog
55 import cog
56 cog.outl("This is line one\\n")
57 cog.outl("This is line two")
58 //]]]
59 This is line one
61 This is line two
62 //[[[end]]]
63 epilogue.
64 """
66 self.assertEqual(Cog().processString(infile), outfile)
68 def testEmptyCog(self):
69 # The cog clause can be totally empty. Not sure why you'd want it,
70 # but it works.
71 infile = """\
72 hello
73 //[[[cog
74 //]]]
75 //[[[end]]]
76 goodbye
77 """
79 infile = reindentBlock(infile)
80 self.assertEqual(Cog().processString(infile), infile)
82 def testMultipleCogs(self):
83 # One file can have many cog chunks, even abutting each other.
84 infile = """\
85 //[[[cog
86 cog.out("chunk1")
87 //]]]
88 chunk1
89 //[[[end]]]
90 //[[[cog
91 cog.out("chunk2")
92 //]]]
93 chunk2
94 //[[[end]]]
95 between chunks
96 //[[[cog
97 cog.out("chunk3")
98 //]]]
99 chunk3
100 //[[[end]]]
101 """
103 infile = reindentBlock(infile)
104 self.assertEqual(Cog().processString(infile), infile)
106 def testTrimBlankLines(self):
107 infile = """\
108 //[[[cog
109 cog.out("This is line one\\n", trimblanklines=True)
110 cog.out('''
111 This is line two
112 ''', dedent=True, trimblanklines=True)
113 cog.outl("This is line three", trimblanklines=True)
114 //]]]
115 This is line one
116 This is line two
117 This is line three
118 //[[[end]]]
119 """
121 infile = reindentBlock(infile)
122 self.assertEqual(Cog().processString(infile), infile)
124 def testTrimEmptyBlankLines(self):
125 infile = """\
126 //[[[cog
127 cog.out("This is line one\\n", trimblanklines=True)
128 cog.out('''
129 This is line two
130 ''', dedent=True, trimblanklines=True)
131 cog.out('', dedent=True, trimblanklines=True)
132 cog.outl("This is line three", trimblanklines=True)
133 //]]]
134 This is line one
135 This is line two
136 This is line three
137 //[[[end]]]
138 """
140 infile = reindentBlock(infile)
141 self.assertEqual(Cog().processString(infile), infile)
143 def testTrimBlankLinesWithLastPartial(self):
144 infile = """\
145 //[[[cog
146 cog.out("This is line one\\n", trimblanklines=True)
147 cog.out("\\nLine two\\nLine three", trimblanklines=True)
148 //]]]
149 This is line one
150 Line two
151 Line three
152 //[[[end]]]
153 """
155 infile = reindentBlock(infile)
156 self.assertEqual(Cog().processString(infile), infile)
158 def testCogOutDedent(self):
159 infile = """\
160 //[[[cog
161 cog.out("This is the first line\\n")
162 cog.out('''
163 This is dedent=True 1
164 This is dedent=True 2
165 ''', dedent=True, trimblanklines=True)
166 cog.out('''
167 This is dedent=False 1
168 This is dedent=False 2
169 ''', dedent=False, trimblanklines=True)
170 cog.out('''
171 This is dedent=default 1
172 This is dedent=default 2
173 ''', trimblanklines=True)
174 cog.out("This is the last line\\n")
175 //]]]
176 This is the first line
177 This is dedent=True 1
178 This is dedent=True 2
179 This is dedent=False 1
180 This is dedent=False 2
181 This is dedent=default 1
182 This is dedent=default 2
183 This is the last line
184 //[[[end]]]
185 """
187 infile = reindentBlock(infile)
188 self.assertEqual(Cog().processString(infile), infile)
190 def test22EndOfLine(self):
191 # In Python 2.2, this cog file was not parsing because the
192 # last line is indented but didn't end with a newline.
193 infile = """\
194 //[[[cog
195 import cog
196 for i in range(3):
197 cog.out("%d\\n" % i)
198 //]]]
199 0
200 1
201 2
202 //[[[end]]]
203 """
205 infile = reindentBlock(infile)
206 self.assertEqual(Cog().processString(infile), infile)
208 def testIndentedCode(self):
209 infile = """\
210 first line
211 [[[cog
212 import cog
213 for i in range(3):
214 cog.out("xx%d\\n" % i)
215 ]]]
216 xx0
217 xx1
218 xx2
219 [[[end]]]
220 last line
221 """
223 infile = reindentBlock(infile)
224 self.assertEqual(Cog().processString(infile), infile)
226 def testPrefixedCode(self):
227 infile = """\
228 --[[[cog
229 --import cog
230 --for i in range(3):
231 -- cog.out("xx%d\\n" % i)
232 --]]]
233 xx0
234 xx1
235 xx2
236 --[[[end]]]
237 """
239 infile = reindentBlock(infile)
240 self.assertEqual(Cog().processString(infile), infile)
242 def testPrefixedIndentedCode(self):
243 infile = """\
244 prologue
245 --[[[cog
246 -- import cog
247 -- for i in range(3):
248 -- cog.out("xy%d\\n" % i)
249 --]]]
250 xy0
251 xy1
252 xy2
253 --[[[end]]]
254 """
256 infile = reindentBlock(infile)
257 self.assertEqual(Cog().processString(infile), infile)
259 def testBogusPrefixMatch(self):
260 infile = """\
261 prologue
262 #[[[cog
263 import cog
264 # This comment should not be clobbered by removing the pound sign.
265 for i in range(3):
266 cog.out("xy%d\\n" % i)
267 #]]]
268 xy0
269 xy1
270 xy2
271 #[[[end]]]
272 """
274 infile = reindentBlock(infile)
275 self.assertEqual(Cog().processString(infile), infile)
277 def testNoFinalNewline(self):
278 # If the cog'ed output has no final newline,
279 # it shouldn't eat up the cog terminator.
280 infile = """\
281 prologue
282 [[[cog
283 import cog
284 for i in range(3):
285 cog.out("%d" % i)
286 ]]]
287 012
288 [[[end]]]
289 epilogue
290 """
292 infile = reindentBlock(infile)
293 self.assertEqual(Cog().processString(infile), infile)
295 def testNoOutputAtAll(self):
296 # If there is absolutely no cog output, that's ok.
297 infile = """\
298 prologue
299 [[[cog
300 i = 1
301 ]]]
302 [[[end]]]
303 epilogue
304 """
306 infile = reindentBlock(infile)
307 self.assertEqual(Cog().processString(infile), infile)
309 def testPurelyBlankLine(self):
310 # If there is a blank line in the cog code with no whitespace
311 # prefix, that should be OK.
313 infile = """\
314 prologue
315 [[[cog
316 import sys
317 cog.out("Hello")
318 $
319 cog.out("There")
320 ]]]
321 HelloThere
322 [[[end]]]
323 epilogue
324 """
326 infile = reindentBlock(infile.replace('$', ''))
327 self.assertEqual(Cog().processString(infile), infile)
329 def testEmptyOutl(self):
330 # Alexander Belchenko suggested the string argument to outl should
331 # be optional. Does it work?
333 infile = """\
334 prologue
335 [[[cog
336 cog.outl("x")
337 cog.outl()
338 cog.outl("y")
339 cog.out() # Also optional, a complete no-op.
340 cog.outl(trimblanklines=True)
341 cog.outl("z")
342 ]]]
343 x
345 y
347 z
348 [[[end]]]
349 epilogue
350 """
352 infile = reindentBlock(infile)
353 self.assertEqual(Cog().processString(infile), infile)
355 def testFirstLineNum(self):
356 infile = """\
357 fooey
358 [[[cog
359 cog.outl("started at line number %d" % cog.firstLineNum)
360 ]]]
361 started at line number 2
362 [[[end]]]
363 blah blah
364 [[[cog
365 cog.outl("and again at line %d" % cog.firstLineNum)
366 ]]]
367 and again at line 8
368 [[[end]]]
369 """
371 infile = reindentBlock(infile)
372 self.assertEqual(Cog().processString(infile), infile)
374 def testCompactOneLineCode(self):
375 infile = """\
376 first line
377 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
378 get rid of this!
379 [[[end]]]
380 last line
381 """
383 outfile = """\
384 first line
385 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
386 hello 81
387 [[[end]]]
388 last line
389 """
391 infile = reindentBlock(infile)
392 self.assertEqual(Cog().processString(infile), reindentBlock(outfile))
394 def testInsideOutCompact(self):
395 infile = """\
396 first line
397 hey?: ]]] what is this? [[[cog strange!
398 get rid of this!
399 [[[end]]]
400 last line
401 """
402 with self.assertRaisesRegex(CogError, r"^infile.txt\(2\): Cog code markers inverted$"):
403 Cog().processString(reindentBlock(infile), "infile.txt")
405 def testSharingGlobals(self):
406 infile = """\
407 first line
408 hey: [[[cog s="hey there" ]]] looky!
409 [[[end]]]
410 more literal junk.
411 [[[cog cog.outl(s) ]]]
412 [[[end]]]
413 last line
414 """
416 outfile = """\
417 first line
418 hey: [[[cog s="hey there" ]]] looky!
419 [[[end]]]
420 more literal junk.
421 [[[cog cog.outl(s) ]]]
422 hey there
423 [[[end]]]
424 last line
425 """
427 infile = reindentBlock(infile)
428 self.assertEqual(Cog().processString(infile), reindentBlock(outfile))
430 def testAssertInCogCode(self):
431 # Check that we can test assertions in cog code in the test framework.
432 infile = """\
433 [[[cog
434 assert 1 == 2, "Oops"
435 ]]]
436 [[[end]]]
437 """
438 infile = reindentBlock(infile)
439 with self.assertRaisesRegex(CogUserException, "AssertionError: Oops"):
440 Cog().processString(infile)
442 def testCogPrevious(self):
443 # Check that we can access the previous run's output.
444 infile = """\
445 [[[cog
446 assert cog.previous == "Hello there!\\n", "WTF??"
447 cog.out(cog.previous)
448 cog.outl("Ran again!")
449 ]]]
450 Hello there!
451 [[[end]]]
452 """
454 outfile = """\
455 [[[cog
456 assert cog.previous == "Hello there!\\n", "WTF??"
457 cog.out(cog.previous)
458 cog.outl("Ran again!")
459 ]]]
460 Hello there!
461 Ran again!
462 [[[end]]]
463 """
465 infile = reindentBlock(infile)
466 self.assertEqual(Cog().processString(infile), reindentBlock(outfile))
469class CogOptionsTests(TestCase):
470 """ Test the CogOptions class.
471 """
473 def testEquality(self):
474 o = CogOptions()
475 p = CogOptions()
476 self.assertEqual(o, p)
477 o.parseArgs(['-r'])
478 self.assertNotEqual(o, p)
479 p.parseArgs(['-r'])
480 self.assertEqual(o, p)
482 def testCloning(self):
483 o = CogOptions()
484 o.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/'])
485 p = o.clone()
486 self.assertEqual(o, p)
487 p.parseArgs(['-I', 'huey', '-D', 'foo=quux'])
488 self.assertNotEqual(o, p)
489 q = CogOptions()
490 q.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/', '-I', 'huey', '-D', 'foo=quux'])
491 self.assertEqual(p, q)
493 def testCombiningFlags(self):
494 # Single-character flags can be combined.
495 o = CogOptions()
496 o.parseArgs(['-e', '-r', '-z'])
497 p = CogOptions()
498 p.parseArgs(['-erz'])
499 self.assertEqual(o, p)
501 def testMarkers(self):
502 o = CogOptions()
503 o._parse_markers('a b c')
504 self.assertEqual('a', o.sBeginSpec)
505 self.assertEqual('b', o.sEndSpec)
506 self.assertEqual('c', o.sEndOutput)
508 def testMarkersSwitch(self):
509 o = CogOptions()
510 o.parseArgs(['--markers', 'a b c'])
511 self.assertEqual('a', o.sBeginSpec)
512 self.assertEqual('b', o.sEndSpec)
513 self.assertEqual('c', o.sEndOutput)
516class FileStructureTests(TestCase):
517 """ Test cases to check that we're properly strict about the structure
518 of files.
519 """
521 def isBad(self, infile, msg=None):
522 infile = reindentBlock(infile)
523 with self.assertRaisesRegex(CogError, "^"+re.escape(msg)+"$"):
524 Cog().processString(infile, 'infile.txt')
526 def testBeginNoEnd(self):
527 infile = """\
528 Fooey
529 #[[[cog
530 cog.outl('hello')
531 """
532 self.isBad(infile, "infile.txt(2): Cog block begun but never ended.")
534 def testNoEoo(self):
535 infile = """\
536 Fooey
537 #[[[cog
538 cog.outl('hello')
539 #]]]
540 """
541 self.isBad(infile, "infile.txt(4): Missing '[[[end]]]' before end of file.")
543 infile2 = """\
544 Fooey
545 #[[[cog
546 cog.outl('hello')
547 #]]]
548 #[[[cog
549 cog.outl('goodbye')
550 #]]]
551 """
552 self.isBad(infile2, "infile.txt(5): Unexpected '[[[cog'")
554 def testStartWithEnd(self):
555 infile = """\
556 #]]]
557 """
558 self.isBad(infile, "infile.txt(1): Unexpected ']]]'")
560 infile2 = """\
561 #[[[cog
562 cog.outl('hello')
563 #]]]
564 #[[[end]]]
565 #]]]
566 """
567 self.isBad(infile2, "infile.txt(5): Unexpected ']]]'")
569 def testStartWithEoo(self):
570 infile = """\
571 #[[[end]]]
572 """
573 self.isBad(infile, "infile.txt(1): Unexpected '[[[end]]]'")
575 infile2 = """\
576 #[[[cog
577 cog.outl('hello')
578 #]]]
579 #[[[end]]]
580 #[[[end]]]
581 """
582 self.isBad(infile2, "infile.txt(5): Unexpected '[[[end]]]'")
584 def testNoEnd(self):
585 infile = """\
586 #[[[cog
587 cog.outl("hello")
588 #[[[end]]]
589 """
590 self.isBad(infile, "infile.txt(3): Unexpected '[[[end]]]'")
592 infile2 = """\
593 #[[[cog
594 cog.outl('hello')
595 #]]]
596 #[[[end]]]
597 #[[[cog
598 cog.outl("hello")
599 #[[[end]]]
600 """
601 self.isBad(infile2, "infile.txt(7): Unexpected '[[[end]]]'")
603 def testTwoBegins(self):
604 infile = """\
605 #[[[cog
606 #[[[cog
607 cog.outl("hello")
608 #]]]
609 #[[[end]]]
610 """
611 self.isBad(infile, "infile.txt(2): Unexpected '[[[cog'")
613 infile2 = """\
614 #[[[cog
615 cog.outl("hello")
616 #]]]
617 #[[[end]]]
618 #[[[cog
619 #[[[cog
620 cog.outl("hello")
621 #]]]
622 #[[[end]]]
623 """
624 self.isBad(infile2, "infile.txt(6): Unexpected '[[[cog'")
626 def testTwoEnds(self):
627 infile = """\
628 #[[[cog
629 cog.outl("hello")
630 #]]]
631 #]]]
632 #[[[end]]]
633 """
634 self.isBad(infile, "infile.txt(4): Unexpected ']]]'")
636 infile2 = """\
637 #[[[cog
638 cog.outl("hello")
639 #]]]
640 #[[[end]]]
641 #[[[cog
642 cog.outl("hello")
643 #]]]
644 #]]]
645 #[[[end]]]
646 """
647 self.isBad(infile2, "infile.txt(8): Unexpected ']]]'")
650class CogErrorTests(TestCase):
651 """ Test cases for cog.error().
652 """
654 def testErrorMsg(self):
655 infile = """\
656 [[[cog cog.error("This ain't right!")]]]
657 [[[end]]]
658 """
660 infile = reindentBlock(infile)
661 with self.assertRaisesRegex(CogGeneratedError, "^This ain't right!$"):
662 Cog().processString(infile)
664 def testErrorNoMsg(self):
665 infile = """\
666 [[[cog cog.error()]]]
667 [[[end]]]
668 """
670 infile = reindentBlock(infile)
671 with self.assertRaisesRegex(CogGeneratedError, "^Error raised by cog generator.$"):
672 Cog().processString(infile)
674 def testNoErrorIfErrorNotCalled(self):
675 infile = """\
676 --[[[cog
677 --import cog
678 --for i in range(3):
679 -- if i > 10:
680 -- cog.error("Something is amiss!")
681 -- cog.out("xx%d\\n" % i)
682 --]]]
683 xx0
684 xx1
685 xx2
686 --[[[end]]]
687 """
689 infile = reindentBlock(infile)
690 self.assertEqual(Cog().processString(infile), infile)
693class CogGeneratorGetCodeTests(TestCase):
694 """ Unit tests against CogGenerator to see if its getCode() method works
695 properly.
696 """
698 def setUp(self):
699 """ All tests get a generator to use, and short same-length names for
700 the functions we're going to use.
701 """
702 self.gen = CogGenerator()
703 self.m = self.gen.parseMarker
704 self.l = self.gen.parseLine
706 def testEmpty(self):
707 self.m('// [[[cog')
708 self.m('// ]]]')
709 self.assertEqual(self.gen.getCode(), '')
711 def testSimple(self):
712 self.m('// [[[cog')
713 self.l(' print "hello"')
714 self.l(' print "bye"')
715 self.m('// ]]]')
716 self.assertEqual(self.gen.getCode(), 'print "hello"\nprint "bye"')
718 def testCompressed1(self):
719 # For a while, I supported compressed code blocks, but no longer.
720 self.m('// [[[cog: print """')
721 self.l('// hello')
722 self.l('// bye')
723 self.m('// """)]]]')
724 self.assertEqual(self.gen.getCode(), 'hello\nbye')
726 def testCompressed2(self):
727 # For a while, I supported compressed code blocks, but no longer.
728 self.m('// [[[cog: print """')
729 self.l('hello')
730 self.l('bye')
731 self.m('// """)]]]')
732 self.assertEqual(self.gen.getCode(), 'hello\nbye')
734 def testCompressed3(self):
735 # For a while, I supported compressed code blocks, but no longer.
736 self.m('// [[[cog')
737 self.l('print """hello')
738 self.l('bye')
739 self.m('// """)]]]')
740 self.assertEqual(self.gen.getCode(), 'print """hello\nbye')
742 def testCompressed4(self):
743 # For a while, I supported compressed code blocks, but no longer.
744 self.m('// [[[cog: print """')
745 self.l('hello')
746 self.l('bye""")')
747 self.m('// ]]]')
748 self.assertEqual(self.gen.getCode(), 'hello\nbye""")')
750 def testNoCommonPrefixForMarkers(self):
751 # It's important to be able to use #if 0 to hide lines from a
752 # C++ compiler.
753 self.m('#if 0 //[[[cog')
754 self.l('\timport cog, sys')
755 self.l('')
756 self.l('\tprint sys.argv')
757 self.m('#endif //]]]')
758 self.assertEqual(self.gen.getCode(), 'import cog, sys\n\nprint sys.argv')
761class TestCaseWithTempDir(TestCase):
763 def newCog(self):
764 """ Initialize the cog members for another run.
765 """
766 # Create a cog engine, and catch its output.
767 self.cog = Cog()
768 self.output = io.StringIO()
769 self.cog.setOutput(stdout=self.output, stderr=self.output)
771 def setUp(self):
772 # Create a temporary directory.
773 self.tempdir = os.path.join(tempfile.gettempdir(), 'testcog_tempdir_' + str(random.random())[2:])
774 os.mkdir(self.tempdir)
775 self.olddir = os.getcwd()
776 os.chdir(self.tempdir)
777 self.newCog()
779 def tearDown(self):
780 os.chdir(self.olddir)
781 # Get rid of the temporary directory.
782 shutil.rmtree(self.tempdir)
784 def assertFilesSame(self, sFName1, sFName2):
785 with open(os.path.join(self.tempdir, sFName1), 'rb') as f1:
786 text1 = f1.read()
787 with open(os.path.join(self.tempdir, sFName2), 'rb') as f2:
788 text2 = f2.read()
789 self.assertEqual(text1, text2)
791 def assertFileContent(self, fname, content):
792 absname = os.path.join(self.tempdir, fname)
793 with open(absname, 'rb') as f:
794 file_content = f.read()
795 self.assertEqual(file_content, content.encode("utf-8"))
798class ArgumentHandlingTests(TestCaseWithTempDir):
800 def testArgumentFailure(self):
801 # Return value 2 means usage problem.
802 self.assertEqual(self.cog.main(['argv0', '-j']), 2)
803 output = self.output.getvalue()
804 self.assertIn("option -j not recognized", output)
805 with self.assertRaisesRegex(CogUsageError, r"^No files to process$"):
806 self.cog.callableMain(['argv0'])
807 with self.assertRaisesRegex(CogUsageError, r"^option -j not recognized$"):
808 self.cog.callableMain(['argv0', '-j'])
810 def testNoDashOAndAtFile(self):
811 makeFiles({"cogfiles.txt": "# Please run cog"})
812 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with @file$"):
813 self.cog.callableMain(['argv0', '-o', 'foo', '@cogfiles.txt'])
815 def testNoDashOAndAmpFile(self):
816 makeFiles({"cogfiles.txt": "# Please run cog"})
817 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with &file$"):
818 self.cog.callableMain(['argv0', '-o', 'foo', '&cogfiles.txt'])
820 def testDashV(self):
821 self.assertEqual(self.cog.main(['argv0', '-v']), 0)
822 output = self.output.getvalue()
823 self.assertEqual('Cog version %s\n' % __version__, output)
825 def producesHelp(self, args):
826 self.newCog()
827 argv = ['argv0'] + args.split()
828 self.assertEqual(self.cog.main(argv), 0)
829 self.assertEqual(usage, self.output.getvalue())
831 def testDashH(self):
832 # -h or -? anywhere on the command line should just print help.
833 self.producesHelp("-h")
834 self.producesHelp("-?")
835 self.producesHelp("fooey.txt -h")
836 self.producesHelp("-o -r @fooey.txt -? @booey.txt")
838 def testDashOAndDashR(self):
839 d = {
840 'cogfile.txt': """\
841 # Please run cog
842 """
843 }
845 makeFiles(d)
846 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with -r \(they are opposites\)$"):
847 self.cog.callableMain(['argv0', '-o', 'foo', '-r', 'cogfile.txt'])
849 def testDashZ(self):
850 d = {
851 'test.cog': """\
852 // This is my C++ file.
853 //[[[cog
854 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
855 for fn in fnames:
856 cog.outl("void %s();" % fn)
857 //]]]
858 """,
860 'test.out': """\
861 // This is my C++ file.
862 //[[[cog
863 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
864 for fn in fnames:
865 cog.outl("void %s();" % fn)
866 //]]]
867 void DoSomething();
868 void DoAnotherThing();
869 void DoLastThing();
870 """,
871 }
873 makeFiles(d)
874 with self.assertRaisesRegex(CogError, r"^test.cog\(6\): Missing '\[\[\[end\]\]\]' before end of file.$"):
875 self.cog.callableMain(['argv0', '-r', 'test.cog'])
876 self.newCog()
877 self.cog.callableMain(['argv0', '-r', '-z', 'test.cog'])
878 self.assertFilesSame('test.cog', 'test.out')
880 def testBadDashD(self):
881 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
882 self.cog.callableMain(['argv0', '-Dfooey', 'cog.txt'])
883 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
884 self.cog.callableMain(['argv0', '-D', 'fooey', 'cog.txt'])
886 def testBadMarkers(self):
887 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'X'$"):
888 self.cog.callableMain(['argv0', '--markers=X'])
889 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'A B C D'$"):
890 self.cog.callableMain(['argv0', '--markers=A B C D'])
893class TestMain(TestCaseWithTempDir):
894 def setUp(self):
895 super().setUp()
896 self.old_argv = sys.argv[:]
897 self.old_stderr = sys.stderr
898 sys.stderr = io.StringIO()
900 def tearDown(self):
901 sys.stderr = self.old_stderr
902 sys.argv = self.old_argv
903 sys.modules.pop('mycode', None)
904 super().tearDown()
906 def test_main_function(self):
907 sys.argv = ["argv0", "-Z"]
908 ret = main()
909 self.assertEqual(ret, 2)
910 stderr = sys.stderr.getvalue()
911 self.assertEqual(stderr, 'option -Z not recognized\n(for help use -h)\n')
913 files = {
914 'test.cog': """\
915 //[[[cog
916 def func():
917 import mycode
918 mycode.boom()
919 //]]]
920 //[[[end]]]
921 -----
922 //[[[cog
923 func()
924 //]]]
925 //[[[end]]]
926 """,
928 'mycode.py': """\
929 def boom():
930 [][0]
931 """,
932 }
934 def test_error_report(self):
935 self.check_error_report()
937 def test_error_report_with_prologue(self):
938 self.check_error_report("-p", "#1\n#2")
940 def check_error_report(self, *args):
941 """Check that the error report is right."""
942 makeFiles(self.files)
943 sys.argv = ["argv0"] + list(args) + ["-r", "test.cog"]
944 main()
945 expected = reindentBlock("""\
946 Traceback (most recent call last):
947 File "test.cog", line 9, in <module>
948 func()
949 File "test.cog", line 4, in func
950 mycode.boom()
951 File "MYCODE", line 2, in boom
952 [][0]
953 IndexError: list index out of range
954 """)
955 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
956 assert expected == sys.stderr.getvalue()
958 def test_error_in_prologue(self):
959 makeFiles(self.files)
960 sys.argv = ["argv0", "-p", "import mycode; mycode.boom()", "-r", "test.cog"]
961 main()
962 expected = reindentBlock("""\
963 Traceback (most recent call last):
964 File "<prologue>", line 1, in <module>
965 import mycode; mycode.boom()
966 File "MYCODE", line 2, in boom
967 [][0]
968 IndexError: list index out of range
969 """)
970 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
971 assert expected == sys.stderr.getvalue()
975class TestFileHandling(TestCaseWithTempDir):
977 def testSimple(self):
978 d = {
979 'test.cog': """\
980 // This is my C++ file.
981 //[[[cog
982 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
983 for fn in fnames:
984 cog.outl("void %s();" % fn)
985 //]]]
986 //[[[end]]]
987 """,
989 'test.out': """\
990 // This is my C++ file.
991 //[[[cog
992 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
993 for fn in fnames:
994 cog.outl("void %s();" % fn)
995 //]]]
996 void DoSomething();
997 void DoAnotherThing();
998 void DoLastThing();
999 //[[[end]]]
1000 """,
1001 }
1003 makeFiles(d)
1004 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1005 self.assertFilesSame('test.cog', 'test.out')
1006 output = self.output.getvalue()
1007 self.assertIn("(changed)", output)
1009 def testPrintOutput(self):
1010 d = {
1011 'test.cog': """\
1012 // This is my C++ file.
1013 //[[[cog
1014 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1015 for fn in fnames:
1016 print("void %s();" % fn)
1017 //]]]
1018 //[[[end]]]
1019 """,
1021 'test.out': """\
1022 // This is my C++ file.
1023 //[[[cog
1024 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1025 for fn in fnames:
1026 print("void %s();" % fn)
1027 //]]]
1028 void DoSomething();
1029 void DoAnotherThing();
1030 void DoLastThing();
1031 //[[[end]]]
1032 """,
1033 }
1035 makeFiles(d)
1036 self.cog.callableMain(['argv0', '-rP', 'test.cog'])
1037 self.assertFilesSame('test.cog', 'test.out')
1038 output = self.output.getvalue()
1039 self.assertIn("(changed)", output)
1041 def testWildcards(self):
1042 d = {
1043 'test.cog': """\
1044 // This is my C++ file.
1045 //[[[cog
1046 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1047 for fn in fnames:
1048 cog.outl("void %s();" % fn)
1049 //]]]
1050 //[[[end]]]
1051 """,
1053 'test2.cog': """\
1054 // This is my C++ file.
1055 //[[[cog
1056 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1057 for fn in fnames:
1058 cog.outl("void %s();" % fn)
1059 //]]]
1060 //[[[end]]]
1061 """,
1063 'test.out': """\
1064 // This is my C++ file.
1065 //[[[cog
1066 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1067 for fn in fnames:
1068 cog.outl("void %s();" % fn)
1069 //]]]
1070 void DoSomething();
1071 void DoAnotherThing();
1072 void DoLastThing();
1073 //[[[end]]]
1074 """,
1076 'not_this_one.cog': """\
1077 // This is my C++ file.
1078 //[[[cog
1079 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1080 for fn in fnames:
1081 cog.outl("void %s();" % fn)
1082 //]]]
1083 //[[[end]]]
1084 """,
1086 'not_this_one.out': """\
1087 // This is my C++ file.
1088 //[[[cog
1089 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1090 for fn in fnames:
1091 cog.outl("void %s();" % fn)
1092 //]]]
1093 //[[[end]]]
1094 """,
1095 }
1097 makeFiles(d)
1098 self.cog.callableMain(['argv0', '-r', 't*.cog'])
1099 self.assertFilesSame('test.cog', 'test.out')
1100 self.assertFilesSame('test2.cog', 'test.out')
1101 self.assertFilesSame('not_this_one.cog', 'not_this_one.out')
1102 output = self.output.getvalue()
1103 self.assertIn("(changed)", output)
1105 def testOutputFile(self):
1106 # -o sets the output file.
1107 d = {
1108 'test.cog': """\
1109 // This is my C++ file.
1110 //[[[cog
1111 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1112 for fn in fnames:
1113 cog.outl("void %s();" % fn)
1114 //]]]
1115 //[[[end]]]
1116 """,
1118 'test.out': """\
1119 // This is my C++ file.
1120 //[[[cog
1121 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1122 for fn in fnames:
1123 cog.outl("void %s();" % fn)
1124 //]]]
1125 void DoSomething();
1126 void DoAnotherThing();
1127 void DoLastThing();
1128 //[[[end]]]
1129 """,
1130 }
1132 makeFiles(d)
1133 self.cog.callableMain(['argv0', '-o', 'in/a/dir/test.cogged', 'test.cog'])
1134 self.assertFilesSame('in/a/dir/test.cogged', 'test.out')
1136 def testAtFile(self):
1137 d = {
1138 'one.cog': """\
1139 //[[[cog
1140 cog.outl("hello world")
1141 //]]]
1142 //[[[end]]]
1143 """,
1145 'one.out': """\
1146 //[[[cog
1147 cog.outl("hello world")
1148 //]]]
1149 hello world
1150 //[[[end]]]
1151 """,
1153 'two.cog': """\
1154 //[[[cog
1155 cog.outl("goodbye cruel world")
1156 //]]]
1157 //[[[end]]]
1158 """,
1160 'two.out': """\
1161 //[[[cog
1162 cog.outl("goodbye cruel world")
1163 //]]]
1164 goodbye cruel world
1165 //[[[end]]]
1166 """,
1168 'cogfiles.txt': """\
1169 # Please run cog
1170 one.cog
1172 two.cog
1173 """
1174 }
1176 makeFiles(d)
1177 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1178 self.assertFilesSame('one.cog', 'one.out')
1179 self.assertFilesSame('two.cog', 'two.out')
1180 output = self.output.getvalue()
1181 self.assertIn("(changed)", output)
1183 def testNestedAtFile(self):
1184 d = {
1185 'one.cog': """\
1186 //[[[cog
1187 cog.outl("hello world")
1188 //]]]
1189 //[[[end]]]
1190 """,
1192 'one.out': """\
1193 //[[[cog
1194 cog.outl("hello world")
1195 //]]]
1196 hello world
1197 //[[[end]]]
1198 """,
1200 'two.cog': """\
1201 //[[[cog
1202 cog.outl("goodbye cruel world")
1203 //]]]
1204 //[[[end]]]
1205 """,
1207 'two.out': """\
1208 //[[[cog
1209 cog.outl("goodbye cruel world")
1210 //]]]
1211 goodbye cruel world
1212 //[[[end]]]
1213 """,
1215 'cogfiles.txt': """\
1216 # Please run cog
1217 one.cog
1218 @cogfiles2.txt
1219 """,
1221 'cogfiles2.txt': """\
1222 # This one too, please.
1223 two.cog
1224 """,
1225 }
1227 makeFiles(d)
1228 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1229 self.assertFilesSame('one.cog', 'one.out')
1230 self.assertFilesSame('two.cog', 'two.out')
1231 output = self.output.getvalue()
1232 self.assertIn("(changed)", output)
1234 def testAtFileWithArgs(self):
1235 d = {
1236 'both.cog': """\
1237 //[[[cog
1238 cog.outl("one: %s" % ('one' in globals()))
1239 cog.outl("two: %s" % ('two' in globals()))
1240 //]]]
1241 //[[[end]]]
1242 """,
1244 'one.out': """\
1245 //[[[cog
1246 cog.outl("one: %s" % ('one' in globals()))
1247 cog.outl("two: %s" % ('two' in globals()))
1248 //]]]
1249 one: True // ONE
1250 two: False // ONE
1251 //[[[end]]]
1252 """,
1254 'two.out': """\
1255 //[[[cog
1256 cog.outl("one: %s" % ('one' in globals()))
1257 cog.outl("two: %s" % ('two' in globals()))
1258 //]]]
1259 one: False // TWO
1260 two: True // TWO
1261 //[[[end]]]
1262 """,
1264 'cogfiles.txt': """\
1265 # Please run cog
1266 both.cog -o in/a/dir/both.one -s ' // ONE' -D one=x
1267 both.cog -o in/a/dir/both.two -s ' // TWO' -D two=x
1268 """
1269 }
1271 makeFiles(d)
1272 self.cog.callableMain(['argv0', '@cogfiles.txt'])
1273 self.assertFilesSame('in/a/dir/both.one', 'one.out')
1274 self.assertFilesSame('in/a/dir/both.two', 'two.out')
1276 def testAtFileWithBadArgCombo(self):
1277 d = {
1278 'both.cog': """\
1279 //[[[cog
1280 cog.outl("one: %s" % ('one' in globals()))
1281 cog.outl("two: %s" % ('two' in globals()))
1282 //]]]
1283 //[[[end]]]
1284 """,
1286 'cogfiles.txt': """\
1287 # Please run cog
1288 both.cog
1289 both.cog -d # This is bad: -r and -d
1290 """
1291 }
1293 makeFiles(d)
1294 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"):
1295 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1297 def testAtFileWithTrickyFilenames(self):
1298 def fix_backslashes(files_txt):
1299 """Make the contents of a files.txt sensitive to the platform."""
1300 if sys.platform != "win32":
1301 files_txt = files_txt.replace("\\", "/")
1302 return files_txt
1304 d = {
1305 'one 1.cog': """\
1306 //[[[cog cog.outl("hello world") ]]]
1307 """,
1309 'one.out': """\
1310 //[[[cog cog.outl("hello world") ]]]
1311 hello world //xxx
1312 """,
1314 'subdir': {
1315 'subback.cog': """\
1316 //[[[cog cog.outl("down deep with backslashes") ]]]
1317 """,
1319 'subfwd.cog': """\
1320 //[[[cog cog.outl("down deep with slashes") ]]]
1321 """,
1322 },
1324 'subback.out': """\
1325 //[[[cog cog.outl("down deep with backslashes") ]]]
1326 down deep with backslashes //yyy
1327 """,
1329 'subfwd.out': """\
1330 //[[[cog cog.outl("down deep with slashes") ]]]
1331 down deep with slashes //zzz
1332 """,
1334 'cogfiles.txt': fix_backslashes("""\
1335 # Please run cog
1336 'one 1.cog' -s ' //xxx'
1337 subdir\\subback.cog -s ' //yyy'
1338 subdir/subfwd.cog -s ' //zzz'
1339 """)
1340 }
1342 makeFiles(d)
1343 self.cog.callableMain(['argv0', '-z', '-r', '@cogfiles.txt'])
1344 self.assertFilesSame('one 1.cog', 'one.out')
1345 self.assertFilesSame('subdir/subback.cog', 'subback.out')
1346 self.assertFilesSame('subdir/subfwd.cog', 'subfwd.out')
1348 def testAmpFile(self):
1349 d = {
1350 'code': {
1351 'files_to_cog': """\
1352 # A locally resolved file name.
1353 test.cog
1354 """,
1356 'test.cog': """\
1357 //[[[cog
1358 import myampsubmodule
1359 //]]]
1360 //[[[end]]]
1361 """,
1363 'test.out': """\
1364 //[[[cog
1365 import myampsubmodule
1366 //]]]
1367 Hello from myampsubmodule
1368 //[[[end]]]
1369 """,
1371 'myampsubmodule.py': """\
1372 import cog
1373 cog.outl("Hello from myampsubmodule")
1374 """
1375 }
1376 }
1378 makeFiles(d)
1379 print(os.path.abspath("code/test.out"))
1380 self.cog.callableMain(['argv0', '-r', '&code/files_to_cog'])
1381 self.assertFilesSame('code/test.cog', 'code/test.out')
1383 def run_with_verbosity(self, verbosity):
1384 d = {
1385 'unchanged.cog': """\
1386 //[[[cog
1387 cog.outl("hello world")
1388 //]]]
1389 hello world
1390 //[[[end]]]
1391 """,
1393 'changed.cog': """\
1394 //[[[cog
1395 cog.outl("goodbye cruel world")
1396 //]]]
1397 //[[[end]]]
1398 """,
1400 'cogfiles.txt': """\
1401 unchanged.cog
1402 changed.cog
1403 """
1404 }
1406 makeFiles(d)
1407 self.cog.callableMain(['argv0', '-r', '--verbosity='+verbosity, '@cogfiles.txt'])
1408 output = self.output.getvalue()
1409 return output
1411 def test_verbosity0(self):
1412 output = self.run_with_verbosity("0")
1413 self.assertEqual(output, "")
1415 def test_verbosity1(self):
1416 output = self.run_with_verbosity("1")
1417 self.assertEqual(output, "Cogging changed.cog (changed)\n")
1419 def test_verbosity2(self):
1420 output = self.run_with_verbosity("2")
1421 self.assertEqual(output, "Cogging unchanged.cog\nCogging changed.cog (changed)\n")
1424class CogTestLineEndings(TestCaseWithTempDir):
1425 """Tests for -U option (force LF line-endings in output)."""
1427 lines_in = ['Some text.',
1428 '//[[[cog',
1429 'cog.outl("Cog text")',
1430 '//]]]',
1431 'gobbledegook.',
1432 '//[[[end]]]',
1433 'epilogue.',
1434 '']
1436 lines_out = ['Some text.',
1437 '//[[[cog',
1438 'cog.outl("Cog text")',
1439 '//]]]',
1440 'Cog text',
1441 '//[[[end]]]',
1442 'epilogue.',
1443 '']
1445 def testOutputNativeEol(self):
1446 makeFiles({'infile': '\n'.join(self.lines_in)})
1447 self.cog.callableMain(['argv0', '-o', 'outfile', 'infile'])
1448 self.assertFileContent('outfile', os.linesep.join(self.lines_out))
1450 def testOutputLfEol(self):
1451 makeFiles({'infile': '\n'.join(self.lines_in)})
1452 self.cog.callableMain(['argv0', '-U', '-o', 'outfile', 'infile'])
1453 self.assertFileContent('outfile', '\n'.join(self.lines_out))
1455 def testReplaceNativeEol(self):
1456 makeFiles({'test.cog': '\n'.join(self.lines_in)})
1457 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1458 self.assertFileContent('test.cog', os.linesep.join(self.lines_out))
1460 def testReplaceLfEol(self):
1461 makeFiles({'test.cog': '\n'.join(self.lines_in)})
1462 self.cog.callableMain(['argv0', '-U', '-r', 'test.cog'])
1463 self.assertFileContent('test.cog', '\n'.join(self.lines_out))
1466class CogTestCharacterEncoding(TestCaseWithTempDir):
1468 def testSimple(self):
1469 d = {
1470 'test.cog': b"""\
1471 // This is my C++ file.
1472 //[[[cog
1473 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
1474 //]]]
1475 //[[[end]]]
1476 """,
1478 'test.out': b"""\
1479 // This is my C++ file.
1480 //[[[cog
1481 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
1482 //]]]
1483 // Unicode: \xe1\x88\xb4 (U+1234)
1484 //[[[end]]]
1485 """.replace(b"\n", os.linesep.encode()),
1486 }
1488 makeFiles(d)
1489 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1490 self.assertFilesSame('test.cog', 'test.out')
1491 output = self.output.getvalue()
1492 self.assertIn("(changed)", output)
1494 def testFileEncodingOption(self):
1495 d = {
1496 'test.cog': b"""\
1497 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
1498 //[[[cog
1499 cog.outl("\xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe")
1500 //]]]
1501 //[[[end]]]
1502 """,
1504 'test.out': b"""\
1505 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
1506 //[[[cog
1507 cog.outl("\xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe")
1508 //]]]
1509 \xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe
1510 //[[[end]]]
1511 """.replace(b"\n", os.linesep.encode()),
1512 }
1514 makeFiles(d)
1515 self.cog.callableMain(['argv0', '-n', 'cp1251', '-r', 'test.cog'])
1516 self.assertFilesSame('test.cog', 'test.out')
1517 output = self.output.getvalue()
1518 self.assertIn("(changed)", output)
1521class TestCaseWithImports(TestCaseWithTempDir):
1522 """ When running tests which import modules, the sys.modules list
1523 leaks from one test to the next. This test case class scrubs
1524 the list after each run to keep the tests isolated from each other.
1525 """
1527 def setUp(self):
1528 super().setUp()
1529 self.sysmodulekeys = list(sys.modules)
1531 def tearDown(self):
1532 modstoscrub = [
1533 modname
1534 for modname in sys.modules
1535 if modname not in self.sysmodulekeys
1536 ]
1537 for modname in modstoscrub:
1538 del sys.modules[modname]
1539 super().tearDown()
1542class CogIncludeTests(TestCaseWithImports):
1543 dincludes = {
1544 'test.cog': """\
1545 //[[[cog
1546 import mymodule
1547 //]]]
1548 //[[[end]]]
1549 """,
1551 'test.out': """\
1552 //[[[cog
1553 import mymodule
1554 //]]]
1555 Hello from mymodule
1556 //[[[end]]]
1557 """,
1559 'test2.out': """\
1560 //[[[cog
1561 import mymodule
1562 //]]]
1563 Hello from mymodule in inc2
1564 //[[[end]]]
1565 """,
1567 'include': {
1568 'mymodule.py': """\
1569 import cog
1570 cog.outl("Hello from mymodule")
1571 """
1572 },
1574 'inc2': {
1575 'mymodule.py': """\
1576 import cog
1577 cog.outl("Hello from mymodule in inc2")
1578 """
1579 },
1581 'inc3': {
1582 'someothermodule.py': """\
1583 import cog
1584 cog.outl("This is some other module.")
1585 """
1586 },
1587 }
1589 def testNeedIncludePath(self):
1590 # Try it without the -I, to see that an ImportError happens.
1591 makeFiles(self.dincludes)
1592 msg = "(ImportError|ModuleNotFoundError): No module named '?mymodule'?"
1593 with self.assertRaisesRegex(CogUserException, msg):
1594 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1596 def testIncludePath(self):
1597 # Test that -I adds include directories properly.
1598 makeFiles(self.dincludes)
1599 self.cog.callableMain(['argv0', '-r', '-I', 'include', 'test.cog'])
1600 self.assertFilesSame('test.cog', 'test.out')
1602 def testTwoIncludePaths(self):
1603 # Test that two -I's add include directories properly.
1604 makeFiles(self.dincludes)
1605 self.cog.callableMain(['argv0', '-r', '-I', 'include', '-I', 'inc2', 'test.cog'])
1606 self.assertFilesSame('test.cog', 'test.out')
1608 def testTwoIncludePaths2(self):
1609 # Test that two -I's add include directories properly.
1610 makeFiles(self.dincludes)
1611 self.cog.callableMain(['argv0', '-r', '-I', 'inc2', '-I', 'include', 'test.cog'])
1612 self.assertFilesSame('test.cog', 'test2.out')
1614 def testUselessIncludePath(self):
1615 # Test that the search will continue past the first directory.
1616 makeFiles(self.dincludes)
1617 self.cog.callableMain(['argv0', '-r', '-I', 'inc3', '-I', 'include', 'test.cog'])
1618 self.assertFilesSame('test.cog', 'test.out')
1620 def testSysPathIsUnchanged(self):
1621 d = {
1622 'bad.cog': """\
1623 //[[[cog cog.error("Oh no!") ]]]
1624 //[[[end]]]
1625 """,
1626 'good.cog': """\
1627 //[[[cog cog.outl("Oh yes!") ]]]
1628 //[[[end]]]
1629 """,
1630 }
1632 makeFiles(d)
1633 # Is it unchanged just by creating a cog engine?
1634 oldsyspath = sys.path[:]
1635 self.newCog()
1636 self.assertEqual(oldsyspath, sys.path)
1637 # Is it unchanged for a successful run?
1638 self.newCog()
1639 self.cog.callableMain(['argv0', '-r', 'good.cog'])
1640 self.assertEqual(oldsyspath, sys.path)
1641 # Is it unchanged for a successful run with includes?
1642 self.newCog()
1643 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'good.cog'])
1644 self.assertEqual(oldsyspath, sys.path)
1645 # Is it unchanged for a successful run with two includes?
1646 self.newCog()
1647 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'good.cog'])
1648 self.assertEqual(oldsyspath, sys.path)
1649 # Is it unchanged for a failed run?
1650 self.newCog()
1651 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1652 self.cog.callableMain(['argv0', '-r', 'bad.cog'])
1653 self.assertEqual(oldsyspath, sys.path)
1654 # Is it unchanged for a failed run with includes?
1655 self.newCog()
1656 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1657 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'bad.cog'])
1658 self.assertEqual(oldsyspath, sys.path)
1659 # Is it unchanged for a failed run with two includes?
1660 self.newCog()
1661 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1662 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'bad.cog'])
1663 self.assertEqual(oldsyspath, sys.path)
1665 def testSubDirectories(self):
1666 # Test that relative paths on the command line work, with includes.
1668 d = {
1669 'code': {
1670 'test.cog': """\
1671 //[[[cog
1672 import mysubmodule
1673 //]]]
1674 //[[[end]]]
1675 """,
1677 'test.out': """\
1678 //[[[cog
1679 import mysubmodule
1680 //]]]
1681 Hello from mysubmodule
1682 //[[[end]]]
1683 """,
1685 'mysubmodule.py': """\
1686 import cog
1687 cog.outl("Hello from mysubmodule")
1688 """
1689 }
1690 }
1692 makeFiles(d)
1693 # We should be able to invoke cog without the -I switch, and it will
1694 # auto-include the current directory
1695 self.cog.callableMain(['argv0', '-r', 'code/test.cog'])
1696 self.assertFilesSame('code/test.cog', 'code/test.out')
1699class CogTestsInFiles(TestCaseWithTempDir):
1701 def testWarnIfNoCogCode(self):
1702 # Test that the -e switch warns if there is no Cog code.
1703 d = {
1704 'with.cog': """\
1705 //[[[cog
1706 cog.outl("hello world")
1707 //]]]
1708 hello world
1709 //[[[end]]]
1710 """,
1712 'without.cog': """\
1713 There's no cog
1714 code in this file.
1715 """,
1716 }
1718 makeFiles(d)
1719 self.cog.callableMain(['argv0', '-e', 'with.cog'])
1720 output = self.output.getvalue()
1721 self.assertNotIn("Warning", output)
1722 self.newCog()
1723 self.cog.callableMain(['argv0', '-e', 'without.cog'])
1724 output = self.output.getvalue()
1725 self.assertIn("Warning: no cog code found in without.cog", output)
1726 self.newCog()
1727 self.cog.callableMain(['argv0', 'without.cog'])
1728 output = self.output.getvalue()
1729 self.assertNotIn("Warning", output)
1731 def testFileNameProps(self):
1732 d = {
1733 'cog1.txt': """\
1734 //[[[cog
1735 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1736 //]]]
1737 this is cog1.txt in, cog1.txt out
1738 [[[end]]]
1739 """,
1741 'cog1.out': """\
1742 //[[[cog
1743 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1744 //]]]
1745 This is cog1.txt in, cog1.txt out
1746 [[[end]]]
1747 """,
1749 'cog1out.out': """\
1750 //[[[cog
1751 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1752 //]]]
1753 This is cog1.txt in, cog1out.txt out
1754 [[[end]]]
1755 """,
1756 }
1758 makeFiles(d)
1759 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
1760 self.assertFilesSame('cog1.txt', 'cog1.out')
1761 self.newCog()
1762 self.cog.callableMain(['argv0', '-o', 'cog1out.txt', 'cog1.txt'])
1763 self.assertFilesSame('cog1out.txt', 'cog1out.out')
1765 def testGlobalsDontCrossFiles(self):
1766 # Make sure that global values don't get shared between files.
1767 d = {
1768 'one.cog': """\
1769 //[[[cog s = "This was set in one.cog" ]]]
1770 //[[[end]]]
1771 //[[[cog cog.outl(s) ]]]
1772 //[[[end]]]
1773 """,
1775 'one.out': """\
1776 //[[[cog s = "This was set in one.cog" ]]]
1777 //[[[end]]]
1778 //[[[cog cog.outl(s) ]]]
1779 This was set in one.cog
1780 //[[[end]]]
1781 """,
1783 'two.cog': """\
1784 //[[[cog
1785 try:
1786 cog.outl(s)
1787 except NameError:
1788 cog.outl("s isn't set!")
1789 //]]]
1790 //[[[end]]]
1791 """,
1793 'two.out': """\
1794 //[[[cog
1795 try:
1796 cog.outl(s)
1797 except NameError:
1798 cog.outl("s isn't set!")
1799 //]]]
1800 s isn't set!
1801 //[[[end]]]
1802 """,
1804 'cogfiles.txt': """\
1805 # Please run cog
1806 one.cog
1808 two.cog
1809 """
1810 }
1812 makeFiles(d)
1813 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1814 self.assertFilesSame('one.cog', 'one.out')
1815 self.assertFilesSame('two.cog', 'two.out')
1816 output = self.output.getvalue()
1817 self.assertIn("(changed)", output)
1819 def testRemoveGeneratedOutput(self):
1820 d = {
1821 'cog1.txt': """\
1822 //[[[cog
1823 cog.outl("This line was generated.")
1824 //]]]
1825 This line was generated.
1826 //[[[end]]]
1827 This line was not.
1828 """,
1830 'cog1.out': """\
1831 //[[[cog
1832 cog.outl("This line was generated.")
1833 //]]]
1834 //[[[end]]]
1835 This line was not.
1836 """,
1838 'cog1.out2': """\
1839 //[[[cog
1840 cog.outl("This line was generated.")
1841 //]]]
1842 This line was generated.
1843 //[[[end]]]
1844 This line was not.
1845 """,
1846 }
1848 makeFiles(d)
1849 # Remove generated output.
1850 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt'])
1851 self.assertFilesSame('cog1.txt', 'cog1.out')
1852 self.newCog()
1853 # Regenerate the generated output.
1854 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
1855 self.assertFilesSame('cog1.txt', 'cog1.out2')
1856 self.newCog()
1857 # Remove the generated output again.
1858 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt'])
1859 self.assertFilesSame('cog1.txt', 'cog1.out')
1861 def testMsgCall(self):
1862 infile = """\
1863 #[[[cog
1864 cog.msg("Hello there!")
1865 #]]]
1866 #[[[end]]]
1867 """
1868 infile = reindentBlock(infile)
1869 self.assertEqual(self.cog.processString(infile), infile)
1870 output = self.output.getvalue()
1871 self.assertEqual(output, "Message: Hello there!\n")
1873 def testErrorMessageHasNoTraceback(self):
1874 # Test that a Cog error is printed to stderr with no traceback.
1876 d = {
1877 'cog1.txt': """\
1878 //[[[cog
1879 cog.outl("This line was newly")
1880 cog.outl("generated by cog")
1881 cog.outl("blah blah.")
1882 //]]]
1883 Xhis line was newly
1884 generated by cog
1885 blah blah.
1886 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
1887 """,
1888 }
1890 makeFiles(d)
1891 stderr = io.StringIO()
1892 self.cog.setOutput(stderr=stderr)
1893 self.cog.main(['argv0', '-c', '-r', "cog1.txt"])
1894 self.assertEqual(self.output.getvalue(), "Cogging cog1.txt\n")
1895 self.assertEqual(stderr.getvalue(), "cog1.txt(9): Output has been edited! Delete old checksum to unprotect.\n")
1897 def testDashD(self):
1898 d = {
1899 'test.cog': """\
1900 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1901 --[[[end]]]
1902 """,
1904 'test.kablooey': """\
1905 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1906 Defined fooey as kablooey
1907 --[[[end]]]
1908 """,
1910 'test.einstein': """\
1911 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1912 Defined fooey as e=mc2
1913 --[[[end]]]
1914 """,
1915 }
1917 makeFiles(d)
1918 self.cog.callableMain(['argv0', '-r', '-D', 'fooey=kablooey', 'test.cog'])
1919 self.assertFilesSame('test.cog', 'test.kablooey')
1920 makeFiles(d)
1921 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', 'test.cog'])
1922 self.assertFilesSame('test.cog', 'test.kablooey')
1923 makeFiles(d)
1924 self.cog.callableMain(['argv0', '-r', '-Dfooey=e=mc2', 'test.cog'])
1925 self.assertFilesSame('test.cog', 'test.einstein')
1926 makeFiles(d)
1927 self.cog.callableMain(['argv0', '-r', '-Dbar=quux', '-Dfooey=kablooey', 'test.cog'])
1928 self.assertFilesSame('test.cog', 'test.kablooey')
1929 makeFiles(d)
1930 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', '-Dbar=quux', 'test.cog'])
1931 self.assertFilesSame('test.cog', 'test.kablooey')
1932 makeFiles(d)
1933 self.cog.callableMain(['argv0', '-r', '-Dfooey=gooey', '-Dfooey=kablooey', 'test.cog'])
1934 self.assertFilesSame('test.cog', 'test.kablooey')
1936 def testOutputToStdout(self):
1937 d = {
1938 'test.cog': """\
1939 --[[[cog cog.outl('Hey there!') ]]]
1940 --[[[end]]]
1941 """
1942 }
1944 makeFiles(d)
1945 stderr = io.StringIO()
1946 self.cog.setOutput(stderr=stderr)
1947 self.cog.callableMain(['argv0', 'test.cog'])
1948 output = self.output.getvalue()
1949 outerr = stderr.getvalue()
1950 self.assertEqual(output, "--[[[cog cog.outl('Hey there!') ]]]\nHey there!\n--[[[end]]]\n")
1951 self.assertEqual(outerr, "")
1953 def testReadFromStdin(self):
1954 stdin = io.StringIO("--[[[cog cog.outl('Wow') ]]]\n--[[[end]]]\n")
1955 def restore_stdin(old_stdin):
1956 sys.stdin = old_stdin
1957 self.addCleanup(restore_stdin, sys.stdin)
1958 sys.stdin = stdin
1960 stderr = io.StringIO()
1961 self.cog.setOutput(stderr=stderr)
1962 self.cog.callableMain(['argv0', '-'])
1963 output = self.output.getvalue()
1964 outerr = stderr.getvalue()
1965 self.assertEqual(output, "--[[[cog cog.outl('Wow') ]]]\nWow\n--[[[end]]]\n")
1966 self.assertEqual(outerr, "")
1968 def testSuffixOutputLines(self):
1969 d = {
1970 'test.cog': """\
1971 Hey there.
1972 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
1973 ;[[[end]]]
1974 Good bye.
1975 """,
1977 'test.out': """\
1978 Hey there.
1979 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
1980 a (foo)
1981 b (foo)
1982 """ # These three trailing spaces are important.
1983 # The suffix is not applied to completely blank lines.
1984 """
1985 c (foo)
1986 ;[[[end]]]
1987 Good bye.
1988 """,
1989 }
1991 makeFiles(d)
1992 self.cog.callableMain(['argv0', '-r', '-s', ' (foo)', 'test.cog'])
1993 self.assertFilesSame('test.cog', 'test.out')
1995 def testEmptySuffix(self):
1996 d = {
1997 'test.cog': """\
1998 ;[[[cog cog.outl('a\\nb\\nc') ]]]
1999 ;[[[end]]]
2000 """,
2002 'test.out': """\
2003 ;[[[cog cog.outl('a\\nb\\nc') ]]]
2004 a
2005 b
2006 c
2007 ;[[[end]]]
2008 """,
2009 }
2011 makeFiles(d)
2012 self.cog.callableMain(['argv0', '-r', '-s', '', 'test.cog'])
2013 self.assertFilesSame('test.cog', 'test.out')
2015 def testHellishSuffix(self):
2016 d = {
2017 'test.cog': """\
2018 ;[[[cog cog.outl('a\\n\\nb') ]]]
2019 """,
2021 'test.out': """\
2022 ;[[[cog cog.outl('a\\n\\nb') ]]]
2023 a /\\n*+([)]><
2025 b /\\n*+([)]><
2026 """,
2027 }
2029 makeFiles(d)
2030 self.cog.callableMain(['argv0', '-z', '-r', '-s', r' /\n*+([)]><', 'test.cog'])
2031 self.assertFilesSame('test.cog', 'test.out')
2033 def testPrologue(self):
2034 d = {
2035 'test.cog': """\
2036 Some text.
2037 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
2038 //[[[end]]]
2039 epilogue.
2040 """,
2042 'test.out': """\
2043 Some text.
2044 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
2045 1.4142135623
2046 //[[[end]]]
2047 epilogue.
2048 """,
2049 }
2051 makeFiles(d)
2052 self.cog.callableMain(['argv0', '-r', '-p', 'import math', 'test.cog'])
2053 self.assertFilesSame('test.cog', 'test.out')
2055 def testThreads(self):
2056 # Test that the implicitly imported cog module is actually different for
2057 # different threads.
2058 numthreads = 20
2060 d = {}
2061 for i in range(numthreads):
2062 d[f'f{i}.cog'] = (
2063 "x\n" * i +
2064 "[[[cog\n" +
2065 f"assert cog.firstLineNum == int(FIRST) == {i+1}\n" +
2066 "]]]\n" +
2067 "[[[end]]]\n"
2068 )
2069 makeFiles(d)
2071 results = []
2073 def thread_main(num):
2074 try:
2075 ret = Cog().main(
2076 ['cog.py', '-r', '-D', f'FIRST={num+1}', f'f{num}.cog']
2077 )
2078 assert ret == 0
2079 except Exception as exc: # pragma: no cover (only happens on test failure)
2080 results.append(exc)
2081 else:
2082 results.append(None)
2084 ts = [threading.Thread(target=thread_main, args=(i,)) for i in range(numthreads)]
2085 for t in ts:
2086 t.start()
2087 for t in ts:
2088 t.join()
2089 assert results == [None] * numthreads
2092class CheckTests(TestCaseWithTempDir):
2093 def run_check(self, args, status=0):
2094 actual_status = self.cog.main(['argv0', '--check'] + args)
2095 print(self.output.getvalue())
2096 self.assertEqual(status, actual_status)
2098 def assert_made_files_unchanged(self, d):
2099 for name, content in d.items():
2100 content = reindentBlock(content)
2101 if os.name == 'nt':
2102 content = content.replace("\n", "\r\n")
2103 self.assertFileContent(name, content)
2105 def test_check_no_cog(self):
2106 d = {
2107 'hello.txt': """\
2108 Hello.
2109 """,
2110 }
2111 makeFiles(d)
2112 self.run_check(['hello.txt'], status=0)
2113 self.assertEqual(self.output.getvalue(), "Checking hello.txt\n")
2114 self.assert_made_files_unchanged(d)
2116 def test_check_good(self):
2117 d = {
2118 'unchanged.cog': """\
2119 //[[[cog
2120 cog.outl("hello world")
2121 //]]]
2122 hello world
2123 //[[[end]]]
2124 """,
2125 }
2126 makeFiles(d)
2127 self.run_check(['unchanged.cog'], status=0)
2128 self.assertEqual(self.output.getvalue(), "Checking unchanged.cog\n")
2129 self.assert_made_files_unchanged(d)
2131 def test_check_bad(self):
2132 d = {
2133 'changed.cog': """\
2134 //[[[cog
2135 cog.outl("goodbye world")
2136 //]]]
2137 hello world
2138 //[[[end]]]
2139 """,
2140 }
2141 makeFiles(d)
2142 self.run_check(['changed.cog'], status=5)
2143 self.assertEqual(self.output.getvalue(), "Checking changed.cog (changed)\nCheck failed\n")
2144 self.assert_made_files_unchanged(d)
2146 def test_check_mixed(self):
2147 d = {
2148 'unchanged.cog': """\
2149 //[[[cog
2150 cog.outl("hello world")
2151 //]]]
2152 hello world
2153 //[[[end]]]
2154 """,
2155 'changed.cog': """\
2156 //[[[cog
2157 cog.outl("goodbye world")
2158 //]]]
2159 hello world
2160 //[[[end]]]
2161 """,
2162 }
2163 makeFiles(d)
2164 for verbosity, output in [
2165 ("0", "Check failed\n"),
2166 ("1", "Checking changed.cog (changed)\nCheck failed\n"),
2167 ("2", "Checking unchanged.cog\nChecking changed.cog (changed)\nCheck failed\n"),
2168 ]:
2169 self.newCog()
2170 self.run_check(['--verbosity=%s' % verbosity, 'unchanged.cog', 'changed.cog'], status=5)
2171 self.assertEqual(self.output.getvalue(), output)
2172 self.assert_made_files_unchanged(d)
2174 def test_check_with_good_checksum(self):
2175 d = {
2176 'good.txt': """\
2177 //[[[cog
2178 cog.outl("This line was newly")
2179 cog.outl("generated by cog")
2180 cog.outl("blah blah.")
2181 //]]]
2182 This line was newly
2183 generated by cog
2184 blah blah.
2185 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2186 """,
2187 }
2188 makeFiles(d)
2189 # Have to use -c with --check if there are checksums in the file.
2190 self.run_check(['-c', 'good.txt'], status=0)
2191 self.assertEqual(self.output.getvalue(), "Checking good.txt\n")
2192 self.assert_made_files_unchanged(d)
2194 def test_check_with_bad_checksum(self):
2195 d = {
2196 'bad.txt': """\
2197 //[[[cog
2198 cog.outl("This line was newly")
2199 cog.outl("generated by cog")
2200 cog.outl("blah blah.")
2201 //]]]
2202 This line was newly
2203 generated by cog
2204 blah blah.
2205 //[[[end]]] (checksum: a9999999e5ad6b95c9e9a184b26f4346)
2206 """,
2207 }
2208 makeFiles(d)
2209 # Have to use -c with --check if there are checksums in the file.
2210 self.run_check(['-c', 'bad.txt'], status=1)
2211 self.assertEqual(self.output.getvalue(), "Checking bad.txt\nbad.txt(9): Output has been edited! Delete old checksum to unprotect.\n")
2212 self.assert_made_files_unchanged(d)
2215class WritabilityTests(TestCaseWithTempDir):
2217 d = {
2218 'test.cog': """\
2219 //[[[cog
2220 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
2221 cog.outl("void %s();" % fn)
2222 //]]]
2223 //[[[end]]]
2224 """,
2226 'test.out': """\
2227 //[[[cog
2228 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
2229 cog.outl("void %s();" % fn)
2230 //]]]
2231 void DoSomething();
2232 void DoAnotherThing();
2233 void DoLastThing();
2234 //[[[end]]]
2235 """,
2236 }
2238 if os.name == 'nt': 2238 ↛ 2240line 2238 didn't jump to line 2240, because the condition on line 2238 was never true
2239 # for Windows
2240 cmd_w_args = 'attrib -R %s'
2241 cmd_w_asterisk = 'attrib -R *'
2242 else:
2243 # for unix-like
2244 cmd_w_args = 'chmod +w %s'
2245 cmd_w_asterisk = 'chmod +w *'
2247 def setUp(self):
2248 super().setUp()
2249 makeFiles(self.d)
2250 self.testcog = os.path.join(self.tempdir, 'test.cog')
2251 os.chmod(self.testcog, stat.S_IREAD) # Make the file readonly.
2252 assert not os.access(self.testcog, os.W_OK)
2254 def tearDown(self):
2255 os.chmod(self.testcog, stat.S_IWRITE) # Make the file writable again.
2256 super().tearDown()
2258 def testReadonlyNoCommand(self):
2259 with self.assertRaisesRegex(CogError, "^Can't overwrite test.cog$"):
2260 self.cog.callableMain(['argv0', '-r', 'test.cog'])
2261 assert not os.access(self.testcog, os.W_OK)
2263 def testReadonlyWithCommand(self):
2264 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_args, 'test.cog'])
2265 self.assertFilesSame('test.cog', 'test.out')
2266 assert os.access(self.testcog, os.W_OK)
2268 def testReadonlyWithCommandWithNoSlot(self):
2269 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_asterisk, 'test.cog'])
2270 self.assertFilesSame('test.cog', 'test.out')
2271 assert os.access(self.testcog, os.W_OK)
2273 def testReadonlyWithIneffectualCommand(self):
2274 with self.assertRaisesRegex(CogError, "^Couldn't make test.cog writable$"):
2275 self.cog.callableMain(['argv0', '-r', '-w', 'echo %s', 'test.cog'])
2276 assert not os.access(self.testcog, os.W_OK)
2279class ChecksumTests(TestCaseWithTempDir):
2281 def testCreateChecksumOutput(self):
2282 d = {
2283 'cog1.txt': """\
2284 //[[[cog
2285 cog.outl("This line was generated.")
2286 //]]]
2287 This line was generated.
2288 //[[[end]]]
2289 This line was not.
2290 """,
2292 'cog1.out': """\
2293 //[[[cog
2294 cog.outl("This line was generated.")
2295 //]]]
2296 This line was generated.
2297 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
2298 This line was not.
2299 """,
2300 }
2302 makeFiles(d)
2303 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt'])
2304 self.assertFilesSame('cog1.txt', 'cog1.out')
2306 def testCheckChecksumOutput(self):
2307 d = {
2308 'cog1.txt': """\
2309 //[[[cog
2310 cog.outl("This line was newly")
2311 cog.outl("generated by cog")
2312 cog.outl("blah blah.")
2313 //]]]
2314 This line was generated.
2315 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
2316 """,
2318 'cog1.out': """\
2319 //[[[cog
2320 cog.outl("This line was newly")
2321 cog.outl("generated by cog")
2322 cog.outl("blah blah.")
2323 //]]]
2324 This line was newly
2325 generated by cog
2326 blah blah.
2327 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2328 """,
2329 }
2331 makeFiles(d)
2332 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt'])
2333 self.assertFilesSame('cog1.txt', 'cog1.out')
2335 def testRemoveChecksumOutput(self):
2336 d = {
2337 'cog1.txt': """\
2338 //[[[cog
2339 cog.outl("This line was newly")
2340 cog.outl("generated by cog")
2341 cog.outl("blah blah.")
2342 //]]]
2343 This line was generated.
2344 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) fooey
2345 """,
2347 'cog1.out': """\
2348 //[[[cog
2349 cog.outl("This line was newly")
2350 cog.outl("generated by cog")
2351 cog.outl("blah blah.")
2352 //]]]
2353 This line was newly
2354 generated by cog
2355 blah blah.
2356 //[[[end]]] fooey
2357 """,
2358 }
2360 makeFiles(d)
2361 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
2362 self.assertFilesSame('cog1.txt', 'cog1.out')
2364 def testTamperedChecksumOutput(self):
2365 d = {
2366 'cog1.txt': """\
2367 //[[[cog
2368 cog.outl("This line was newly")
2369 cog.outl("generated by cog")
2370 cog.outl("blah blah.")
2371 //]]]
2372 Xhis line was newly
2373 generated by cog
2374 blah blah.
2375 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2376 """,
2378 'cog2.txt': """\
2379 //[[[cog
2380 cog.outl("This line was newly")
2381 cog.outl("generated by cog")
2382 cog.outl("blah blah.")
2383 //]]]
2384 This line was newly
2385 generated by cog
2386 blah blah!
2387 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2388 """,
2390 'cog3.txt': """\
2391 //[[[cog
2392 cog.outl("This line was newly")
2393 cog.outl("generated by cog")
2394 cog.outl("blah blah.")
2395 //]]]
2397 This line was newly
2398 generated by cog
2399 blah blah.
2400 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2401 """,
2403 'cog4.txt': """\
2404 //[[[cog
2405 cog.outl("This line was newly")
2406 cog.outl("generated by cog")
2407 cog.outl("blah blah.")
2408 //]]]
2409 This line was newly
2410 generated by cog
2411 blah blah..
2412 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2413 """,
2415 'cog5.txt': """\
2416 //[[[cog
2417 cog.outl("This line was newly")
2418 cog.outl("generated by cog")
2419 cog.outl("blah blah.")
2420 //]]]
2421 This line was newly
2422 generated by cog
2423 blah blah.
2424 extra
2425 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2426 """,
2428 'cog6.txt': """\
2429 //[[[cog
2430 cog.outl("This line was newly")
2431 cog.outl("generated by cog")
2432 cog.outl("blah blah.")
2433 //]]]
2434 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2435 """,
2436 }
2438 makeFiles(d)
2439 with self.assertRaisesRegex(CogError,
2440 r"^cog1.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
2441 self.cog.callableMain(['argv0', '-c', "cog1.txt"])
2442 with self.assertRaisesRegex(CogError,
2443 r"^cog2.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
2444 self.cog.callableMain(['argv0', '-c', "cog2.txt"])
2445 with self.assertRaisesRegex(CogError,
2446 r"^cog3.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"):
2447 self.cog.callableMain(['argv0', '-c', "cog3.txt"])
2448 with self.assertRaisesRegex(CogError,
2449 r"^cog4.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
2450 self.cog.callableMain(['argv0', '-c', "cog4.txt"])
2451 with self.assertRaisesRegex(CogError,
2452 r"^cog5.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"):
2453 self.cog.callableMain(['argv0', '-c', "cog5.txt"])
2454 with self.assertRaisesRegex(CogError,
2455 r"^cog6.txt\(6\): Output has been edited! Delete old checksum to unprotect.$"):
2456 self.cog.callableMain(['argv0', '-c', "cog6.txt"])
2458 def testArgvIsntModified(self):
2459 argv = ['argv0', '-v']
2460 orig_argv = argv[:]
2461 self.cog.callableMain(argv)
2462 self.assertEqual(argv, orig_argv)
2465class CustomMarkerTests(TestCaseWithTempDir):
2467 def testCustomerMarkers(self):
2468 d = {
2469 'test.cog': """\
2470 //{{
2471 cog.outl("void %s();" % "MyFunction")
2472 //}}
2473 //{{end}}
2474 """,
2476 'test.out': """\
2477 //{{
2478 cog.outl("void %s();" % "MyFunction")
2479 //}}
2480 void MyFunction();
2481 //{{end}}
2482 """,
2483 }
2485 makeFiles(d)
2486 self.cog.callableMain([
2487 'argv0', '-r',
2488 '--markers={{ }} {{end}}',
2489 'test.cog'
2490 ])
2491 self.assertFilesSame('test.cog', 'test.out')
2493 def testTrulyWackyMarkers(self):
2494 # Make sure the markers are properly re-escaped.
2495 d = {
2496 'test.cog': """\
2497 //**(
2498 cog.outl("void %s();" % "MyFunction")
2499 //**)
2500 //**(end)**
2501 """,
2503 'test.out': """\
2504 //**(
2505 cog.outl("void %s();" % "MyFunction")
2506 //**)
2507 void MyFunction();
2508 //**(end)**
2509 """,
2510 }
2512 makeFiles(d)
2513 self.cog.callableMain([
2514 'argv0', '-r',
2515 '--markers=**( **) **(end)**',
2516 'test.cog'
2517 ])
2518 self.assertFilesSame('test.cog', 'test.out')
2520 def testChangeJustOneMarker(self):
2521 d = {
2522 'test.cog': """\
2523 //**(
2524 cog.outl("void %s();" % "MyFunction")
2525 //]]]
2526 //[[[end]]]
2527 """,
2529 'test.out': """\
2530 //**(
2531 cog.outl("void %s();" % "MyFunction")
2532 //]]]
2533 void MyFunction();
2534 //[[[end]]]
2535 """,
2536 }
2538 makeFiles(d)
2539 self.cog.callableMain([
2540 'argv0', '-r',
2541 '--markers=**( ]]] [[[end]]]',
2542 'test.cog'
2543 ])
2544 self.assertFilesSame('test.cog', 'test.out')
2547class BlakeTests(TestCaseWithTempDir):
2549 # Blake Winton's contributions.
2550 def testDeleteCode(self):
2551 # -o sets the output file.
2552 d = {
2553 'test.cog': """\
2554 // This is my C++ file.
2555 //[[[cog
2556 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
2557 for fn in fnames:
2558 cog.outl("void %s();" % fn)
2559 //]]]
2560 Some Sample Code Here
2561 //[[[end]]]Data Data
2562 And Some More
2563 """,
2565 'test.out': """\
2566 // This is my C++ file.
2567 void DoSomething();
2568 void DoAnotherThing();
2569 void DoLastThing();
2570 And Some More
2571 """,
2572 }
2574 makeFiles(d)
2575 self.cog.callableMain(['argv0', '-d', '-o', 'test.cogged', 'test.cog'])
2576 self.assertFilesSame('test.cogged', 'test.out')
2578 def testDeleteCodeWithDashRFails(self):
2579 d = {
2580 'test.cog': """\
2581 // This is my C++ file.
2582 """
2583 }
2585 makeFiles(d)
2586 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"):
2587 self.cog.callableMain(['argv0', '-r', '-d', 'test.cog'])
2589 def testSettingGlobals(self):
2590 # Blake Winton contributed a way to set the globals that will be used in
2591 # processFile().
2592 d = {
2593 'test.cog': """\
2594 // This is my C++ file.
2595 //[[[cog
2596 for fn in fnames:
2597 cog.outl("void %s();" % fn)
2598 //]]]
2599 Some Sample Code Here
2600 //[[[end]]]""",
2602 'test.out': """\
2603 // This is my C++ file.
2604 void DoBlake();
2605 void DoWinton();
2606 void DoContribution();
2607 """,
2608 }
2610 makeFiles(d)
2611 globals = {}
2612 globals['fnames'] = ['DoBlake', 'DoWinton', 'DoContribution']
2613 self.cog.options.bDeleteCode = True
2614 self.cog.processFile('test.cog', 'test.cogged', globals=globals)
2615 self.assertFilesSame('test.cogged', 'test.out')
2618class ErrorCallTests(TestCaseWithTempDir):
2620 def testErrorCallHasNoTraceback(self):
2621 # Test that cog.error() doesn't show a traceback.
2622 d = {
2623 'error.cog': """\
2624 //[[[cog
2625 cog.error("Something Bad!")
2626 //]]]
2627 //[[[end]]]
2628 """,
2629 }
2631 makeFiles(d)
2632 self.cog.main(['argv0', '-r', 'error.cog'])
2633 output = self.output.getvalue()
2634 self.assertEqual(output, "Cogging error.cog\nError: Something Bad!\n")
2636 def testRealErrorHasTraceback(self):
2637 # Test that a genuine error does show a traceback.
2638 d = {
2639 'error.cog': """\
2640 //[[[cog
2641 raise RuntimeError("Hey!")
2642 //]]]
2643 //[[[end]]]
2644 """,
2645 }
2647 makeFiles(d)
2648 self.cog.main(['argv0', '-r', 'error.cog'])
2649 output = self.output.getvalue()
2650 msg = 'Actual output:\n' + output
2651 self.assertTrue(output.startswith("Cogging error.cog\nTraceback (most recent"), msg)
2652 self.assertIn("RuntimeError: Hey!", output)
2655# Things not yet tested:
2656# - A bad -w command (currently fails silently).