Coverage for cogapp/test_cogapp.py: 29.57%
845 statements
« prev ^ index » next coverage.py v7.2.5, created at 2023-04-30 06:39 -0400
« prev ^ index » next coverage.py v7.2.5, created at 2023-04-30 06:39 -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 text1 = open(os.path.join(self.tempdir, sFName1), 'rb').read()
786 text2 = open(os.path.join(self.tempdir, sFName2), 'rb').read()
787 self.assertEqual(text1, text2)
789 def assertFileContent(self, sFName, sContent):
790 sAbsName = os.path.join(self.tempdir, sFName)
791 f = open(sAbsName, 'rb')
792 try:
793 sFileContent = f.read()
794 finally:
795 f.close()
796 self.assertEqual(sFileContent, sContent.encode("utf-8"))
799class ArgumentHandlingTests(TestCaseWithTempDir):
801 def testArgumentFailure(self):
802 # Return value 2 means usage problem.
803 self.assertEqual(self.cog.main(['argv0', '-j']), 2)
804 output = self.output.getvalue()
805 self.assertIn("option -j not recognized", output)
806 with self.assertRaisesRegex(CogUsageError, r"^No files to process$"):
807 self.cog.callableMain(['argv0'])
808 with self.assertRaisesRegex(CogUsageError, r"^option -j not recognized$"):
809 self.cog.callableMain(['argv0', '-j'])
811 def testNoDashOAndAtFile(self):
812 d = {
813 'cogfiles.txt': """\
814 # Please run cog
815 """
816 }
818 makeFiles(d)
819 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with @file$"):
820 self.cog.callableMain(['argv0', '-o', 'foo', '@cogfiles.txt'])
822 def testDashV(self):
823 self.assertEqual(self.cog.main(['argv0', '-v']), 0)
824 output = self.output.getvalue()
825 self.assertEqual('Cog version %s\n' % __version__, output)
827 def producesHelp(self, args):
828 self.newCog()
829 argv = ['argv0'] + args.split()
830 self.assertEqual(self.cog.main(argv), 0)
831 self.assertEqual(usage, self.output.getvalue())
833 def testDashH(self):
834 # -h or -? anywhere on the command line should just print help.
835 self.producesHelp("-h")
836 self.producesHelp("-?")
837 self.producesHelp("fooey.txt -h")
838 self.producesHelp("-o -r @fooey.txt -? @booey.txt")
840 def testDashOAndDashR(self):
841 d = {
842 'cogfile.txt': """\
843 # Please run cog
844 """
845 }
847 makeFiles(d)
848 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with -r \(they are opposites\)$"):
849 self.cog.callableMain(['argv0', '-o', 'foo', '-r', 'cogfile.txt'])
851 def testDashZ(self):
852 d = {
853 'test.cog': """\
854 // This is my C++ file.
855 //[[[cog
856 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
857 for fn in fnames:
858 cog.outl("void %s();" % fn)
859 //]]]
860 """,
862 'test.out': """\
863 // This is my C++ file.
864 //[[[cog
865 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
866 for fn in fnames:
867 cog.outl("void %s();" % fn)
868 //]]]
869 void DoSomething();
870 void DoAnotherThing();
871 void DoLastThing();
872 """,
873 }
875 makeFiles(d)
876 with self.assertRaisesRegex(CogError, r"^test.cog\(6\): Missing '\[\[\[end\]\]\]' before end of file.$"):
877 self.cog.callableMain(['argv0', '-r', 'test.cog'])
878 self.newCog()
879 self.cog.callableMain(['argv0', '-r', '-z', 'test.cog'])
880 self.assertFilesSame('test.cog', 'test.out')
882 def testBadDashD(self):
883 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
884 self.cog.callableMain(['argv0', '-Dfooey', 'cog.txt'])
885 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
886 self.cog.callableMain(['argv0', '-D', 'fooey', 'cog.txt'])
888 def testBadMarkers(self):
889 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'X'$"):
890 self.cog.callableMain(['argv0', '--markers=X'])
891 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'A B C D'$"):
892 self.cog.callableMain(['argv0', '--markers=A B C D'])
895class TestMain(TestCaseWithTempDir):
896 def setUp(self):
897 super().setUp()
898 self.old_argv = sys.argv[:]
899 self.old_stderr = sys.stderr
900 sys.stderr = io.StringIO()
902 def tearDown(self):
903 sys.stderr = self.old_stderr
904 sys.argv = self.old_argv
905 sys.modules.pop('mycode', None)
906 super().tearDown()
908 def test_main_function(self):
909 sys.argv = ["argv0", "-Z"]
910 ret = main()
911 self.assertEqual(ret, 2)
912 stderr = sys.stderr.getvalue()
913 self.assertEqual(stderr, 'option -Z not recognized\n(for help use -h)\n')
915 files = {
916 'test.cog': """\
917 //[[[cog
918 def func():
919 import mycode
920 mycode.boom()
921 //]]]
922 //[[[end]]]
923 -----
924 //[[[cog
925 func()
926 //]]]
927 //[[[end]]]
928 """,
930 'mycode.py': """\
931 def boom():
932 [][0]
933 """,
934 }
936 def test_error_report(self):
937 self.check_error_report()
939 def test_error_report_with_prologue(self):
940 self.check_error_report("-p", "#1\n#2")
942 def check_error_report(self, *args):
943 """Check that the error report is right."""
944 makeFiles(self.files)
945 sys.argv = ["argv0"] + list(args) + ["-r", "test.cog"]
946 main()
947 expected = reindentBlock("""\
948 Traceback (most recent call last):
949 File "test.cog", line 9, in <module>
950 func()
951 File "test.cog", line 4, in func
952 mycode.boom()
953 File "MYCODE", line 2, in boom
954 [][0]
955 IndexError: list index out of range
956 """)
957 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
958 assert expected == sys.stderr.getvalue()
960 def test_error_in_prologue(self):
961 makeFiles(self.files)
962 sys.argv = ["argv0", "-p", "import mycode; mycode.boom()", "-r", "test.cog"]
963 main()
964 expected = reindentBlock("""\
965 Traceback (most recent call last):
966 File "<prologue>", line 1, in <module>
967 import mycode; mycode.boom()
968 File "MYCODE", line 2, in boom
969 [][0]
970 IndexError: list index out of range
971 """)
972 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
973 assert expected == sys.stderr.getvalue()
977class TestFileHandling(TestCaseWithTempDir):
979 def testSimple(self):
980 d = {
981 'test.cog': """\
982 // This is my C++ file.
983 //[[[cog
984 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
985 for fn in fnames:
986 cog.outl("void %s();" % fn)
987 //]]]
988 //[[[end]]]
989 """,
991 'test.out': """\
992 // This is my C++ file.
993 //[[[cog
994 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
995 for fn in fnames:
996 cog.outl("void %s();" % fn)
997 //]]]
998 void DoSomething();
999 void DoAnotherThing();
1000 void DoLastThing();
1001 //[[[end]]]
1002 """,
1003 }
1005 makeFiles(d)
1006 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1007 self.assertFilesSame('test.cog', 'test.out')
1008 output = self.output.getvalue()
1009 self.assertIn("(changed)", output)
1011 def testPrintOutput(self):
1012 d = {
1013 'test.cog': """\
1014 // This is my C++ file.
1015 //[[[cog
1016 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1017 for fn in fnames:
1018 print("void %s();" % fn)
1019 //]]]
1020 //[[[end]]]
1021 """,
1023 'test.out': """\
1024 // This is my C++ file.
1025 //[[[cog
1026 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1027 for fn in fnames:
1028 print("void %s();" % fn)
1029 //]]]
1030 void DoSomething();
1031 void DoAnotherThing();
1032 void DoLastThing();
1033 //[[[end]]]
1034 """,
1035 }
1037 makeFiles(d)
1038 self.cog.callableMain(['argv0', '-rP', 'test.cog'])
1039 self.assertFilesSame('test.cog', 'test.out')
1040 output = self.output.getvalue()
1041 self.assertIn("(changed)", output)
1043 def testWildcards(self):
1044 d = {
1045 'test.cog': """\
1046 // This is my C++ file.
1047 //[[[cog
1048 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1049 for fn in fnames:
1050 cog.outl("void %s();" % fn)
1051 //]]]
1052 //[[[end]]]
1053 """,
1055 'test2.cog': """\
1056 // This is my C++ file.
1057 //[[[cog
1058 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1059 for fn in fnames:
1060 cog.outl("void %s();" % fn)
1061 //]]]
1062 //[[[end]]]
1063 """,
1065 'test.out': """\
1066 // This is my C++ file.
1067 //[[[cog
1068 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1069 for fn in fnames:
1070 cog.outl("void %s();" % fn)
1071 //]]]
1072 void DoSomething();
1073 void DoAnotherThing();
1074 void DoLastThing();
1075 //[[[end]]]
1076 """,
1078 'not_this_one.cog': """\
1079 // This is my C++ file.
1080 //[[[cog
1081 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1082 for fn in fnames:
1083 cog.outl("void %s();" % fn)
1084 //]]]
1085 //[[[end]]]
1086 """,
1088 'not_this_one.out': """\
1089 // This is my C++ file.
1090 //[[[cog
1091 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1092 for fn in fnames:
1093 cog.outl("void %s();" % fn)
1094 //]]]
1095 //[[[end]]]
1096 """,
1097 }
1099 makeFiles(d)
1100 self.cog.callableMain(['argv0', '-r', 't*.cog'])
1101 self.assertFilesSame('test.cog', 'test.out')
1102 self.assertFilesSame('test2.cog', 'test.out')
1103 self.assertFilesSame('not_this_one.cog', 'not_this_one.out')
1104 output = self.output.getvalue()
1105 self.assertIn("(changed)", output)
1107 def testOutputFile(self):
1108 # -o sets the output file.
1109 d = {
1110 'test.cog': """\
1111 // This is my C++ file.
1112 //[[[cog
1113 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1114 for fn in fnames:
1115 cog.outl("void %s();" % fn)
1116 //]]]
1117 //[[[end]]]
1118 """,
1120 'test.out': """\
1121 // This is my C++ file.
1122 //[[[cog
1123 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1124 for fn in fnames:
1125 cog.outl("void %s();" % fn)
1126 //]]]
1127 void DoSomething();
1128 void DoAnotherThing();
1129 void DoLastThing();
1130 //[[[end]]]
1131 """,
1132 }
1134 makeFiles(d)
1135 self.cog.callableMain(['argv0', '-o', 'in/a/dir/test.cogged', 'test.cog'])
1136 self.assertFilesSame('in/a/dir/test.cogged', 'test.out')
1138 def testAtFile(self):
1139 d = {
1140 'one.cog': """\
1141 //[[[cog
1142 cog.outl("hello world")
1143 //]]]
1144 //[[[end]]]
1145 """,
1147 'one.out': """\
1148 //[[[cog
1149 cog.outl("hello world")
1150 //]]]
1151 hello world
1152 //[[[end]]]
1153 """,
1155 'two.cog': """\
1156 //[[[cog
1157 cog.outl("goodbye cruel world")
1158 //]]]
1159 //[[[end]]]
1160 """,
1162 'two.out': """\
1163 //[[[cog
1164 cog.outl("goodbye cruel world")
1165 //]]]
1166 goodbye cruel world
1167 //[[[end]]]
1168 """,
1170 'cogfiles.txt': """\
1171 # Please run cog
1172 one.cog
1174 two.cog
1175 """
1176 }
1178 makeFiles(d)
1179 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1180 self.assertFilesSame('one.cog', 'one.out')
1181 self.assertFilesSame('two.cog', 'two.out')
1182 output = self.output.getvalue()
1183 self.assertIn("(changed)", output)
1185 def testNestedAtFile(self):
1186 d = {
1187 'one.cog': """\
1188 //[[[cog
1189 cog.outl("hello world")
1190 //]]]
1191 //[[[end]]]
1192 """,
1194 'one.out': """\
1195 //[[[cog
1196 cog.outl("hello world")
1197 //]]]
1198 hello world
1199 //[[[end]]]
1200 """,
1202 'two.cog': """\
1203 //[[[cog
1204 cog.outl("goodbye cruel world")
1205 //]]]
1206 //[[[end]]]
1207 """,
1209 'two.out': """\
1210 //[[[cog
1211 cog.outl("goodbye cruel world")
1212 //]]]
1213 goodbye cruel world
1214 //[[[end]]]
1215 """,
1217 'cogfiles.txt': """\
1218 # Please run cog
1219 one.cog
1220 @cogfiles2.txt
1221 """,
1223 'cogfiles2.txt': """\
1224 # This one too, please.
1225 two.cog
1226 """,
1227 }
1229 makeFiles(d)
1230 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1231 self.assertFilesSame('one.cog', 'one.out')
1232 self.assertFilesSame('two.cog', 'two.out')
1233 output = self.output.getvalue()
1234 self.assertIn("(changed)", output)
1236 def testAtFileWithArgs(self):
1237 d = {
1238 'both.cog': """\
1239 //[[[cog
1240 cog.outl("one: %s" % ('one' in globals()))
1241 cog.outl("two: %s" % ('two' in globals()))
1242 //]]]
1243 //[[[end]]]
1244 """,
1246 'one.out': """\
1247 //[[[cog
1248 cog.outl("one: %s" % ('one' in globals()))
1249 cog.outl("two: %s" % ('two' in globals()))
1250 //]]]
1251 one: True // ONE
1252 two: False // ONE
1253 //[[[end]]]
1254 """,
1256 'two.out': """\
1257 //[[[cog
1258 cog.outl("one: %s" % ('one' in globals()))
1259 cog.outl("two: %s" % ('two' in globals()))
1260 //]]]
1261 one: False // TWO
1262 two: True // TWO
1263 //[[[end]]]
1264 """,
1266 'cogfiles.txt': """\
1267 # Please run cog
1268 both.cog -o in/a/dir/both.one -s ' // ONE' -D one=x
1269 both.cog -o in/a/dir/both.two -s ' // TWO' -D two=x
1270 """
1271 }
1273 makeFiles(d)
1274 self.cog.callableMain(['argv0', '@cogfiles.txt'])
1275 self.assertFilesSame('in/a/dir/both.one', 'one.out')
1276 self.assertFilesSame('in/a/dir/both.two', 'two.out')
1278 def testAtFileWithBadArgCombo(self):
1279 d = {
1280 'both.cog': """\
1281 //[[[cog
1282 cog.outl("one: %s" % ('one' in globals()))
1283 cog.outl("two: %s" % ('two' in globals()))
1284 //]]]
1285 //[[[end]]]
1286 """,
1288 'cogfiles.txt': """\
1289 # Please run cog
1290 both.cog
1291 both.cog -d # This is bad: -r and -d
1292 """
1293 }
1295 makeFiles(d)
1296 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"):
1297 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1299 def testAtFileWithTrickyFilenames(self):
1300 def fix_backslashes(files_txt):
1301 """Make the contents of a files.txt sensitive to the platform."""
1302 if sys.platform != "win32":
1303 files_txt = files_txt.replace("\\", "/")
1304 return files_txt
1306 d = {
1307 'one 1.cog': """\
1308 //[[[cog cog.outl("hello world") ]]]
1309 """,
1311 'one.out': """\
1312 //[[[cog cog.outl("hello world") ]]]
1313 hello world //xxx
1314 """,
1316 'subdir': {
1317 'subback.cog': """\
1318 //[[[cog cog.outl("down deep with backslashes") ]]]
1319 """,
1321 'subfwd.cog': """\
1322 //[[[cog cog.outl("down deep with slashes") ]]]
1323 """,
1324 },
1326 'subback.out': """\
1327 //[[[cog cog.outl("down deep with backslashes") ]]]
1328 down deep with backslashes //yyy
1329 """,
1331 'subfwd.out': """\
1332 //[[[cog cog.outl("down deep with slashes") ]]]
1333 down deep with slashes //zzz
1334 """,
1336 'cogfiles.txt': fix_backslashes("""\
1337 # Please run cog
1338 'one 1.cog' -s ' //xxx'
1339 subdir\\subback.cog -s ' //yyy'
1340 subdir/subfwd.cog -s ' //zzz'
1341 """)
1342 }
1344 makeFiles(d)
1345 self.cog.callableMain(['argv0', '-z', '-r', '@cogfiles.txt'])
1346 self.assertFilesSame('one 1.cog', 'one.out')
1347 self.assertFilesSame('subdir/subback.cog', 'subback.out')
1348 self.assertFilesSame('subdir/subfwd.cog', 'subfwd.out')
1350 def run_with_verbosity(self, verbosity):
1351 d = {
1352 'unchanged.cog': """\
1353 //[[[cog
1354 cog.outl("hello world")
1355 //]]]
1356 hello world
1357 //[[[end]]]
1358 """,
1360 'changed.cog': """\
1361 //[[[cog
1362 cog.outl("goodbye cruel world")
1363 //]]]
1364 //[[[end]]]
1365 """,
1367 'cogfiles.txt': """\
1368 unchanged.cog
1369 changed.cog
1370 """
1371 }
1373 makeFiles(d)
1374 self.cog.callableMain(['argv0', '-r', '--verbosity='+verbosity, '@cogfiles.txt'])
1375 output = self.output.getvalue()
1376 return output
1378 def test_verbosity0(self):
1379 output = self.run_with_verbosity("0")
1380 self.assertEqual(output, "")
1382 def test_verbosity1(self):
1383 output = self.run_with_verbosity("1")
1384 self.assertEqual(output, "Cogging changed.cog (changed)\n")
1386 def test_verbosity2(self):
1387 output = self.run_with_verbosity("2")
1388 self.assertEqual(output, "Cogging unchanged.cog\nCogging changed.cog (changed)\n")
1391class CogTestLineEndings(TestCaseWithTempDir):
1392 """Tests for -U option (force LF line-endings in output)."""
1394 lines_in = ['Some text.',
1395 '//[[[cog',
1396 'cog.outl("Cog text")',
1397 '//]]]',
1398 'gobbledegook.',
1399 '//[[[end]]]',
1400 'epilogue.',
1401 '']
1403 lines_out = ['Some text.',
1404 '//[[[cog',
1405 'cog.outl("Cog text")',
1406 '//]]]',
1407 'Cog text',
1408 '//[[[end]]]',
1409 'epilogue.',
1410 '']
1412 def testOutputNativeEol(self):
1413 makeFiles({'infile': '\n'.join(self.lines_in)})
1414 self.cog.callableMain(['argv0', '-o', 'outfile', 'infile'])
1415 self.assertFileContent('outfile', os.linesep.join(self.lines_out))
1417 def testOutputLfEol(self):
1418 makeFiles({'infile': '\n'.join(self.lines_in)})
1419 self.cog.callableMain(['argv0', '-U', '-o', 'outfile', 'infile'])
1420 self.assertFileContent('outfile', '\n'.join(self.lines_out))
1422 def testReplaceNativeEol(self):
1423 makeFiles({'test.cog': '\n'.join(self.lines_in)})
1424 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1425 self.assertFileContent('test.cog', os.linesep.join(self.lines_out))
1427 def testReplaceLfEol(self):
1428 makeFiles({'test.cog': '\n'.join(self.lines_in)})
1429 self.cog.callableMain(['argv0', '-U', '-r', 'test.cog'])
1430 self.assertFileContent('test.cog', '\n'.join(self.lines_out))
1433class CogTestCharacterEncoding(TestCaseWithTempDir):
1435 def testSimple(self):
1436 d = {
1437 'test.cog': b"""\
1438 // This is my C++ file.
1439 //[[[cog
1440 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
1441 //]]]
1442 //[[[end]]]
1443 """,
1445 'test.out': b"""\
1446 // This is my C++ file.
1447 //[[[cog
1448 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
1449 //]]]
1450 // Unicode: \xe1\x88\xb4 (U+1234)
1451 //[[[end]]]
1452 """.replace(b"\n", os.linesep.encode()),
1453 }
1455 makeFiles(d)
1456 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1457 self.assertFilesSame('test.cog', 'test.out')
1458 output = self.output.getvalue()
1459 self.assertIn("(changed)", output)
1461 def testFileEncodingOption(self):
1462 d = {
1463 'test.cog': b"""\
1464 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
1465 //[[[cog
1466 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")
1467 //]]]
1468 //[[[end]]]
1469 """,
1471 'test.out': b"""\
1472 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
1473 //[[[cog
1474 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")
1475 //]]]
1476 \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
1477 //[[[end]]]
1478 """.replace(b"\n", os.linesep.encode()),
1479 }
1481 makeFiles(d)
1482 self.cog.callableMain(['argv0', '-n', 'cp1251', '-r', 'test.cog'])
1483 self.assertFilesSame('test.cog', 'test.out')
1484 output = self.output.getvalue()
1485 self.assertIn("(changed)", output)
1488class TestCaseWithImports(TestCaseWithTempDir):
1489 """ When running tests which import modules, the sys.modules list
1490 leaks from one test to the next. This test case class scrubs
1491 the list after each run to keep the tests isolated from each other.
1492 """
1494 def setUp(self):
1495 super().setUp()
1496 self.sysmodulekeys = list(sys.modules)
1498 def tearDown(self):
1499 modstoscrub = [
1500 modname
1501 for modname in sys.modules
1502 if modname not in self.sysmodulekeys
1503 ]
1504 for modname in modstoscrub:
1505 del sys.modules[modname]
1506 super().tearDown()
1509class CogIncludeTests(TestCaseWithImports):
1510 dincludes = {
1511 'test.cog': """\
1512 //[[[cog
1513 import mymodule
1514 //]]]
1515 //[[[end]]]
1516 """,
1518 'test.out': """\
1519 //[[[cog
1520 import mymodule
1521 //]]]
1522 Hello from mymodule
1523 //[[[end]]]
1524 """,
1526 'test2.out': """\
1527 //[[[cog
1528 import mymodule
1529 //]]]
1530 Hello from mymodule in inc2
1531 //[[[end]]]
1532 """,
1534 'include': {
1535 'mymodule.py': """\
1536 import cog
1537 cog.outl("Hello from mymodule")
1538 """
1539 },
1541 'inc2': {
1542 'mymodule.py': """\
1543 import cog
1544 cog.outl("Hello from mymodule in inc2")
1545 """
1546 },
1548 'inc3': {
1549 'someothermodule.py': """\
1550 import cog
1551 cog.outl("This is some other module.")
1552 """
1553 },
1554 }
1556 def testNeedIncludePath(self):
1557 # Try it without the -I, to see that an ImportError happens.
1558 makeFiles(self.dincludes)
1559 msg = "(ImportError|ModuleNotFoundError): No module named '?mymodule'?"
1560 with self.assertRaisesRegex(CogUserException, msg):
1561 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1563 def testIncludePath(self):
1564 # Test that -I adds include directories properly.
1565 makeFiles(self.dincludes)
1566 self.cog.callableMain(['argv0', '-r', '-I', 'include', 'test.cog'])
1567 self.assertFilesSame('test.cog', 'test.out')
1569 def testTwoIncludePaths(self):
1570 # Test that two -I's add include directories properly.
1571 makeFiles(self.dincludes)
1572 self.cog.callableMain(['argv0', '-r', '-I', 'include', '-I', 'inc2', 'test.cog'])
1573 self.assertFilesSame('test.cog', 'test.out')
1575 def testTwoIncludePaths2(self):
1576 # Test that two -I's add include directories properly.
1577 makeFiles(self.dincludes)
1578 self.cog.callableMain(['argv0', '-r', '-I', 'inc2', '-I', 'include', 'test.cog'])
1579 self.assertFilesSame('test.cog', 'test2.out')
1581 def testUselessIncludePath(self):
1582 # Test that the search will continue past the first directory.
1583 makeFiles(self.dincludes)
1584 self.cog.callableMain(['argv0', '-r', '-I', 'inc3', '-I', 'include', 'test.cog'])
1585 self.assertFilesSame('test.cog', 'test.out')
1587 def testSysPathIsUnchanged(self):
1588 d = {
1589 'bad.cog': """\
1590 //[[[cog cog.error("Oh no!") ]]]
1591 //[[[end]]]
1592 """,
1593 'good.cog': """\
1594 //[[[cog cog.outl("Oh yes!") ]]]
1595 //[[[end]]]
1596 """,
1597 }
1599 makeFiles(d)
1600 # Is it unchanged just by creating a cog engine?
1601 oldsyspath = sys.path[:]
1602 self.newCog()
1603 self.assertEqual(oldsyspath, sys.path)
1604 # Is it unchanged for a successful run?
1605 self.newCog()
1606 self.cog.callableMain(['argv0', '-r', 'good.cog'])
1607 self.assertEqual(oldsyspath, sys.path)
1608 # Is it unchanged for a successful run with includes?
1609 self.newCog()
1610 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'good.cog'])
1611 self.assertEqual(oldsyspath, sys.path)
1612 # Is it unchanged for a successful run with two includes?
1613 self.newCog()
1614 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'good.cog'])
1615 self.assertEqual(oldsyspath, sys.path)
1616 # Is it unchanged for a failed run?
1617 self.newCog()
1618 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1619 self.cog.callableMain(['argv0', '-r', 'bad.cog'])
1620 self.assertEqual(oldsyspath, sys.path)
1621 # Is it unchanged for a failed run with includes?
1622 self.newCog()
1623 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1624 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'bad.cog'])
1625 self.assertEqual(oldsyspath, sys.path)
1626 # Is it unchanged for a failed run with two includes?
1627 self.newCog()
1628 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1629 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'bad.cog'])
1630 self.assertEqual(oldsyspath, sys.path)
1632 def testSubDirectories(self):
1633 # Test that relative paths on the command line work, with includes.
1635 d = {
1636 'code': {
1637 'test.cog': """\
1638 //[[[cog
1639 import mysubmodule
1640 //]]]
1641 //[[[end]]]
1642 """,
1644 'test.out': """\
1645 //[[[cog
1646 import mysubmodule
1647 //]]]
1648 Hello from mysubmodule
1649 //[[[end]]]
1650 """,
1652 'mysubmodule.py': """\
1653 import cog
1654 cog.outl("Hello from mysubmodule")
1655 """
1656 }
1657 }
1659 makeFiles(d)
1660 # We should be able to invoke cog without the -I switch, and it will
1661 # auto-include the current directory
1662 self.cog.callableMain(['argv0', '-r', 'code/test.cog'])
1663 self.assertFilesSame('code/test.cog', 'code/test.out')
1666class CogTestsInFiles(TestCaseWithTempDir):
1668 def testWarnIfNoCogCode(self):
1669 # Test that the -e switch warns if there is no Cog code.
1670 d = {
1671 'with.cog': """\
1672 //[[[cog
1673 cog.outl("hello world")
1674 //]]]
1675 hello world
1676 //[[[end]]]
1677 """,
1679 'without.cog': """\
1680 There's no cog
1681 code in this file.
1682 """,
1683 }
1685 makeFiles(d)
1686 self.cog.callableMain(['argv0', '-e', 'with.cog'])
1687 output = self.output.getvalue()
1688 self.assertNotIn("Warning", output)
1689 self.newCog()
1690 self.cog.callableMain(['argv0', '-e', 'without.cog'])
1691 output = self.output.getvalue()
1692 self.assertIn("Warning: no cog code found in without.cog", output)
1693 self.newCog()
1694 self.cog.callableMain(['argv0', 'without.cog'])
1695 output = self.output.getvalue()
1696 self.assertNotIn("Warning", output)
1698 def testFileNameProps(self):
1699 d = {
1700 'cog1.txt': """\
1701 //[[[cog
1702 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1703 //]]]
1704 this is cog1.txt in, cog1.txt out
1705 [[[end]]]
1706 """,
1708 'cog1.out': """\
1709 //[[[cog
1710 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1711 //]]]
1712 This is cog1.txt in, cog1.txt out
1713 [[[end]]]
1714 """,
1716 'cog1out.out': """\
1717 //[[[cog
1718 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1719 //]]]
1720 This is cog1.txt in, cog1out.txt out
1721 [[[end]]]
1722 """,
1723 }
1725 makeFiles(d)
1726 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
1727 self.assertFilesSame('cog1.txt', 'cog1.out')
1728 self.newCog()
1729 self.cog.callableMain(['argv0', '-o', 'cog1out.txt', 'cog1.txt'])
1730 self.assertFilesSame('cog1out.txt', 'cog1out.out')
1732 def testGlobalsDontCrossFiles(self):
1733 # Make sure that global values don't get shared between files.
1734 d = {
1735 'one.cog': """\
1736 //[[[cog s = "This was set in one.cog" ]]]
1737 //[[[end]]]
1738 //[[[cog cog.outl(s) ]]]
1739 //[[[end]]]
1740 """,
1742 'one.out': """\
1743 //[[[cog s = "This was set in one.cog" ]]]
1744 //[[[end]]]
1745 //[[[cog cog.outl(s) ]]]
1746 This was set in one.cog
1747 //[[[end]]]
1748 """,
1750 'two.cog': """\
1751 //[[[cog
1752 try:
1753 cog.outl(s)
1754 except NameError:
1755 cog.outl("s isn't set!")
1756 //]]]
1757 //[[[end]]]
1758 """,
1760 'two.out': """\
1761 //[[[cog
1762 try:
1763 cog.outl(s)
1764 except NameError:
1765 cog.outl("s isn't set!")
1766 //]]]
1767 s isn't set!
1768 //[[[end]]]
1769 """,
1771 'cogfiles.txt': """\
1772 # Please run cog
1773 one.cog
1775 two.cog
1776 """
1777 }
1779 makeFiles(d)
1780 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1781 self.assertFilesSame('one.cog', 'one.out')
1782 self.assertFilesSame('two.cog', 'two.out')
1783 output = self.output.getvalue()
1784 self.assertIn("(changed)", output)
1786 def testRemoveGeneratedOutput(self):
1787 d = {
1788 'cog1.txt': """\
1789 //[[[cog
1790 cog.outl("This line was generated.")
1791 //]]]
1792 This line was generated.
1793 //[[[end]]]
1794 This line was not.
1795 """,
1797 'cog1.out': """\
1798 //[[[cog
1799 cog.outl("This line was generated.")
1800 //]]]
1801 //[[[end]]]
1802 This line was not.
1803 """,
1805 'cog1.out2': """\
1806 //[[[cog
1807 cog.outl("This line was generated.")
1808 //]]]
1809 This line was generated.
1810 //[[[end]]]
1811 This line was not.
1812 """,
1813 }
1815 makeFiles(d)
1816 # Remove generated output.
1817 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt'])
1818 self.assertFilesSame('cog1.txt', 'cog1.out')
1819 self.newCog()
1820 # Regenerate the generated output.
1821 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
1822 self.assertFilesSame('cog1.txt', 'cog1.out2')
1823 self.newCog()
1824 # Remove the generated output again.
1825 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt'])
1826 self.assertFilesSame('cog1.txt', 'cog1.out')
1828 def testMsgCall(self):
1829 infile = """\
1830 #[[[cog
1831 cog.msg("Hello there!")
1832 #]]]
1833 #[[[end]]]
1834 """
1835 infile = reindentBlock(infile)
1836 self.assertEqual(self.cog.processString(infile), infile)
1837 output = self.output.getvalue()
1838 self.assertEqual(output, "Message: Hello there!\n")
1840 def testErrorMessageHasNoTraceback(self):
1841 # Test that a Cog error is printed to stderr with no traceback.
1843 d = {
1844 'cog1.txt': """\
1845 //[[[cog
1846 cog.outl("This line was newly")
1847 cog.outl("generated by cog")
1848 cog.outl("blah blah.")
1849 //]]]
1850 Xhis line was newly
1851 generated by cog
1852 blah blah.
1853 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
1854 """,
1855 }
1857 makeFiles(d)
1858 stderr = io.StringIO()
1859 self.cog.setOutput(stderr=stderr)
1860 self.cog.main(['argv0', '-c', '-r', "cog1.txt"])
1861 self.assertEqual(self.output.getvalue(), "Cogging cog1.txt\n")
1862 self.assertEqual(stderr.getvalue(), "cog1.txt(9): Output has been edited! Delete old checksum to unprotect.\n")
1864 def testDashD(self):
1865 d = {
1866 'test.cog': """\
1867 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1868 --[[[end]]]
1869 """,
1871 'test.kablooey': """\
1872 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1873 Defined fooey as kablooey
1874 --[[[end]]]
1875 """,
1877 'test.einstein': """\
1878 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1879 Defined fooey as e=mc2
1880 --[[[end]]]
1881 """,
1882 }
1884 makeFiles(d)
1885 self.cog.callableMain(['argv0', '-r', '-D', 'fooey=kablooey', 'test.cog'])
1886 self.assertFilesSame('test.cog', 'test.kablooey')
1887 makeFiles(d)
1888 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', 'test.cog'])
1889 self.assertFilesSame('test.cog', 'test.kablooey')
1890 makeFiles(d)
1891 self.cog.callableMain(['argv0', '-r', '-Dfooey=e=mc2', 'test.cog'])
1892 self.assertFilesSame('test.cog', 'test.einstein')
1893 makeFiles(d)
1894 self.cog.callableMain(['argv0', '-r', '-Dbar=quux', '-Dfooey=kablooey', 'test.cog'])
1895 self.assertFilesSame('test.cog', 'test.kablooey')
1896 makeFiles(d)
1897 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', '-Dbar=quux', 'test.cog'])
1898 self.assertFilesSame('test.cog', 'test.kablooey')
1899 makeFiles(d)
1900 self.cog.callableMain(['argv0', '-r', '-Dfooey=gooey', '-Dfooey=kablooey', 'test.cog'])
1901 self.assertFilesSame('test.cog', 'test.kablooey')
1903 def testOutputToStdout(self):
1904 d = {
1905 'test.cog': """\
1906 --[[[cog cog.outl('Hey there!') ]]]
1907 --[[[end]]]
1908 """
1909 }
1911 makeFiles(d)
1912 stderr = io.StringIO()
1913 self.cog.setOutput(stderr=stderr)
1914 self.cog.callableMain(['argv0', 'test.cog'])
1915 output = self.output.getvalue()
1916 outerr = stderr.getvalue()
1917 self.assertEqual(output, "--[[[cog cog.outl('Hey there!') ]]]\nHey there!\n--[[[end]]]\n")
1918 self.assertEqual(outerr, "")
1920 def testReadFromStdin(self):
1921 stdin = io.StringIO("--[[[cog cog.outl('Wow') ]]]\n--[[[end]]]\n")
1922 def restore_stdin(old_stdin):
1923 sys.stdin = old_stdin
1924 self.addCleanup(restore_stdin, sys.stdin)
1925 sys.stdin = stdin
1927 stderr = io.StringIO()
1928 self.cog.setOutput(stderr=stderr)
1929 self.cog.callableMain(['argv0', '-'])
1930 output = self.output.getvalue()
1931 outerr = stderr.getvalue()
1932 self.assertEqual(output, "--[[[cog cog.outl('Wow') ]]]\nWow\n--[[[end]]]\n")
1933 self.assertEqual(outerr, "")
1935 def testSuffixOutputLines(self):
1936 d = {
1937 'test.cog': """\
1938 Hey there.
1939 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
1940 ;[[[end]]]
1941 Good bye.
1942 """,
1944 'test.out': """\
1945 Hey there.
1946 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
1947 a (foo)
1948 b (foo)
1949 """ # These three trailing spaces are important.
1950 # The suffix is not applied to completely blank lines.
1951 """
1952 c (foo)
1953 ;[[[end]]]
1954 Good bye.
1955 """,
1956 }
1958 makeFiles(d)
1959 self.cog.callableMain(['argv0', '-r', '-s', ' (foo)', 'test.cog'])
1960 self.assertFilesSame('test.cog', 'test.out')
1962 def testEmptySuffix(self):
1963 d = {
1964 'test.cog': """\
1965 ;[[[cog cog.outl('a\\nb\\nc') ]]]
1966 ;[[[end]]]
1967 """,
1969 'test.out': """\
1970 ;[[[cog cog.outl('a\\nb\\nc') ]]]
1971 a
1972 b
1973 c
1974 ;[[[end]]]
1975 """,
1976 }
1978 makeFiles(d)
1979 self.cog.callableMain(['argv0', '-r', '-s', '', 'test.cog'])
1980 self.assertFilesSame('test.cog', 'test.out')
1982 def testHellishSuffix(self):
1983 d = {
1984 'test.cog': """\
1985 ;[[[cog cog.outl('a\\n\\nb') ]]]
1986 """,
1988 'test.out': """\
1989 ;[[[cog cog.outl('a\\n\\nb') ]]]
1990 a /\\n*+([)]><
1992 b /\\n*+([)]><
1993 """,
1994 }
1996 makeFiles(d)
1997 self.cog.callableMain(['argv0', '-z', '-r', '-s', r' /\n*+([)]><', 'test.cog'])
1998 self.assertFilesSame('test.cog', 'test.out')
2000 def testPrologue(self):
2001 d = {
2002 'test.cog': """\
2003 Some text.
2004 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
2005 //[[[end]]]
2006 epilogue.
2007 """,
2009 'test.out': """\
2010 Some text.
2011 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
2012 1.4142135623
2013 //[[[end]]]
2014 epilogue.
2015 """,
2016 }
2018 makeFiles(d)
2019 self.cog.callableMain(['argv0', '-r', '-p', 'import math', 'test.cog'])
2020 self.assertFilesSame('test.cog', 'test.out')
2022 def testThreads(self):
2023 # Test that the implicitly imported cog module is actually different for
2024 # different threads.
2025 numthreads = 20
2027 d = {}
2028 for i in range(numthreads):
2029 d[f'f{i}.cog'] = (
2030 "x\n" * i +
2031 "[[[cog\n" +
2032 f"assert cog.firstLineNum == int(FIRST) == {i+1}\n" +
2033 "]]]\n" +
2034 "[[[end]]]\n"
2035 )
2036 makeFiles(d)
2038 results = []
2040 def thread_main(num):
2041 try:
2042 ret = Cog().main(
2043 ['cog.py', '-r', '-D', f'FIRST={num+1}', f'f{num}.cog']
2044 )
2045 assert ret == 0
2046 except Exception as exc: # pragma: no cover (only happens on test failure)
2047 results.append(exc)
2048 else:
2049 results.append(None)
2051 ts = [threading.Thread(target=thread_main, args=(i,)) for i in range(numthreads)]
2052 for t in ts:
2053 t.start()
2054 for t in ts:
2055 t.join()
2056 assert results == [None] * numthreads
2059class CheckTests(TestCaseWithTempDir):
2060 def run_check(self, args, status=0):
2061 actual_status = self.cog.main(['argv0', '--check'] + args)
2062 print(self.output.getvalue())
2063 self.assertEqual(status, actual_status)
2065 def assert_made_files_unchanged(self, d):
2066 for name, content in d.items():
2067 content = reindentBlock(content)
2068 if os.name == 'nt':
2069 content = content.replace("\n", "\r\n")
2070 self.assertFileContent(name, content)
2072 def test_check_no_cog(self):
2073 d = {
2074 'hello.txt': """\
2075 Hello.
2076 """,
2077 }
2078 makeFiles(d)
2079 self.run_check(['hello.txt'], status=0)
2080 self.assertEqual(self.output.getvalue(), "Checking hello.txt\n")
2081 self.assert_made_files_unchanged(d)
2083 def test_check_good(self):
2084 d = {
2085 'unchanged.cog': """\
2086 //[[[cog
2087 cog.outl("hello world")
2088 //]]]
2089 hello world
2090 //[[[end]]]
2091 """,
2092 }
2093 makeFiles(d)
2094 self.run_check(['unchanged.cog'], status=0)
2095 self.assertEqual(self.output.getvalue(), "Checking unchanged.cog\n")
2096 self.assert_made_files_unchanged(d)
2098 def test_check_bad(self):
2099 d = {
2100 'changed.cog': """\
2101 //[[[cog
2102 cog.outl("goodbye world")
2103 //]]]
2104 hello world
2105 //[[[end]]]
2106 """,
2107 }
2108 makeFiles(d)
2109 self.run_check(['changed.cog'], status=5)
2110 self.assertEqual(self.output.getvalue(), "Checking changed.cog (changed)\nCheck failed\n")
2111 self.assert_made_files_unchanged(d)
2113 def test_check_mixed(self):
2114 d = {
2115 'unchanged.cog': """\
2116 //[[[cog
2117 cog.outl("hello world")
2118 //]]]
2119 hello world
2120 //[[[end]]]
2121 """,
2122 'changed.cog': """\
2123 //[[[cog
2124 cog.outl("goodbye world")
2125 //]]]
2126 hello world
2127 //[[[end]]]
2128 """,
2129 }
2130 makeFiles(d)
2131 for verbosity, output in [
2132 ("0", "Check failed\n"),
2133 ("1", "Checking changed.cog (changed)\nCheck failed\n"),
2134 ("2", "Checking unchanged.cog\nChecking changed.cog (changed)\nCheck failed\n"),
2135 ]:
2136 self.newCog()
2137 self.run_check(['--verbosity=%s' % verbosity, 'unchanged.cog', 'changed.cog'], status=5)
2138 self.assertEqual(self.output.getvalue(), output)
2139 self.assert_made_files_unchanged(d)
2141 def test_check_with_good_checksum(self):
2142 d = {
2143 'good.txt': """\
2144 //[[[cog
2145 cog.outl("This line was newly")
2146 cog.outl("generated by cog")
2147 cog.outl("blah blah.")
2148 //]]]
2149 This line was newly
2150 generated by cog
2151 blah blah.
2152 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2153 """,
2154 }
2155 makeFiles(d)
2156 # Have to use -c with --check if there are checksums in the file.
2157 self.run_check(['-c', 'good.txt'], status=0)
2158 self.assertEqual(self.output.getvalue(), "Checking good.txt\n")
2159 self.assert_made_files_unchanged(d)
2161 def test_check_with_bad_checksum(self):
2162 d = {
2163 'bad.txt': """\
2164 //[[[cog
2165 cog.outl("This line was newly")
2166 cog.outl("generated by cog")
2167 cog.outl("blah blah.")
2168 //]]]
2169 This line was newly
2170 generated by cog
2171 blah blah.
2172 //[[[end]]] (checksum: a9999999e5ad6b95c9e9a184b26f4346)
2173 """,
2174 }
2175 makeFiles(d)
2176 # Have to use -c with --check if there are checksums in the file.
2177 self.run_check(['-c', 'bad.txt'], status=1)
2178 self.assertEqual(self.output.getvalue(), "Checking bad.txt\nbad.txt(9): Output has been edited! Delete old checksum to unprotect.\n")
2179 self.assert_made_files_unchanged(d)
2182class WritabilityTests(TestCaseWithTempDir):
2184 d = {
2185 'test.cog': """\
2186 //[[[cog
2187 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
2188 cog.outl("void %s();" % fn)
2189 //]]]
2190 //[[[end]]]
2191 """,
2193 'test.out': """\
2194 //[[[cog
2195 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
2196 cog.outl("void %s();" % fn)
2197 //]]]
2198 void DoSomething();
2199 void DoAnotherThing();
2200 void DoLastThing();
2201 //[[[end]]]
2202 """,
2203 }
2205 if os.name == 'nt': 2205 ↛ 2207line 2205 didn't jump to line 2207, because the condition on line 2205 was never true
2206 # for Windows
2207 cmd_w_args = 'attrib -R %s'
2208 cmd_w_asterisk = 'attrib -R *'
2209 else:
2210 # for unix-like
2211 cmd_w_args = 'chmod +w %s'
2212 cmd_w_asterisk = 'chmod +w *'
2214 def setUp(self):
2215 super().setUp()
2216 makeFiles(self.d)
2217 self.testcog = os.path.join(self.tempdir, 'test.cog')
2218 os.chmod(self.testcog, stat.S_IREAD) # Make the file readonly.
2219 assert not os.access(self.testcog, os.W_OK)
2221 def tearDown(self):
2222 os.chmod(self.testcog, stat.S_IWRITE) # Make the file writable again.
2223 super().tearDown()
2225 def testReadonlyNoCommand(self):
2226 with self.assertRaisesRegex(CogError, "^Can't overwrite test.cog$"):
2227 self.cog.callableMain(['argv0', '-r', 'test.cog'])
2228 assert not os.access(self.testcog, os.W_OK)
2230 def testReadonlyWithCommand(self):
2231 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_args, 'test.cog'])
2232 self.assertFilesSame('test.cog', 'test.out')
2233 assert os.access(self.testcog, os.W_OK)
2235 def testReadonlyWithCommandWithNoSlot(self):
2236 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_asterisk, 'test.cog'])
2237 self.assertFilesSame('test.cog', 'test.out')
2238 assert os.access(self.testcog, os.W_OK)
2240 def testReadonlyWithIneffectualCommand(self):
2241 with self.assertRaisesRegex(CogError, "^Couldn't make test.cog writable$"):
2242 self.cog.callableMain(['argv0', '-r', '-w', 'echo %s', 'test.cog'])
2243 assert not os.access(self.testcog, os.W_OK)
2246class ChecksumTests(TestCaseWithTempDir):
2248 def testCreateChecksumOutput(self):
2249 d = {
2250 'cog1.txt': """\
2251 //[[[cog
2252 cog.outl("This line was generated.")
2253 //]]]
2254 This line was generated.
2255 //[[[end]]]
2256 This line was not.
2257 """,
2259 'cog1.out': """\
2260 //[[[cog
2261 cog.outl("This line was generated.")
2262 //]]]
2263 This line was generated.
2264 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
2265 This line was not.
2266 """,
2267 }
2269 makeFiles(d)
2270 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt'])
2271 self.assertFilesSame('cog1.txt', 'cog1.out')
2273 def testCheckChecksumOutput(self):
2274 d = {
2275 'cog1.txt': """\
2276 //[[[cog
2277 cog.outl("This line was newly")
2278 cog.outl("generated by cog")
2279 cog.outl("blah blah.")
2280 //]]]
2281 This line was generated.
2282 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
2283 """,
2285 'cog1.out': """\
2286 //[[[cog
2287 cog.outl("This line was newly")
2288 cog.outl("generated by cog")
2289 cog.outl("blah blah.")
2290 //]]]
2291 This line was newly
2292 generated by cog
2293 blah blah.
2294 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2295 """,
2296 }
2298 makeFiles(d)
2299 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt'])
2300 self.assertFilesSame('cog1.txt', 'cog1.out')
2302 def testRemoveChecksumOutput(self):
2303 d = {
2304 'cog1.txt': """\
2305 //[[[cog
2306 cog.outl("This line was newly")
2307 cog.outl("generated by cog")
2308 cog.outl("blah blah.")
2309 //]]]
2310 This line was generated.
2311 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) fooey
2312 """,
2314 'cog1.out': """\
2315 //[[[cog
2316 cog.outl("This line was newly")
2317 cog.outl("generated by cog")
2318 cog.outl("blah blah.")
2319 //]]]
2320 This line was newly
2321 generated by cog
2322 blah blah.
2323 //[[[end]]] fooey
2324 """,
2325 }
2327 makeFiles(d)
2328 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
2329 self.assertFilesSame('cog1.txt', 'cog1.out')
2331 def testTamperedChecksumOutput(self):
2332 d = {
2333 'cog1.txt': """\
2334 //[[[cog
2335 cog.outl("This line was newly")
2336 cog.outl("generated by cog")
2337 cog.outl("blah blah.")
2338 //]]]
2339 Xhis line was newly
2340 generated by cog
2341 blah blah.
2342 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2343 """,
2345 'cog2.txt': """\
2346 //[[[cog
2347 cog.outl("This line was newly")
2348 cog.outl("generated by cog")
2349 cog.outl("blah blah.")
2350 //]]]
2351 This line was newly
2352 generated by cog
2353 blah blah!
2354 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2355 """,
2357 'cog3.txt': """\
2358 //[[[cog
2359 cog.outl("This line was newly")
2360 cog.outl("generated by cog")
2361 cog.outl("blah blah.")
2362 //]]]
2364 This line was newly
2365 generated by cog
2366 blah blah.
2367 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2368 """,
2370 'cog4.txt': """\
2371 //[[[cog
2372 cog.outl("This line was newly")
2373 cog.outl("generated by cog")
2374 cog.outl("blah blah.")
2375 //]]]
2376 This line was newly
2377 generated by cog
2378 blah blah..
2379 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2380 """,
2382 'cog5.txt': """\
2383 //[[[cog
2384 cog.outl("This line was newly")
2385 cog.outl("generated by cog")
2386 cog.outl("blah blah.")
2387 //]]]
2388 This line was newly
2389 generated by cog
2390 blah blah.
2391 extra
2392 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2393 """,
2395 'cog6.txt': """\
2396 //[[[cog
2397 cog.outl("This line was newly")
2398 cog.outl("generated by cog")
2399 cog.outl("blah blah.")
2400 //]]]
2401 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2402 """,
2403 }
2405 makeFiles(d)
2406 with self.assertRaisesRegex(CogError,
2407 r"^cog1.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
2408 self.cog.callableMain(['argv0', '-c', "cog1.txt"])
2409 with self.assertRaisesRegex(CogError,
2410 r"^cog2.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
2411 self.cog.callableMain(['argv0', '-c', "cog2.txt"])
2412 with self.assertRaisesRegex(CogError,
2413 r"^cog3.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"):
2414 self.cog.callableMain(['argv0', '-c', "cog3.txt"])
2415 with self.assertRaisesRegex(CogError,
2416 r"^cog4.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
2417 self.cog.callableMain(['argv0', '-c', "cog4.txt"])
2418 with self.assertRaisesRegex(CogError,
2419 r"^cog5.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"):
2420 self.cog.callableMain(['argv0', '-c', "cog5.txt"])
2421 with self.assertRaisesRegex(CogError,
2422 r"^cog6.txt\(6\): Output has been edited! Delete old checksum to unprotect.$"):
2423 self.cog.callableMain(['argv0', '-c', "cog6.txt"])
2425 def testArgvIsntModified(self):
2426 argv = ['argv0', '-v']
2427 orig_argv = argv[:]
2428 self.cog.callableMain(argv)
2429 self.assertEqual(argv, orig_argv)
2432class CustomMarkerTests(TestCaseWithTempDir):
2434 def testCustomerMarkers(self):
2435 d = {
2436 'test.cog': """\
2437 //{{
2438 cog.outl("void %s();" % "MyFunction")
2439 //}}
2440 //{{end}}
2441 """,
2443 'test.out': """\
2444 //{{
2445 cog.outl("void %s();" % "MyFunction")
2446 //}}
2447 void MyFunction();
2448 //{{end}}
2449 """,
2450 }
2452 makeFiles(d)
2453 self.cog.callableMain([
2454 'argv0', '-r',
2455 '--markers={{ }} {{end}}',
2456 'test.cog'
2457 ])
2458 self.assertFilesSame('test.cog', 'test.out')
2460 def testTrulyWackyMarkers(self):
2461 # Make sure the markers are properly re-escaped.
2462 d = {
2463 'test.cog': """\
2464 //**(
2465 cog.outl("void %s();" % "MyFunction")
2466 //**)
2467 //**(end)**
2468 """,
2470 'test.out': """\
2471 //**(
2472 cog.outl("void %s();" % "MyFunction")
2473 //**)
2474 void MyFunction();
2475 //**(end)**
2476 """,
2477 }
2479 makeFiles(d)
2480 self.cog.callableMain([
2481 'argv0', '-r',
2482 '--markers=**( **) **(end)**',
2483 'test.cog'
2484 ])
2485 self.assertFilesSame('test.cog', 'test.out')
2487 def testChangeJustOneMarker(self):
2488 d = {
2489 'test.cog': """\
2490 //**(
2491 cog.outl("void %s();" % "MyFunction")
2492 //]]]
2493 //[[[end]]]
2494 """,
2496 'test.out': """\
2497 //**(
2498 cog.outl("void %s();" % "MyFunction")
2499 //]]]
2500 void MyFunction();
2501 //[[[end]]]
2502 """,
2503 }
2505 makeFiles(d)
2506 self.cog.callableMain([
2507 'argv0', '-r',
2508 '--markers=**( ]]] [[[end]]]',
2509 'test.cog'
2510 ])
2511 self.assertFilesSame('test.cog', 'test.out')
2514class BlakeTests(TestCaseWithTempDir):
2516 # Blake Winton's contributions.
2517 def testDeleteCode(self):
2518 # -o sets the output file.
2519 d = {
2520 'test.cog': """\
2521 // This is my C++ file.
2522 //[[[cog
2523 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
2524 for fn in fnames:
2525 cog.outl("void %s();" % fn)
2526 //]]]
2527 Some Sample Code Here
2528 //[[[end]]]Data Data
2529 And Some More
2530 """,
2532 'test.out': """\
2533 // This is my C++ file.
2534 void DoSomething();
2535 void DoAnotherThing();
2536 void DoLastThing();
2537 And Some More
2538 """,
2539 }
2541 makeFiles(d)
2542 self.cog.callableMain(['argv0', '-d', '-o', 'test.cogged', 'test.cog'])
2543 self.assertFilesSame('test.cogged', 'test.out')
2545 def testDeleteCodeWithDashRFails(self):
2546 d = {
2547 'test.cog': """\
2548 // This is my C++ file.
2549 """
2550 }
2552 makeFiles(d)
2553 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"):
2554 self.cog.callableMain(['argv0', '-r', '-d', 'test.cog'])
2556 def testSettingGlobals(self):
2557 # Blake Winton contributed a way to set the globals that will be used in
2558 # processFile().
2559 d = {
2560 'test.cog': """\
2561 // This is my C++ file.
2562 //[[[cog
2563 for fn in fnames:
2564 cog.outl("void %s();" % fn)
2565 //]]]
2566 Some Sample Code Here
2567 //[[[end]]]""",
2569 'test.out': """\
2570 // This is my C++ file.
2571 void DoBlake();
2572 void DoWinton();
2573 void DoContribution();
2574 """,
2575 }
2577 makeFiles(d)
2578 globals = {}
2579 globals['fnames'] = ['DoBlake', 'DoWinton', 'DoContribution']
2580 self.cog.options.bDeleteCode = True
2581 self.cog.processFile('test.cog', 'test.cogged', globals=globals)
2582 self.assertFilesSame('test.cogged', 'test.out')
2585class ErrorCallTests(TestCaseWithTempDir):
2587 def testErrorCallHasNoTraceback(self):
2588 # Test that cog.error() doesn't show a traceback.
2589 d = {
2590 'error.cog': """\
2591 //[[[cog
2592 cog.error("Something Bad!")
2593 //]]]
2594 //[[[end]]]
2595 """,
2596 }
2598 makeFiles(d)
2599 self.cog.main(['argv0', '-r', 'error.cog'])
2600 output = self.output.getvalue()
2601 self.assertEqual(output, "Cogging error.cog\nError: Something Bad!\n")
2603 def testRealErrorHasTraceback(self):
2604 # Test that a genuine error does show a traceback.
2605 d = {
2606 'error.cog': """\
2607 //[[[cog
2608 raise RuntimeError("Hey!")
2609 //]]]
2610 //[[[end]]]
2611 """,
2612 }
2614 makeFiles(d)
2615 self.cog.main(['argv0', '-r', 'error.cog'])
2616 output = self.output.getvalue()
2617 msg = 'Actual output:\n' + output
2618 self.assertTrue(output.startswith("Cogging error.cog\nTraceback (most recent"), msg)
2619 self.assertIn("RuntimeError: Hey!", output)
2622# Things not yet tested:
2623# - A bad -w command (currently fails silently).