Coverage for cogapp/test_cogapp.py: 29.94%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1""" Test cogapp.
2 http://nedbatchelder.com/code/cog
4 Copyright 2004-2019, Ned Batchelder.
5"""
7from __future__ import absolute_import
9import os
10import os.path
11import random
12import re
13import shutil
14import stat
15import sys
16import tempfile
17import threading
19from .backward import StringIO, to_bytes, TestCase, PY3
20from .cogapp import Cog, CogOptions, CogGenerator
21from .cogapp import CogError, CogUsageError, CogGeneratedError, CogUserException
22from .cogapp import usage, __version__, main
23from .makefiles import *
24from .whiteutils import reindentBlock
27class CogTestsInMemory(TestCase):
28 """ Test cases for cogapp.Cog()
29 """
31 def testNoCog(self):
32 strings = [
33 '',
34 ' ',
35 ' \t \t \tx',
36 'hello',
37 'the cat\nin the\nhat.',
38 'Horton\n\tHears A\n\t\tWho'
39 ]
40 for s in strings:
41 self.assertEqual(Cog().processString(s), s)
43 def testSimple(self):
44 infile = """\
45 Some text.
46 //[[[cog
47 import cog
48 cog.outl("This is line one\\n")
49 cog.outl("This is line two")
50 //]]]
51 gobbledegook.
52 //[[[end]]]
53 epilogue.
54 """
56 outfile = """\
57 Some text.
58 //[[[cog
59 import cog
60 cog.outl("This is line one\\n")
61 cog.outl("This is line two")
62 //]]]
63 This is line one
65 This is line two
66 //[[[end]]]
67 epilogue.
68 """
70 self.assertEqual(Cog().processString(infile), outfile)
72 def testEmptyCog(self):
73 # The cog clause can be totally empty. Not sure why you'd want it,
74 # but it works.
75 infile = """\
76 hello
77 //[[[cog
78 //]]]
79 //[[[end]]]
80 goodbye
81 """
83 infile = reindentBlock(infile)
84 self.assertEqual(Cog().processString(infile), infile)
86 def testMultipleCogs(self):
87 # One file can have many cog chunks, even abutting each other.
88 infile = """\
89 //[[[cog
90 cog.out("chunk1")
91 //]]]
92 chunk1
93 //[[[end]]]
94 //[[[cog
95 cog.out("chunk2")
96 //]]]
97 chunk2
98 //[[[end]]]
99 between chunks
100 //[[[cog
101 cog.out("chunk3")
102 //]]]
103 chunk3
104 //[[[end]]]
105 """
107 infile = reindentBlock(infile)
108 self.assertEqual(Cog().processString(infile), infile)
110 def testTrimBlankLines(self):
111 infile = """\
112 //[[[cog
113 cog.out("This is line one\\n", trimblanklines=True)
114 cog.out('''
115 This is line two
116 ''', dedent=True, trimblanklines=True)
117 cog.outl("This is line three", trimblanklines=True)
118 //]]]
119 This is line one
120 This is line two
121 This is line three
122 //[[[end]]]
123 """
125 infile = reindentBlock(infile)
126 self.assertEqual(Cog().processString(infile), infile)
128 def testTrimEmptyBlankLines(self):
129 infile = """\
130 //[[[cog
131 cog.out("This is line one\\n", trimblanklines=True)
132 cog.out('''
133 This is line two
134 ''', dedent=True, trimblanklines=True)
135 cog.out('', dedent=True, trimblanklines=True)
136 cog.outl("This is line three", trimblanklines=True)
137 //]]]
138 This is line one
139 This is line two
140 This is line three
141 //[[[end]]]
142 """
144 infile = reindentBlock(infile)
145 self.assertEqual(Cog().processString(infile), infile)
147 def testTrimBlankLinesWithLastPartial(self):
148 infile = """\
149 //[[[cog
150 cog.out("This is line one\\n", trimblanklines=True)
151 cog.out("\\nLine two\\nLine three", trimblanklines=True)
152 //]]]
153 This is line one
154 Line two
155 Line three
156 //[[[end]]]
157 """
159 infile = reindentBlock(infile)
160 self.assertEqual(Cog().processString(infile), infile)
162 def testCogOutDedent(self):
163 infile = """\
164 //[[[cog
165 cog.out("This is the first line\\n")
166 cog.out('''
167 This is dedent=True 1
168 This is dedent=True 2
169 ''', dedent=True, trimblanklines=True)
170 cog.out('''
171 This is dedent=False 1
172 This is dedent=False 2
173 ''', dedent=False, trimblanklines=True)
174 cog.out('''
175 This is dedent=default 1
176 This is dedent=default 2
177 ''', trimblanklines=True)
178 cog.out("This is the last line\\n")
179 //]]]
180 This is the first line
181 This is dedent=True 1
182 This is dedent=True 2
183 This is dedent=False 1
184 This is dedent=False 2
185 This is dedent=default 1
186 This is dedent=default 2
187 This is the last line
188 //[[[end]]]
189 """
191 infile = reindentBlock(infile)
192 self.assertEqual(Cog().processString(infile), infile)
194 def test22EndOfLine(self):
195 # In Python 2.2, this cog file was not parsing because the
196 # last line is indented but didn't end with a newline.
197 infile = """\
198 //[[[cog
199 import cog
200 for i in range(3):
201 cog.out("%d\\n" % i)
202 //]]]
203 0
204 1
205 2
206 //[[[end]]]
207 """
209 infile = reindentBlock(infile)
210 self.assertEqual(Cog().processString(infile), infile)
212 def testIndentedCode(self):
213 infile = """\
214 first line
215 [[[cog
216 import cog
217 for i in range(3):
218 cog.out("xx%d\\n" % i)
219 ]]]
220 xx0
221 xx1
222 xx2
223 [[[end]]]
224 last line
225 """
227 infile = reindentBlock(infile)
228 self.assertEqual(Cog().processString(infile), infile)
230 def testPrefixedCode(self):
231 infile = """\
232 --[[[cog
233 --import cog
234 --for i in range(3):
235 -- cog.out("xx%d\\n" % i)
236 --]]]
237 xx0
238 xx1
239 xx2
240 --[[[end]]]
241 """
243 infile = reindentBlock(infile)
244 self.assertEqual(Cog().processString(infile), infile)
246 def testPrefixedIndentedCode(self):
247 infile = """\
248 prologue
249 --[[[cog
250 -- import cog
251 -- for i in range(3):
252 -- cog.out("xy%d\\n" % i)
253 --]]]
254 xy0
255 xy1
256 xy2
257 --[[[end]]]
258 """
260 infile = reindentBlock(infile)
261 self.assertEqual(Cog().processString(infile), infile)
263 def testBogusPrefixMatch(self):
264 infile = """\
265 prologue
266 #[[[cog
267 import cog
268 # This comment should not be clobbered by removing the pound sign.
269 for i in range(3):
270 cog.out("xy%d\\n" % i)
271 #]]]
272 xy0
273 xy1
274 xy2
275 #[[[end]]]
276 """
278 infile = reindentBlock(infile)
279 self.assertEqual(Cog().processString(infile), infile)
281 def testNoFinalNewline(self):
282 # If the cog'ed output has no final newline,
283 # it shouldn't eat up the cog terminator.
284 infile = """\
285 prologue
286 [[[cog
287 import cog
288 for i in range(3):
289 cog.out("%d" % i)
290 ]]]
291 012
292 [[[end]]]
293 epilogue
294 """
296 infile = reindentBlock(infile)
297 self.assertEqual(Cog().processString(infile), infile)
299 def testNoOutputAtAll(self):
300 # If there is absolutely no cog output, that's ok.
301 infile = """\
302 prologue
303 [[[cog
304 i = 1
305 ]]]
306 [[[end]]]
307 epilogue
308 """
310 infile = reindentBlock(infile)
311 self.assertEqual(Cog().processString(infile), infile)
313 def testPurelyBlankLine(self):
314 # If there is a blank line in the cog code with no whitespace
315 # prefix, that should be OK.
317 infile = """\
318 prologue
319 [[[cog
320 import sys
321 cog.out("Hello")
322 $
323 cog.out("There")
324 ]]]
325 HelloThere
326 [[[end]]]
327 epilogue
328 """
330 infile = reindentBlock(infile.replace('$', ''))
331 self.assertEqual(Cog().processString(infile), infile)
333 def testEmptyOutl(self):
334 # Alexander Belchenko suggested the string argument to outl should
335 # be optional. Does it work?
337 infile = """\
338 prologue
339 [[[cog
340 cog.outl("x")
341 cog.outl()
342 cog.outl("y")
343 cog.out() # Also optional, a complete no-op.
344 cog.outl(trimblanklines=True)
345 cog.outl("z")
346 ]]]
347 x
349 y
351 z
352 [[[end]]]
353 epilogue
354 """
356 infile = reindentBlock(infile)
357 self.assertEqual(Cog().processString(infile), infile)
359 def testFirstLineNum(self):
360 infile = """\
361 fooey
362 [[[cog
363 cog.outl("started at line number %d" % cog.firstLineNum)
364 ]]]
365 started at line number 2
366 [[[end]]]
367 blah blah
368 [[[cog
369 cog.outl("and again at line %d" % cog.firstLineNum)
370 ]]]
371 and again at line 8
372 [[[end]]]
373 """
375 infile = reindentBlock(infile)
376 self.assertEqual(Cog().processString(infile), infile)
378 def testCompactOneLineCode(self):
379 infile = """\
380 first line
381 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
382 get rid of this!
383 [[[end]]]
384 last line
385 """
387 outfile = """\
388 first line
389 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
390 hello 81
391 [[[end]]]
392 last line
393 """
395 infile = reindentBlock(infile)
396 self.assertEqual(Cog().processString(infile), reindentBlock(outfile))
398 def testInsideOutCompact(self):
399 infile = """\
400 first line
401 hey?: ]]] what is this? [[[cog strange!
402 get rid of this!
403 [[[end]]]
404 last line
405 """
406 with self.assertRaisesRegex(CogError, r"^infile.txt\(2\): Cog code markers inverted$"):
407 Cog().processString(reindentBlock(infile), "infile.txt")
409 def testSharingGlobals(self):
410 infile = """\
411 first line
412 hey: [[[cog s="hey there" ]]] looky!
413 [[[end]]]
414 more literal junk.
415 [[[cog cog.outl(s) ]]]
416 [[[end]]]
417 last line
418 """
420 outfile = """\
421 first line
422 hey: [[[cog s="hey there" ]]] looky!
423 [[[end]]]
424 more literal junk.
425 [[[cog cog.outl(s) ]]]
426 hey there
427 [[[end]]]
428 last line
429 """
431 infile = reindentBlock(infile)
432 self.assertEqual(Cog().processString(infile), reindentBlock(outfile))
434 def testAssertInCogCode(self):
435 # Check that we can test assertions in cog code in the test framework.
436 infile = """\
437 [[[cog
438 assert 1 == 2, "Oops"
439 ]]]
440 [[[end]]]
441 """
442 infile = reindentBlock(infile)
443 with self.assertRaisesRegex(CogUserException, "AssertionError: Oops"):
444 Cog().processString(infile)
446 def testCogPrevious(self):
447 # Check that we can access the previous run's output.
448 infile = """\
449 [[[cog
450 assert cog.previous == "Hello there!\\n", "WTF??"
451 cog.out(cog.previous)
452 cog.outl("Ran again!")
453 ]]]
454 Hello there!
455 [[[end]]]
456 """
458 outfile = """\
459 [[[cog
460 assert cog.previous == "Hello there!\\n", "WTF??"
461 cog.out(cog.previous)
462 cog.outl("Ran again!")
463 ]]]
464 Hello there!
465 Ran again!
466 [[[end]]]
467 """
469 infile = reindentBlock(infile)
470 self.assertEqual(Cog().processString(infile), reindentBlock(outfile))
473class CogOptionsTests(TestCase):
474 """ Test the CogOptions class.
475 """
477 def testEquality(self):
478 o = CogOptions()
479 p = CogOptions()
480 self.assertEqual(o, p)
481 o.parseArgs(['-r'])
482 self.assertNotEqual(o, p)
483 p.parseArgs(['-r'])
484 self.assertEqual(o, p)
486 def testCloning(self):
487 o = CogOptions()
488 o.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/'])
489 p = o.clone()
490 self.assertEqual(o, p)
491 p.parseArgs(['-I', 'huey', '-D', 'foo=quux'])
492 self.assertNotEqual(o, p)
493 q = CogOptions()
494 q.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/', '-I', 'huey', '-D', 'foo=quux'])
495 self.assertEqual(p, q)
497 def testCombiningFlags(self):
498 # Single-character flags can be combined.
499 o = CogOptions()
500 o.parseArgs(['-e', '-r', '-z'])
501 p = CogOptions()
502 p.parseArgs(['-erz'])
503 self.assertEqual(o, p)
505 def testMarkers(self):
506 o = CogOptions()
507 o._parse_markers('a b c')
508 self.assertEqual('a', o.sBeginSpec)
509 self.assertEqual('b', o.sEndSpec)
510 self.assertEqual('c', o.sEndOutput)
512 def testMarkersSwitch(self):
513 o = CogOptions()
514 o.parseArgs(['--markers', 'a b c'])
515 self.assertEqual('a', o.sBeginSpec)
516 self.assertEqual('b', o.sEndSpec)
517 self.assertEqual('c', o.sEndOutput)
520class FileStructureTests(TestCase):
521 """ Test cases to check that we're properly strict about the structure
522 of files.
523 """
525 def isBad(self, infile, msg=None):
526 infile = reindentBlock(infile)
527 with self.assertRaisesRegex(CogError, "^"+re.escape(msg)+"$"):
528 Cog().processString(infile, 'infile.txt')
530 def testBeginNoEnd(self):
531 infile = """\
532 Fooey
533 #[[[cog
534 cog.outl('hello')
535 """
536 self.isBad(infile, "infile.txt(2): Cog block begun but never ended.")
538 def testNoEoo(self):
539 infile = """\
540 Fooey
541 #[[[cog
542 cog.outl('hello')
543 #]]]
544 """
545 self.isBad(infile, "infile.txt(4): Missing '[[[end]]]' before end of file.")
547 infile2 = """\
548 Fooey
549 #[[[cog
550 cog.outl('hello')
551 #]]]
552 #[[[cog
553 cog.outl('goodbye')
554 #]]]
555 """
556 self.isBad(infile2, "infile.txt(5): Unexpected '[[[cog'")
558 def testStartWithEnd(self):
559 infile = """\
560 #]]]
561 """
562 self.isBad(infile, "infile.txt(1): Unexpected ']]]'")
564 infile2 = """\
565 #[[[cog
566 cog.outl('hello')
567 #]]]
568 #[[[end]]]
569 #]]]
570 """
571 self.isBad(infile2, "infile.txt(5): Unexpected ']]]'")
573 def testStartWithEoo(self):
574 infile = """\
575 #[[[end]]]
576 """
577 self.isBad(infile, "infile.txt(1): Unexpected '[[[end]]]'")
579 infile2 = """\
580 #[[[cog
581 cog.outl('hello')
582 #]]]
583 #[[[end]]]
584 #[[[end]]]
585 """
586 self.isBad(infile2, "infile.txt(5): Unexpected '[[[end]]]'")
588 def testNoEnd(self):
589 infile = """\
590 #[[[cog
591 cog.outl("hello")
592 #[[[end]]]
593 """
594 self.isBad(infile, "infile.txt(3): Unexpected '[[[end]]]'")
596 infile2 = """\
597 #[[[cog
598 cog.outl('hello')
599 #]]]
600 #[[[end]]]
601 #[[[cog
602 cog.outl("hello")
603 #[[[end]]]
604 """
605 self.isBad(infile2, "infile.txt(7): Unexpected '[[[end]]]'")
607 def testTwoBegins(self):
608 infile = """\
609 #[[[cog
610 #[[[cog
611 cog.outl("hello")
612 #]]]
613 #[[[end]]]
614 """
615 self.isBad(infile, "infile.txt(2): Unexpected '[[[cog'")
617 infile2 = """\
618 #[[[cog
619 cog.outl("hello")
620 #]]]
621 #[[[end]]]
622 #[[[cog
623 #[[[cog
624 cog.outl("hello")
625 #]]]
626 #[[[end]]]
627 """
628 self.isBad(infile2, "infile.txt(6): Unexpected '[[[cog'")
630 def testTwoEnds(self):
631 infile = """\
632 #[[[cog
633 cog.outl("hello")
634 #]]]
635 #]]]
636 #[[[end]]]
637 """
638 self.isBad(infile, "infile.txt(4): Unexpected ']]]'")
640 infile2 = """\
641 #[[[cog
642 cog.outl("hello")
643 #]]]
644 #[[[end]]]
645 #[[[cog
646 cog.outl("hello")
647 #]]]
648 #]]]
649 #[[[end]]]
650 """
651 self.isBad(infile2, "infile.txt(8): Unexpected ']]]'")
654class CogErrorTests(TestCase):
655 """ Test cases for cog.error().
656 """
658 def testErrorMsg(self):
659 infile = """\
660 [[[cog cog.error("This ain't right!")]]]
661 [[[end]]]
662 """
664 infile = reindentBlock(infile)
665 with self.assertRaisesRegex(CogGeneratedError, "^This ain't right!$"):
666 Cog().processString(infile)
668 def testErrorNoMsg(self):
669 infile = """\
670 [[[cog cog.error()]]]
671 [[[end]]]
672 """
674 infile = reindentBlock(infile)
675 with self.assertRaisesRegex(CogGeneratedError, "^Error raised by cog generator.$"):
676 Cog().processString(infile)
678 def testNoErrorIfErrorNotCalled(self):
679 infile = """\
680 --[[[cog
681 --import cog
682 --for i in range(3):
683 -- if i > 10:
684 -- cog.error("Something is amiss!")
685 -- cog.out("xx%d\\n" % i)
686 --]]]
687 xx0
688 xx1
689 xx2
690 --[[[end]]]
691 """
693 infile = reindentBlock(infile)
694 self.assertEqual(Cog().processString(infile), infile)
697class CogGeneratorGetCodeTests(TestCase):
698 """ Unit tests against CogGenerator to see if its getCode() method works
699 properly.
700 """
702 def setUp(self):
703 """ All tests get a generator to use, and short same-length names for
704 the functions we're going to use.
705 """
706 self.gen = CogGenerator()
707 self.m = self.gen.parseMarker
708 self.l = self.gen.parseLine
710 def testEmpty(self):
711 self.m('// [[[cog')
712 self.m('// ]]]')
713 self.assertEqual(self.gen.getCode(), '')
715 def testSimple(self):
716 self.m('// [[[cog')
717 self.l(' print "hello"')
718 self.l(' print "bye"')
719 self.m('// ]]]')
720 self.assertEqual(self.gen.getCode(), 'print "hello"\nprint "bye"')
722 def testCompressed1(self):
723 # For a while, I supported compressed code blocks, but no longer.
724 self.m('// [[[cog: print """')
725 self.l('// hello')
726 self.l('// bye')
727 self.m('// """)]]]')
728 self.assertEqual(self.gen.getCode(), 'hello\nbye')
730 def testCompressed2(self):
731 # For a while, I supported compressed code blocks, but no longer.
732 self.m('// [[[cog: print """')
733 self.l('hello')
734 self.l('bye')
735 self.m('// """)]]]')
736 self.assertEqual(self.gen.getCode(), 'hello\nbye')
738 def testCompressed3(self):
739 # For a while, I supported compressed code blocks, but no longer.
740 self.m('// [[[cog')
741 self.l('print """hello')
742 self.l('bye')
743 self.m('// """)]]]')
744 self.assertEqual(self.gen.getCode(), 'print """hello\nbye')
746 def testCompressed4(self):
747 # For a while, I supported compressed code blocks, but no longer.
748 self.m('// [[[cog: print """')
749 self.l('hello')
750 self.l('bye""")')
751 self.m('// ]]]')
752 self.assertEqual(self.gen.getCode(), 'hello\nbye""")')
754 def testNoCommonPrefixForMarkers(self):
755 # It's important to be able to use #if 0 to hide lines from a
756 # C++ compiler.
757 self.m('#if 0 //[[[cog')
758 self.l('\timport cog, sys')
759 self.l('')
760 self.l('\tprint sys.argv')
761 self.m('#endif //]]]')
762 self.assertEqual(self.gen.getCode(), 'import cog, sys\n\nprint sys.argv')
765class TestCaseWithTempDir(TestCase):
767 def newCog(self):
768 """ Initialize the cog members for another run.
769 """
770 # Create a cog engine, and catch its output.
771 self.cog = Cog()
772 self.output = StringIO()
773 self.cog.setOutput(stdout=self.output, stderr=self.output)
775 def setUp(self):
776 # Create a temporary directory.
777 self.tempdir = os.path.join(tempfile.gettempdir(), 'testcog_tempdir_' + str(random.random())[2:])
778 os.mkdir(self.tempdir)
779 self.olddir = os.getcwd()
780 os.chdir(self.tempdir)
781 self.newCog()
783 def tearDown(self):
784 os.chdir(self.olddir)
785 # Get rid of the temporary directory.
786 shutil.rmtree(self.tempdir)
788 def assertFilesSame(self, sFName1, sFName2):
789 text1 = open(os.path.join(self.tempdir, sFName1), 'rb').read()
790 text2 = open(os.path.join(self.tempdir, sFName2), 'rb').read()
791 self.assertEqual(text1, text2)
793 def assertFileContent(self, sFName, sContent):
794 sAbsName = os.path.join(self.tempdir, sFName)
795 f = open(sAbsName, 'rb')
796 try:
797 sFileContent = f.read()
798 finally:
799 f.close()
800 self.assertEqual(sFileContent, to_bytes(sContent))
803class ArgumentHandlingTests(TestCaseWithTempDir):
805 def testArgumentFailure(self):
806 # Return value 2 means usage problem.
807 self.assertEqual(self.cog.main(['argv0', '-j']), 2)
808 output = self.output.getvalue()
809 self.assertIn("option -j not recognized", output)
810 with self.assertRaisesRegex(CogUsageError, r"^No files to process$"):
811 self.cog.callableMain(['argv0'])
812 with self.assertRaisesRegex(CogUsageError, r"^option -j not recognized$"):
813 self.cog.callableMain(['argv0', '-j'])
815 def testNoDashOAndAtFile(self):
816 d = {
817 'cogfiles.txt': """\
818 # Please run cog
819 """
820 }
822 makeFiles(d)
823 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with @file$"):
824 self.cog.callableMain(['argv0', '-o', 'foo', '@cogfiles.txt'])
826 def testDashV(self):
827 self.assertEqual(self.cog.main(['argv0', '-v']), 0)
828 output = self.output.getvalue()
829 self.assertEqual('Cog version %s\n' % __version__, output)
831 def producesHelp(self, args):
832 self.newCog()
833 argv = ['argv0'] + args.split()
834 self.assertEqual(self.cog.main(argv), 0)
835 self.assertEqual(usage, self.output.getvalue())
837 def testDashH(self):
838 # -h or -? anywhere on the command line should just print help.
839 self.producesHelp("-h")
840 self.producesHelp("-?")
841 self.producesHelp("fooey.txt -h")
842 self.producesHelp("-o -r @fooey.txt -? @booey.txt")
844 def testDashOAndDashR(self):
845 d = {
846 'cogfile.txt': """\
847 # Please run cog
848 """
849 }
851 makeFiles(d)
852 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with -r \(they are opposites\)$"):
853 self.cog.callableMain(['argv0', '-o', 'foo', '-r', 'cogfile.txt'])
855 def testDashZ(self):
856 d = {
857 'test.cog': """\
858 // This is my C++ file.
859 //[[[cog
860 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
861 for fn in fnames:
862 cog.outl("void %s();" % fn)
863 //]]]
864 """,
866 'test.out': """\
867 // This is my C++ file.
868 //[[[cog
869 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
870 for fn in fnames:
871 cog.outl("void %s();" % fn)
872 //]]]
873 void DoSomething();
874 void DoAnotherThing();
875 void DoLastThing();
876 """,
877 }
879 makeFiles(d)
880 with self.assertRaisesRegex(CogError, r"^test.cog\(6\): Missing '\[\[\[end\]\]\]' before end of file.$"):
881 self.cog.callableMain(['argv0', '-r', 'test.cog'])
882 self.newCog()
883 self.cog.callableMain(['argv0', '-r', '-z', 'test.cog'])
884 self.assertFilesSame('test.cog', 'test.out')
886 def testBadDashD(self):
887 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
888 self.cog.callableMain(['argv0', '-Dfooey', 'cog.txt'])
889 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
890 self.cog.callableMain(['argv0', '-D', 'fooey', 'cog.txt'])
892 def testBadMarkers(self):
893 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'X'$"):
894 self.cog.callableMain(['argv0', '--markers=X'])
895 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'A B C D'$"):
896 self.cog.callableMain(['argv0', '--markers=A B C D'])
899class TestMain(TestCaseWithTempDir):
900 def setUp(self):
901 super(TestMain, self).setUp()
902 self.old_argv = sys.argv[:]
903 self.old_stderr = sys.stderr
904 sys.stderr = StringIO()
906 def tearDown(self):
907 sys.stderr = self.old_stderr
908 sys.argv = self.old_argv
909 sys.modules.pop('mycode', None)
910 super(TestMain, self).tearDown()
912 def test_main_function(self):
913 sys.argv = ["argv0", "-Z"]
914 ret = main()
915 self.assertEqual(ret, 2)
916 stderr = sys.stderr.getvalue()
917 self.assertEqual(stderr, 'option -Z not recognized\n(for help use -?)\n')
919 files = {
920 'test.cog': """\
921 //[[[cog
922 def func():
923 import mycode
924 mycode.boom()
925 //]]]
926 //[[[end]]]
927 -----
928 //[[[cog
929 func()
930 //]]]
931 //[[[end]]]
932 """,
934 'mycode.py': """\
935 def boom():
936 [][0]
937 """,
938 }
940 def test_error_report(self):
941 self.check_error_report()
943 def test_error_report_with_prologue(self):
944 self.check_error_report("-p", "#1\n#2")
946 def check_error_report(self, *args):
947 """Check that the error report is right."""
948 makeFiles(self.files)
949 sys.argv = ["argv0"] + list(args) + ["-r", "test.cog"]
950 main()
951 expected = reindentBlock("""\
952 Traceback (most recent call last):
953 File "test.cog", line 9, in <module>
954 func()
955 File "test.cog", line 4, in func
956 mycode.boom()
957 File "MYCODE", line 2, in boom
958 [][0]
959 IndexError: list index out of range
960 """)
961 if PY3:
962 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
963 else:
964 expected = expected.replace("MYCODE", "mycode.py")
965 assert expected == sys.stderr.getvalue()
967 def test_error_in_prologue(self):
968 makeFiles(self.files)
969 sys.argv = ["argv0", "-p", "import mycode; mycode.boom()", "-r", "test.cog"]
970 main()
971 expected = reindentBlock("""\
972 Traceback (most recent call last):
973 File "<prologue>", line 1, in <module>
974 import mycode; mycode.boom()
975 File "MYCODE", line 2, in boom
976 [][0]
977 IndexError: list index out of range
978 """)
979 if PY3:
980 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
981 else:
982 expected = expected.replace("MYCODE", "mycode.py")
983 assert expected == sys.stderr.getvalue()
987class TestFileHandling(TestCaseWithTempDir):
989 def testSimple(self):
990 d = {
991 'test.cog': """\
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 //[[[end]]]
999 """,
1001 'test.out': """\
1002 // This is my C++ file.
1003 //[[[cog
1004 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1005 for fn in fnames:
1006 cog.outl("void %s();" % fn)
1007 //]]]
1008 void DoSomething();
1009 void DoAnotherThing();
1010 void DoLastThing();
1011 //[[[end]]]
1012 """,
1013 }
1015 makeFiles(d)
1016 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1017 self.assertFilesSame('test.cog', 'test.out')
1018 output = self.output.getvalue()
1019 self.assertIn("(changed)", output)
1021 def testPrintOutput(self):
1022 d = {
1023 'test.cog': """\
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 //[[[end]]]
1031 """,
1033 'test.out': """\
1034 // This is my C++ file.
1035 //[[[cog
1036 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1037 for fn in fnames:
1038 print("void %s();" % fn)
1039 //]]]
1040 void DoSomething();
1041 void DoAnotherThing();
1042 void DoLastThing();
1043 //[[[end]]]
1044 """,
1045 }
1047 makeFiles(d)
1048 self.cog.callableMain(['argv0', '-rP', 'test.cog'])
1049 self.assertFilesSame('test.cog', 'test.out')
1050 output = self.output.getvalue()
1051 self.assertIn("(changed)", output)
1053 def testWildcards(self):
1054 d = {
1055 'test.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 'test2.cog': """\
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 //[[[end]]]
1073 """,
1075 'test.out': """\
1076 // This is my C++ file.
1077 //[[[cog
1078 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1079 for fn in fnames:
1080 cog.outl("void %s();" % fn)
1081 //]]]
1082 void DoSomething();
1083 void DoAnotherThing();
1084 void DoLastThing();
1085 //[[[end]]]
1086 """,
1088 'not_this_one.cog': """\
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 """,
1098 'not_this_one.out': """\
1099 // This is my C++ file.
1100 //[[[cog
1101 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1102 for fn in fnames:
1103 cog.outl("void %s();" % fn)
1104 //]]]
1105 //[[[end]]]
1106 """,
1107 }
1109 makeFiles(d)
1110 self.cog.callableMain(['argv0', '-r', 't*.cog'])
1111 self.assertFilesSame('test.cog', 'test.out')
1112 self.assertFilesSame('test2.cog', 'test.out')
1113 self.assertFilesSame('not_this_one.cog', 'not_this_one.out')
1114 output = self.output.getvalue()
1115 self.assertIn("(changed)", output)
1117 def testOutputFile(self):
1118 # -o sets the output file.
1119 d = {
1120 'test.cog': """\
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 //[[[end]]]
1128 """,
1130 'test.out': """\
1131 // This is my C++ file.
1132 //[[[cog
1133 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1134 for fn in fnames:
1135 cog.outl("void %s();" % fn)
1136 //]]]
1137 void DoSomething();
1138 void DoAnotherThing();
1139 void DoLastThing();
1140 //[[[end]]]
1141 """,
1142 }
1144 makeFiles(d)
1145 self.cog.callableMain(['argv0', '-o', 'in/a/dir/test.cogged', 'test.cog'])
1146 self.assertFilesSame('in/a/dir/test.cogged', 'test.out')
1148 def testAtFile(self):
1149 d = {
1150 'one.cog': """\
1151 //[[[cog
1152 cog.outl("hello world")
1153 //]]]
1154 //[[[end]]]
1155 """,
1157 'one.out': """\
1158 //[[[cog
1159 cog.outl("hello world")
1160 //]]]
1161 hello world
1162 //[[[end]]]
1163 """,
1165 'two.cog': """\
1166 //[[[cog
1167 cog.outl("goodbye cruel world")
1168 //]]]
1169 //[[[end]]]
1170 """,
1172 'two.out': """\
1173 //[[[cog
1174 cog.outl("goodbye cruel world")
1175 //]]]
1176 goodbye cruel world
1177 //[[[end]]]
1178 """,
1180 'cogfiles.txt': """\
1181 # Please run cog
1182 one.cog
1184 two.cog
1185 """
1186 }
1188 makeFiles(d)
1189 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1190 self.assertFilesSame('one.cog', 'one.out')
1191 self.assertFilesSame('two.cog', 'two.out')
1192 output = self.output.getvalue()
1193 self.assertIn("(changed)", output)
1195 def testNestedAtFile(self):
1196 d = {
1197 'one.cog': """\
1198 //[[[cog
1199 cog.outl("hello world")
1200 //]]]
1201 //[[[end]]]
1202 """,
1204 'one.out': """\
1205 //[[[cog
1206 cog.outl("hello world")
1207 //]]]
1208 hello world
1209 //[[[end]]]
1210 """,
1212 'two.cog': """\
1213 //[[[cog
1214 cog.outl("goodbye cruel world")
1215 //]]]
1216 //[[[end]]]
1217 """,
1219 'two.out': """\
1220 //[[[cog
1221 cog.outl("goodbye cruel world")
1222 //]]]
1223 goodbye cruel world
1224 //[[[end]]]
1225 """,
1227 'cogfiles.txt': """\
1228 # Please run cog
1229 one.cog
1230 @cogfiles2.txt
1231 """,
1233 'cogfiles2.txt': """\
1234 # This one too, please.
1235 two.cog
1236 """,
1237 }
1239 makeFiles(d)
1240 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1241 self.assertFilesSame('one.cog', 'one.out')
1242 self.assertFilesSame('two.cog', 'two.out')
1243 output = self.output.getvalue()
1244 self.assertIn("(changed)", output)
1246 def testAtFileWithArgs(self):
1247 d = {
1248 'both.cog': """\
1249 //[[[cog
1250 cog.outl("one: %s" % ('one' in globals()))
1251 cog.outl("two: %s" % ('two' in globals()))
1252 //]]]
1253 //[[[end]]]
1254 """,
1256 'one.out': """\
1257 //[[[cog
1258 cog.outl("one: %s" % ('one' in globals()))
1259 cog.outl("two: %s" % ('two' in globals()))
1260 //]]]
1261 one: True // ONE
1262 two: False // ONE
1263 //[[[end]]]
1264 """,
1266 'two.out': """\
1267 //[[[cog
1268 cog.outl("one: %s" % ('one' in globals()))
1269 cog.outl("two: %s" % ('two' in globals()))
1270 //]]]
1271 one: False // TWO
1272 two: True // TWO
1273 //[[[end]]]
1274 """,
1276 'cogfiles.txt': """\
1277 # Please run cog
1278 both.cog -o in/a/dir/both.one -s ' // ONE' -D one=x
1279 both.cog -o in/a/dir/both.two -s ' // TWO' -D two=x
1280 """
1281 }
1283 makeFiles(d)
1284 self.cog.callableMain(['argv0', '@cogfiles.txt'])
1285 self.assertFilesSame('in/a/dir/both.one', 'one.out')
1286 self.assertFilesSame('in/a/dir/both.two', 'two.out')
1288 def testAtFileWithBadArgCombo(self):
1289 d = {
1290 'both.cog': """\
1291 //[[[cog
1292 cog.outl("one: %s" % ('one' in globals()))
1293 cog.outl("two: %s" % ('two' in globals()))
1294 //]]]
1295 //[[[end]]]
1296 """,
1298 'cogfiles.txt': """\
1299 # Please run cog
1300 both.cog
1301 both.cog -d # This is bad: -r and -d
1302 """
1303 }
1305 makeFiles(d)
1306 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"):
1307 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1309 def testAtFileWithTrickyFilenames(self):
1310 def fix_backslashes(files_txt):
1311 """Make the contents of a files.txt sensitive to the platform."""
1312 if sys.platform != "win32":
1313 files_txt = files_txt.replace("\\", "/")
1314 return files_txt
1316 d = {
1317 'one 1.cog': """\
1318 //[[[cog cog.outl("hello world") ]]]
1319 """,
1321 'one.out': """\
1322 //[[[cog cog.outl("hello world") ]]]
1323 hello world //xxx
1324 """,
1326 'subdir': {
1327 'subback.cog': """\
1328 //[[[cog cog.outl("down deep with backslashes") ]]]
1329 """,
1331 'subfwd.cog': """\
1332 //[[[cog cog.outl("down deep with slashes") ]]]
1333 """,
1334 },
1336 'subback.out': """\
1337 //[[[cog cog.outl("down deep with backslashes") ]]]
1338 down deep with backslashes //yyy
1339 """,
1341 'subfwd.out': """\
1342 //[[[cog cog.outl("down deep with slashes") ]]]
1343 down deep with slashes //zzz
1344 """,
1346 'cogfiles.txt': fix_backslashes("""\
1347 # Please run cog
1348 'one 1.cog' -s ' //xxx'
1349 subdir\\subback.cog -s ' //yyy'
1350 subdir/subfwd.cog -s ' //zzz'
1351 """)
1352 }
1354 makeFiles(d)
1355 self.cog.callableMain(['argv0', '-z', '-r', '@cogfiles.txt'])
1356 self.assertFilesSame('one 1.cog', 'one.out')
1357 self.assertFilesSame('subdir/subback.cog', 'subback.out')
1358 self.assertFilesSame('subdir/subfwd.cog', 'subfwd.out')
1360 def run_with_verbosity(self, verbosity):
1361 d = {
1362 'unchanged.cog': """\
1363 //[[[cog
1364 cog.outl("hello world")
1365 //]]]
1366 hello world
1367 //[[[end]]]
1368 """,
1370 'changed.cog': """\
1371 //[[[cog
1372 cog.outl("goodbye cruel world")
1373 //]]]
1374 //[[[end]]]
1375 """,
1377 'cogfiles.txt': """\
1378 unchanged.cog
1379 changed.cog
1380 """
1381 }
1383 makeFiles(d)
1384 self.cog.callableMain(['argv0', '-r', '--verbosity='+verbosity, '@cogfiles.txt'])
1385 output = self.output.getvalue()
1386 return output
1388 def test_verbosity0(self):
1389 output = self.run_with_verbosity("0")
1390 self.assertEqual(output, "")
1392 def test_verbosity1(self):
1393 output = self.run_with_verbosity("1")
1394 self.assertEqual(output, "Cogging changed.cog (changed)\n")
1396 def test_verbosity2(self):
1397 output = self.run_with_verbosity("2")
1398 self.assertEqual(output, "Cogging unchanged.cog\nCogging changed.cog (changed)\n")
1401class CogTestLineEndings(TestCaseWithTempDir):
1402 """Tests for -U option (force LF line-endings in output)."""
1404 lines_in = ['Some text.',
1405 '//[[[cog',
1406 'cog.outl("Cog text")',
1407 '//]]]',
1408 'gobbledegook.',
1409 '//[[[end]]]',
1410 'epilogue.',
1411 '']
1413 lines_out = ['Some text.',
1414 '//[[[cog',
1415 'cog.outl("Cog text")',
1416 '//]]]',
1417 'Cog text',
1418 '//[[[end]]]',
1419 'epilogue.',
1420 '']
1422 def testOutputNativeEol(self):
1423 makeFiles({'infile': '\n'.join(self.lines_in)})
1424 self.cog.callableMain(['argv0', '-o', 'outfile', 'infile'])
1425 self.assertFileContent('outfile', os.linesep.join(self.lines_out))
1427 def testOutputLfEol(self):
1428 makeFiles({'infile': '\n'.join(self.lines_in)})
1429 self.cog.callableMain(['argv0', '-U', '-o', 'outfile', 'infile'])
1430 self.assertFileContent('outfile', '\n'.join(self.lines_out))
1432 def testReplaceNativeEol(self):
1433 makeFiles({'test.cog': '\n'.join(self.lines_in)})
1434 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1435 self.assertFileContent('test.cog', os.linesep.join(self.lines_out))
1437 def testReplaceLfEol(self):
1438 makeFiles({'test.cog': '\n'.join(self.lines_in)})
1439 self.cog.callableMain(['argv0', '-U', '-r', 'test.cog'])
1440 self.assertFileContent('test.cog', '\n'.join(self.lines_out))
1443class CogTestCharacterEncoding(TestCaseWithTempDir):
1445 def testSimple(self):
1446 d = {
1447 'test.cog': b"""\
1448 // This is my C++ file.
1449 //[[[cog
1450 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
1451 //]]]
1452 //[[[end]]]
1453 """,
1455 'test.out': b"""\
1456 // This is my C++ file.
1457 //[[[cog
1458 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
1459 //]]]
1460 // Unicode: \xe1\x88\xb4 (U+1234)
1461 //[[[end]]]
1462 """.replace(b"\n", os.linesep.encode()),
1463 }
1465 makeFiles(d, bytes=True)
1466 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1467 self.assertFilesSame('test.cog', 'test.out')
1468 output = self.output.getvalue()
1469 self.assertIn("(changed)", output)
1471 def testFileEncodingOption(self):
1472 d = {
1473 'test.cog': b"""\
1474 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
1475 //[[[cog
1476 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")
1477 //]]]
1478 //[[[end]]]
1479 """,
1481 'test.out': b"""\
1482 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
1483 //[[[cog
1484 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")
1485 //]]]
1486 \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
1487 //[[[end]]]
1488 """.replace(b"\n", os.linesep.encode()),
1489 }
1491 makeFiles(d, bytes=True)
1492 self.cog.callableMain(['argv0', '-n', 'cp1251', '-r', 'test.cog'])
1493 self.assertFilesSame('test.cog', 'test.out')
1494 output = self.output.getvalue()
1495 self.assertIn("(changed)", output)
1498class TestCaseWithImports(TestCaseWithTempDir):
1499 """ When running tests which import modules, the sys.modules list
1500 leaks from one test to the next. This test case class scrubs
1501 the list after each run to keep the tests isolated from each other.
1502 """
1504 def setUp(self):
1505 super(TestCaseWithImports, self).setUp()
1506 self.sysmodulekeys = list(sys.modules)
1508 def tearDown(self):
1509 modstoscrub = [
1510 modname
1511 for modname in sys.modules
1512 if modname not in self.sysmodulekeys
1513 ]
1514 for modname in modstoscrub:
1515 del sys.modules[modname]
1516 super(TestCaseWithImports, self).tearDown()
1519class CogIncludeTests(TestCaseWithImports):
1520 dincludes = {
1521 'test.cog': """\
1522 //[[[cog
1523 import mymodule
1524 //]]]
1525 //[[[end]]]
1526 """,
1528 'test.out': """\
1529 //[[[cog
1530 import mymodule
1531 //]]]
1532 Hello from mymodule
1533 //[[[end]]]
1534 """,
1536 'test2.out': """\
1537 //[[[cog
1538 import mymodule
1539 //]]]
1540 Hello from mymodule in inc2
1541 //[[[end]]]
1542 """,
1544 'include': {
1545 'mymodule.py': """\
1546 import cog
1547 cog.outl("Hello from mymodule")
1548 """
1549 },
1551 'inc2': {
1552 'mymodule.py': """\
1553 import cog
1554 cog.outl("Hello from mymodule in inc2")
1555 """
1556 },
1558 'inc3': {
1559 'someothermodule.py': """\
1560 import cog
1561 cog.outl("This is some other module.")
1562 """
1563 },
1564 }
1566 def testNeedIncludePath(self):
1567 # Try it without the -I, to see that an ImportError happens.
1568 makeFiles(self.dincludes)
1569 msg = "(ImportError|ModuleNotFoundError): No module named '?mymodule'?"
1570 with self.assertRaisesRegex(CogUserException, msg):
1571 self.cog.callableMain(['argv0', '-r', 'test.cog'])
1573 def testIncludePath(self):
1574 # Test that -I adds include directories properly.
1575 makeFiles(self.dincludes)
1576 self.cog.callableMain(['argv0', '-r', '-I', 'include', 'test.cog'])
1577 self.assertFilesSame('test.cog', 'test.out')
1579 def testTwoIncludePaths(self):
1580 # Test that two -I's add include directories properly.
1581 makeFiles(self.dincludes)
1582 self.cog.callableMain(['argv0', '-r', '-I', 'include', '-I', 'inc2', 'test.cog'])
1583 self.assertFilesSame('test.cog', 'test.out')
1585 def testTwoIncludePaths2(self):
1586 # Test that two -I's add include directories properly.
1587 makeFiles(self.dincludes)
1588 self.cog.callableMain(['argv0', '-r', '-I', 'inc2', '-I', 'include', 'test.cog'])
1589 self.assertFilesSame('test.cog', 'test2.out')
1591 def testUselessIncludePath(self):
1592 # Test that the search will continue past the first directory.
1593 makeFiles(self.dincludes)
1594 self.cog.callableMain(['argv0', '-r', '-I', 'inc3', '-I', 'include', 'test.cog'])
1595 self.assertFilesSame('test.cog', 'test.out')
1597 def testSysPathIsUnchanged(self):
1598 d = {
1599 'bad.cog': """\
1600 //[[[cog cog.error("Oh no!") ]]]
1601 //[[[end]]]
1602 """,
1603 'good.cog': """\
1604 //[[[cog cog.outl("Oh yes!") ]]]
1605 //[[[end]]]
1606 """,
1607 }
1609 makeFiles(d)
1610 # Is it unchanged just by creating a cog engine?
1611 oldsyspath = sys.path[:]
1612 self.newCog()
1613 self.assertEqual(oldsyspath, sys.path)
1614 # Is it unchanged for a successful run?
1615 self.newCog()
1616 self.cog.callableMain(['argv0', '-r', 'good.cog'])
1617 self.assertEqual(oldsyspath, sys.path)
1618 # Is it unchanged for a successful run with includes?
1619 self.newCog()
1620 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'good.cog'])
1621 self.assertEqual(oldsyspath, sys.path)
1622 # Is it unchanged for a successful run with two includes?
1623 self.newCog()
1624 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'good.cog'])
1625 self.assertEqual(oldsyspath, sys.path)
1626 # Is it unchanged for a failed run?
1627 self.newCog()
1628 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1629 self.cog.callableMain(['argv0', '-r', 'bad.cog'])
1630 self.assertEqual(oldsyspath, sys.path)
1631 # Is it unchanged for a failed run with includes?
1632 self.newCog()
1633 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1634 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'bad.cog'])
1635 self.assertEqual(oldsyspath, sys.path)
1636 # Is it unchanged for a failed run with two includes?
1637 self.newCog()
1638 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1639 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'bad.cog'])
1640 self.assertEqual(oldsyspath, sys.path)
1642 def testSubDirectories(self):
1643 # Test that relative paths on the command line work, with includes.
1645 d = {
1646 'code': {
1647 'test.cog': """\
1648 //[[[cog
1649 import mysubmodule
1650 //]]]
1651 //[[[end]]]
1652 """,
1654 'test.out': """\
1655 //[[[cog
1656 import mysubmodule
1657 //]]]
1658 Hello from mysubmodule
1659 //[[[end]]]
1660 """,
1662 'mysubmodule.py': """\
1663 import cog
1664 cog.outl("Hello from mysubmodule")
1665 """
1666 }
1667 }
1669 makeFiles(d)
1670 # We should be able to invoke cog without the -I switch, and it will
1671 # auto-include the current directory
1672 self.cog.callableMain(['argv0', '-r', 'code/test.cog'])
1673 self.assertFilesSame('code/test.cog', 'code/test.out')
1676class CogTestsInFiles(TestCaseWithTempDir):
1678 def testWarnIfNoCogCode(self):
1679 # Test that the -e switch warns if there is no Cog code.
1680 d = {
1681 'with.cog': """\
1682 //[[[cog
1683 cog.outl("hello world")
1684 //]]]
1685 hello world
1686 //[[[end]]]
1687 """,
1689 'without.cog': """\
1690 There's no cog
1691 code in this file.
1692 """,
1693 }
1695 makeFiles(d)
1696 self.cog.callableMain(['argv0', '-e', 'with.cog'])
1697 output = self.output.getvalue()
1698 self.assertNotIn("Warning", output)
1699 self.newCog()
1700 self.cog.callableMain(['argv0', '-e', 'without.cog'])
1701 output = self.output.getvalue()
1702 self.assertIn("Warning: no cog code found in without.cog", output)
1703 self.newCog()
1704 self.cog.callableMain(['argv0', 'without.cog'])
1705 output = self.output.getvalue()
1706 self.assertNotIn("Warning", output)
1708 def testFileNameProps(self):
1709 d = {
1710 'cog1.txt': """\
1711 //[[[cog
1712 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1713 //]]]
1714 this is cog1.txt in, cog1.txt out
1715 [[[end]]]
1716 """,
1718 'cog1.out': """\
1719 //[[[cog
1720 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1721 //]]]
1722 This is cog1.txt in, cog1.txt out
1723 [[[end]]]
1724 """,
1726 'cog1out.out': """\
1727 //[[[cog
1728 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1729 //]]]
1730 This is cog1.txt in, cog1out.txt out
1731 [[[end]]]
1732 """,
1733 }
1735 makeFiles(d)
1736 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
1737 self.assertFilesSame('cog1.txt', 'cog1.out')
1738 self.newCog()
1739 self.cog.callableMain(['argv0', '-o', 'cog1out.txt', 'cog1.txt'])
1740 self.assertFilesSame('cog1out.txt', 'cog1out.out')
1742 def testGlobalsDontCrossFiles(self):
1743 # Make sure that global values don't get shared between files.
1744 d = {
1745 'one.cog': """\
1746 //[[[cog s = "This was set in one.cog" ]]]
1747 //[[[end]]]
1748 //[[[cog cog.outl(s) ]]]
1749 //[[[end]]]
1750 """,
1752 'one.out': """\
1753 //[[[cog s = "This was set in one.cog" ]]]
1754 //[[[end]]]
1755 //[[[cog cog.outl(s) ]]]
1756 This was set in one.cog
1757 //[[[end]]]
1758 """,
1760 'two.cog': """\
1761 //[[[cog
1762 try:
1763 cog.outl(s)
1764 except NameError:
1765 cog.outl("s isn't set!")
1766 //]]]
1767 //[[[end]]]
1768 """,
1770 'two.out': """\
1771 //[[[cog
1772 try:
1773 cog.outl(s)
1774 except NameError:
1775 cog.outl("s isn't set!")
1776 //]]]
1777 s isn't set!
1778 //[[[end]]]
1779 """,
1781 'cogfiles.txt': """\
1782 # Please run cog
1783 one.cog
1785 two.cog
1786 """
1787 }
1789 makeFiles(d)
1790 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
1791 self.assertFilesSame('one.cog', 'one.out')
1792 self.assertFilesSame('two.cog', 'two.out')
1793 output = self.output.getvalue()
1794 self.assertIn("(changed)", output)
1796 def testRemoveGeneratedOutput(self):
1797 d = {
1798 'cog1.txt': """\
1799 //[[[cog
1800 cog.outl("This line was generated.")
1801 //]]]
1802 This line was generated.
1803 //[[[end]]]
1804 This line was not.
1805 """,
1807 'cog1.out': """\
1808 //[[[cog
1809 cog.outl("This line was generated.")
1810 //]]]
1811 //[[[end]]]
1812 This line was not.
1813 """,
1815 'cog1.out2': """\
1816 //[[[cog
1817 cog.outl("This line was generated.")
1818 //]]]
1819 This line was generated.
1820 //[[[end]]]
1821 This line was not.
1822 """,
1823 }
1825 makeFiles(d)
1826 # Remove generated output.
1827 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt'])
1828 self.assertFilesSame('cog1.txt', 'cog1.out')
1829 self.newCog()
1830 # Regenerate the generated output.
1831 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
1832 self.assertFilesSame('cog1.txt', 'cog1.out2')
1833 self.newCog()
1834 # Remove the generated output again.
1835 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt'])
1836 self.assertFilesSame('cog1.txt', 'cog1.out')
1838 def testMsgCall(self):
1839 infile = """\
1840 #[[[cog
1841 cog.msg("Hello there!")
1842 #]]]
1843 #[[[end]]]
1844 """
1845 infile = reindentBlock(infile)
1846 self.assertEqual(self.cog.processString(infile), infile)
1847 output = self.output.getvalue()
1848 self.assertEqual(output, "Message: Hello there!\n")
1850 def testErrorMessageHasNoTraceback(self):
1851 # Test that a Cog error is printed to stderr with no traceback.
1853 d = {
1854 'cog1.txt': """\
1855 //[[[cog
1856 cog.outl("This line was newly")
1857 cog.outl("generated by cog")
1858 cog.outl("blah blah.")
1859 //]]]
1860 Xhis line was newly
1861 generated by cog
1862 blah blah.
1863 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
1864 """,
1865 }
1867 makeFiles(d)
1868 stderr = StringIO()
1869 self.cog.setOutput(stderr=stderr)
1870 self.cog.main(['argv0', '-c', '-r', "cog1.txt"])
1871 self.assertEqual(self.output.getvalue(), "Cogging cog1.txt\n")
1872 self.assertEqual(stderr.getvalue(), "cog1.txt(9): Output has been edited! Delete old checksum to unprotect.\n")
1874 def testDashD(self):
1875 d = {
1876 'test.cog': """\
1877 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1878 --[[[end]]]
1879 """,
1881 'test.kablooey': """\
1882 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1883 Defined fooey as kablooey
1884 --[[[end]]]
1885 """,
1887 'test.einstein': """\
1888 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1889 Defined fooey as e=mc2
1890 --[[[end]]]
1891 """,
1892 }
1894 makeFiles(d)
1895 self.cog.callableMain(['argv0', '-r', '-D', 'fooey=kablooey', 'test.cog'])
1896 self.assertFilesSame('test.cog', 'test.kablooey')
1897 makeFiles(d)
1898 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', 'test.cog'])
1899 self.assertFilesSame('test.cog', 'test.kablooey')
1900 makeFiles(d)
1901 self.cog.callableMain(['argv0', '-r', '-Dfooey=e=mc2', 'test.cog'])
1902 self.assertFilesSame('test.cog', 'test.einstein')
1903 makeFiles(d)
1904 self.cog.callableMain(['argv0', '-r', '-Dbar=quux', '-Dfooey=kablooey', 'test.cog'])
1905 self.assertFilesSame('test.cog', 'test.kablooey')
1906 makeFiles(d)
1907 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', '-Dbar=quux', 'test.cog'])
1908 self.assertFilesSame('test.cog', 'test.kablooey')
1909 makeFiles(d)
1910 self.cog.callableMain(['argv0', '-r', '-Dfooey=gooey', '-Dfooey=kablooey', 'test.cog'])
1911 self.assertFilesSame('test.cog', 'test.kablooey')
1913 def testOutputToStdout(self):
1914 d = {
1915 'test.cog': """\
1916 --[[[cog cog.outl('Hey there!') ]]]
1917 --[[[end]]]
1918 """
1919 }
1921 makeFiles(d)
1922 stderr = StringIO()
1923 self.cog.setOutput(stderr=stderr)
1924 self.cog.callableMain(['argv0', 'test.cog'])
1925 output = self.output.getvalue()
1926 outerr = stderr.getvalue()
1927 self.assertEqual(output, "--[[[cog cog.outl('Hey there!') ]]]\nHey there!\n--[[[end]]]\n")
1928 self.assertEqual(outerr, "")
1930 def testReadFromStdin(self):
1931 stdin = StringIO("--[[[cog cog.outl('Wow') ]]]\n--[[[end]]]\n")
1932 def restore_stdin(old_stdin):
1933 sys.stdin = old_stdin
1934 self.addCleanup(restore_stdin, sys.stdin)
1935 sys.stdin = stdin
1937 stderr = StringIO()
1938 self.cog.setOutput(stderr=stderr)
1939 self.cog.callableMain(['argv0', '-'])
1940 output = self.output.getvalue()
1941 outerr = stderr.getvalue()
1942 self.assertEqual(output, "--[[[cog cog.outl('Wow') ]]]\nWow\n--[[[end]]]\n")
1943 self.assertEqual(outerr, "")
1945 def testSuffixOutputLines(self):
1946 d = {
1947 'test.cog': """\
1948 Hey there.
1949 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
1950 ;[[[end]]]
1951 Good bye.
1952 """,
1954 'test.out': """\
1955 Hey there.
1956 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
1957 a (foo)
1958 b (foo)
1959 """ # These three trailing spaces are important.
1960 # The suffix is not applied to completely blank lines.
1961 """
1962 c (foo)
1963 ;[[[end]]]
1964 Good bye.
1965 """,
1966 }
1968 makeFiles(d)
1969 self.cog.callableMain(['argv0', '-r', '-s', ' (foo)', 'test.cog'])
1970 self.assertFilesSame('test.cog', 'test.out')
1972 def testEmptySuffix(self):
1973 d = {
1974 'test.cog': """\
1975 ;[[[cog cog.outl('a\\nb\\nc') ]]]
1976 ;[[[end]]]
1977 """,
1979 'test.out': """\
1980 ;[[[cog cog.outl('a\\nb\\nc') ]]]
1981 a
1982 b
1983 c
1984 ;[[[end]]]
1985 """,
1986 }
1988 makeFiles(d)
1989 self.cog.callableMain(['argv0', '-r', '-s', '', 'test.cog'])
1990 self.assertFilesSame('test.cog', 'test.out')
1992 def testHellishSuffix(self):
1993 d = {
1994 'test.cog': """\
1995 ;[[[cog cog.outl('a\\n\\nb') ]]]
1996 """,
1998 'test.out': """\
1999 ;[[[cog cog.outl('a\\n\\nb') ]]]
2000 a /\\n*+([)]><
2002 b /\\n*+([)]><
2003 """,
2004 }
2006 makeFiles(d)
2007 self.cog.callableMain(['argv0', '-z', '-r', '-s', r' /\n*+([)]><', 'test.cog'])
2008 self.assertFilesSame('test.cog', 'test.out')
2010 def testPrologue(self):
2011 d = {
2012 'test.cog': """\
2013 Some text.
2014 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
2015 //[[[end]]]
2016 epilogue.
2017 """,
2019 'test.out': """\
2020 Some text.
2021 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
2022 1.4142135623
2023 //[[[end]]]
2024 epilogue.
2025 """,
2026 }
2028 makeFiles(d)
2029 self.cog.callableMain(['argv0', '-r', '-p', 'import math', 'test.cog'])
2030 self.assertFilesSame('test.cog', 'test.out')
2032 def testThreads(self):
2033 # Test that the implicitly imported cog module is actually different for
2034 # different threads.
2035 numthreads = 20
2037 d = {}
2038 for i in range(numthreads):
2039 d['f{}.cog'.format(i)] = (
2040 "x\n" * i +
2041 "[[[cog\n" +
2042 "assert cog.firstLineNum == int(FIRST) == {}\n".format(i+1) +
2043 "]]]\n" +
2044 "[[[end]]]\n"
2045 )
2046 makeFiles(d)
2048 results = []
2050 def thread_main(num):
2051 try:
2052 ret = Cog().main(
2053 ['cog.py', '-r', '-D', 'FIRST={}'.format(num+1), 'f{}.cog'.format(num)]
2054 )
2055 assert ret == 0
2056 except Exception as exc: # pragma: no cover (only happens on test failure)
2057 results.append(exc)
2058 else:
2059 results.append(None)
2061 ts = [threading.Thread(target=thread_main, args=(i,)) for i in range(numthreads)]
2062 for t in ts:
2063 t.start()
2064 for t in ts:
2065 t.join()
2066 assert results == [None] * numthreads
2069class WritabilityTests(TestCaseWithTempDir):
2071 d = {
2072 'test.cog': """\
2073 //[[[cog
2074 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
2075 cog.outl("void %s();" % fn)
2076 //]]]
2077 //[[[end]]]
2078 """,
2080 'test.out': """\
2081 //[[[cog
2082 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
2083 cog.outl("void %s();" % fn)
2084 //]]]
2085 void DoSomething();
2086 void DoAnotherThing();
2087 void DoLastThing();
2088 //[[[end]]]
2089 """,
2090 }
2092 if os.name == 'nt': #pragma: no cover
2093 # for Windows
2094 cmd_w_args = 'attrib -R %s'
2095 cmd_w_asterisk = 'attrib -R *'
2096 else: #pragma: no cover
2097 # for unix-like
2098 cmd_w_args = 'chmod +w %s'
2099 cmd_w_asterisk = 'chmod +w *'
2101 def setUp(self):
2102 super(WritabilityTests, self).setUp()
2103 makeFiles(self.d)
2104 self.testcog = os.path.join(self.tempdir, 'test.cog')
2105 os.chmod(self.testcog, stat.S_IREAD) # Make the file readonly.
2106 assert not os.access(self.testcog, os.W_OK)
2108 def tearDown(self):
2109 os.chmod(self.testcog, stat.S_IWRITE) # Make the file writable again.
2110 super(WritabilityTests, self).tearDown()
2112 def testReadonlyNoCommand(self):
2113 with self.assertRaisesRegex(CogError, "^Can't overwrite test.cog$"):
2114 self.cog.callableMain(['argv0', '-r', 'test.cog'])
2115 assert not os.access(self.testcog, os.W_OK)
2117 def testReadonlyWithCommand(self):
2118 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_args, 'test.cog'])
2119 self.assertFilesSame('test.cog', 'test.out')
2120 assert os.access(self.testcog, os.W_OK)
2122 def testReadonlyWithCommandWithNoSlot(self):
2123 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_asterisk, 'test.cog'])
2124 self.assertFilesSame('test.cog', 'test.out')
2125 assert os.access(self.testcog, os.W_OK)
2127 def testReadonlyWithIneffectualCommand(self):
2128 with self.assertRaisesRegex(CogError, "^Couldn't make test.cog writable$"):
2129 self.cog.callableMain(['argv0', '-r', '-w', 'echo %s', 'test.cog'])
2130 assert not os.access(self.testcog, os.W_OK)
2133class ChecksumTests(TestCaseWithTempDir):
2135 def testCreateChecksumOutput(self):
2136 d = {
2137 'cog1.txt': """\
2138 //[[[cog
2139 cog.outl("This line was generated.")
2140 //]]]
2141 This line was generated.
2142 //[[[end]]]
2143 This line was not.
2144 """,
2146 'cog1.out': """\
2147 //[[[cog
2148 cog.outl("This line was generated.")
2149 //]]]
2150 This line was generated.
2151 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
2152 This line was not.
2153 """,
2154 }
2156 makeFiles(d)
2157 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt'])
2158 self.assertFilesSame('cog1.txt', 'cog1.out')
2160 def testCheckChecksumOutput(self):
2161 d = {
2162 'cog1.txt': """\
2163 //[[[cog
2164 cog.outl("This line was newly")
2165 cog.outl("generated by cog")
2166 cog.outl("blah blah.")
2167 //]]]
2168 This line was generated.
2169 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
2170 """,
2172 'cog1.out': """\
2173 //[[[cog
2174 cog.outl("This line was newly")
2175 cog.outl("generated by cog")
2176 cog.outl("blah blah.")
2177 //]]]
2178 This line was newly
2179 generated by cog
2180 blah blah.
2181 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2182 """,
2183 }
2185 makeFiles(d)
2186 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt'])
2187 self.assertFilesSame('cog1.txt', 'cog1.out')
2189 def testRemoveChecksumOutput(self):
2190 d = {
2191 'cog1.txt': """\
2192 //[[[cog
2193 cog.outl("This line was newly")
2194 cog.outl("generated by cog")
2195 cog.outl("blah blah.")
2196 //]]]
2197 This line was generated.
2198 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) fooey
2199 """,
2201 'cog1.out': """\
2202 //[[[cog
2203 cog.outl("This line was newly")
2204 cog.outl("generated by cog")
2205 cog.outl("blah blah.")
2206 //]]]
2207 This line was newly
2208 generated by cog
2209 blah blah.
2210 //[[[end]]] fooey
2211 """,
2212 }
2214 makeFiles(d)
2215 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
2216 self.assertFilesSame('cog1.txt', 'cog1.out')
2218 def testTamperedChecksumOutput(self):
2219 d = {
2220 'cog1.txt': """\
2221 //[[[cog
2222 cog.outl("This line was newly")
2223 cog.outl("generated by cog")
2224 cog.outl("blah blah.")
2225 //]]]
2226 Xhis line was newly
2227 generated by cog
2228 blah blah.
2229 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2230 """,
2232 'cog2.txt': """\
2233 //[[[cog
2234 cog.outl("This line was newly")
2235 cog.outl("generated by cog")
2236 cog.outl("blah blah.")
2237 //]]]
2238 This line was newly
2239 generated by cog
2240 blah blah!
2241 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2242 """,
2244 'cog3.txt': """\
2245 //[[[cog
2246 cog.outl("This line was newly")
2247 cog.outl("generated by cog")
2248 cog.outl("blah blah.")
2249 //]]]
2251 This line was newly
2252 generated by cog
2253 blah blah.
2254 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2255 """,
2257 'cog4.txt': """\
2258 //[[[cog
2259 cog.outl("This line was newly")
2260 cog.outl("generated by cog")
2261 cog.outl("blah blah.")
2262 //]]]
2263 This line was newly
2264 generated by cog
2265 blah blah..
2266 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2267 """,
2269 'cog5.txt': """\
2270 //[[[cog
2271 cog.outl("This line was newly")
2272 cog.outl("generated by cog")
2273 cog.outl("blah blah.")
2274 //]]]
2275 This line was newly
2276 generated by cog
2277 blah blah.
2278 extra
2279 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2280 """,
2282 'cog6.txt': """\
2283 //[[[cog
2284 cog.outl("This line was newly")
2285 cog.outl("generated by cog")
2286 cog.outl("blah blah.")
2287 //]]]
2288 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2289 """,
2290 }
2292 makeFiles(d)
2293 with self.assertRaisesRegex(CogError,
2294 r"^cog1.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
2295 self.cog.callableMain(['argv0', '-c', "cog1.txt"])
2296 with self.assertRaisesRegex(CogError,
2297 r"^cog2.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
2298 self.cog.callableMain(['argv0', '-c', "cog2.txt"])
2299 with self.assertRaisesRegex(CogError,
2300 r"^cog3.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"):
2301 self.cog.callableMain(['argv0', '-c', "cog3.txt"])
2302 with self.assertRaisesRegex(CogError,
2303 r"^cog4.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
2304 self.cog.callableMain(['argv0', '-c', "cog4.txt"])
2305 with self.assertRaisesRegex(CogError,
2306 r"^cog5.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"):
2307 self.cog.callableMain(['argv0', '-c', "cog5.txt"])
2308 with self.assertRaisesRegex(CogError,
2309 r"^cog6.txt\(6\): Output has been edited! Delete old checksum to unprotect.$"):
2310 self.cog.callableMain(['argv0', '-c', "cog6.txt"])
2312 def testArgvIsntModified(self):
2313 argv = ['argv0', '-v']
2314 orig_argv = argv[:]
2315 self.cog.callableMain(argv)
2316 self.assertEqual(argv, orig_argv)
2319class CustomMarkerTests(TestCaseWithTempDir):
2321 def testCustomerMarkers(self):
2322 d = {
2323 'test.cog': """\
2324 //{{
2325 cog.outl("void %s();" % "MyFunction")
2326 //}}
2327 //{{end}}
2328 """,
2330 'test.out': """\
2331 //{{
2332 cog.outl("void %s();" % "MyFunction")
2333 //}}
2334 void MyFunction();
2335 //{{end}}
2336 """,
2337 }
2339 makeFiles(d)
2340 self.cog.callableMain([
2341 'argv0', '-r',
2342 '--markers={{ }} {{end}}',
2343 'test.cog'
2344 ])
2345 self.assertFilesSame('test.cog', 'test.out')
2347 def testTrulyWackyMarkers(self):
2348 # Make sure the markers are properly re-escaped.
2349 d = {
2350 'test.cog': """\
2351 //**(
2352 cog.outl("void %s();" % "MyFunction")
2353 //**)
2354 //**(end)**
2355 """,
2357 'test.out': """\
2358 //**(
2359 cog.outl("void %s();" % "MyFunction")
2360 //**)
2361 void MyFunction();
2362 //**(end)**
2363 """,
2364 }
2366 makeFiles(d)
2367 self.cog.callableMain([
2368 'argv0', '-r',
2369 '--markers=**( **) **(end)**',
2370 'test.cog'
2371 ])
2372 self.assertFilesSame('test.cog', 'test.out')
2374 def testChangeJustOneMarker(self):
2375 d = {
2376 'test.cog': """\
2377 //**(
2378 cog.outl("void %s();" % "MyFunction")
2379 //]]]
2380 //[[[end]]]
2381 """,
2383 'test.out': """\
2384 //**(
2385 cog.outl("void %s();" % "MyFunction")
2386 //]]]
2387 void MyFunction();
2388 //[[[end]]]
2389 """,
2390 }
2392 makeFiles(d)
2393 self.cog.callableMain([
2394 'argv0', '-r',
2395 '--markers=**( ]]] [[[end]]]',
2396 'test.cog'
2397 ])
2398 self.assertFilesSame('test.cog', 'test.out')
2401class BlakeTests(TestCaseWithTempDir):
2403 # Blake Winton's contributions.
2404 def testDeleteCode(self):
2405 # -o sets the output file.
2406 d = {
2407 'test.cog': """\
2408 // This is my C++ file.
2409 //[[[cog
2410 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
2411 for fn in fnames:
2412 cog.outl("void %s();" % fn)
2413 //]]]
2414 Some Sample Code Here
2415 //[[[end]]]Data Data
2416 And Some More
2417 """,
2419 'test.out': """\
2420 // This is my C++ file.
2421 void DoSomething();
2422 void DoAnotherThing();
2423 void DoLastThing();
2424 And Some More
2425 """,
2426 }
2428 makeFiles(d)
2429 self.cog.callableMain(['argv0', '-d', '-o', 'test.cogged', 'test.cog'])
2430 self.assertFilesSame('test.cogged', 'test.out')
2432 def testDeleteCodeWithDashRFails(self):
2433 d = {
2434 'test.cog': """\
2435 // This is my C++ file.
2436 """
2437 }
2439 makeFiles(d)
2440 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"):
2441 self.cog.callableMain(['argv0', '-r', '-d', 'test.cog'])
2443 def testSettingGlobals(self):
2444 # Blake Winton contributed a way to set the globals that will be used in
2445 # processFile().
2446 d = {
2447 'test.cog': """\
2448 // This is my C++ file.
2449 //[[[cog
2450 for fn in fnames:
2451 cog.outl("void %s();" % fn)
2452 //]]]
2453 Some Sample Code Here
2454 //[[[end]]]""",
2456 'test.out': """\
2457 // This is my C++ file.
2458 void DoBlake();
2459 void DoWinton();
2460 void DoContribution();
2461 """,
2462 }
2464 makeFiles(d)
2465 globals = {}
2466 globals['fnames'] = ['DoBlake', 'DoWinton', 'DoContribution']
2467 self.cog.options.bDeleteCode = True
2468 self.cog.processFile('test.cog', 'test.cogged', globals=globals)
2469 self.assertFilesSame('test.cogged', 'test.out')
2472class ErrorCallTests(TestCaseWithTempDir):
2474 def testErrorCallHasNoTraceback(self):
2475 # Test that cog.error() doesn't show a traceback.
2476 d = {
2477 'error.cog': """\
2478 //[[[cog
2479 cog.error("Something Bad!")
2480 //]]]
2481 //[[[end]]]
2482 """,
2483 }
2485 makeFiles(d)
2486 self.cog.main(['argv0', '-r', 'error.cog'])
2487 output = self.output.getvalue()
2488 self.assertEqual(output, "Cogging error.cog\nError: Something Bad!\n")
2490 def testRealErrorHasTraceback(self):
2491 # Test that a genuine error does show a traceback.
2492 d = {
2493 'error.cog': """\
2494 //[[[cog
2495 raise RuntimeError("Hey!")
2496 //]]]
2497 //[[[end]]]
2498 """,
2499 }
2501 makeFiles(d)
2502 self.cog.main(['argv0', '-r', 'error.cog'])
2503 output = self.output.getvalue()
2504 msg = 'Actual output:\n' + output
2505 self.assertTrue(output.startswith("Cogging error.cog\nTraceback (most recent"), msg)
2506 self.assertIn("RuntimeError: Hey!", output)
2509# Things not yet tested:
2510# - A bad -w command (currently fails silently).