Coverage for cogapp/test_cogapp.py: 29.63%
854 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-23 18:50 -0500
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-23 18:50 -0500
1"""Test cogapp."""
3import io
4import os
5import os.path
6import random
7import re
8import shutil
9import stat
10import sys
11import tempfile
12import threading
13from unittest import TestCase
15from .cogapp import Cog, CogOptions, CogGenerator
16from .cogapp import CogError, CogUsageError, CogGeneratedError, CogUserException
17from .cogapp import usage, __version__, main
18from .makefiles import make_files
19from .whiteutils import reindent_block
22class CogTestsInMemory(TestCase):
23 """Test cases for cogapp.Cog()"""
25 def test_no_cog(self):
26 strings = [
27 "",
28 " ",
29 " \t \t \tx",
30 "hello",
31 "the cat\nin the\nhat.",
32 "Horton\n\tHears A\n\t\tWho",
33 ]
34 for s in strings:
35 self.assertEqual(Cog().process_string(s), s)
37 def test_simple(self):
38 infile = """\
39 Some text.
40 //[[[cog
41 import cog
42 cog.outl("This is line one\\n")
43 cog.outl("This is line two")
44 //]]]
45 gobbledegook.
46 //[[[end]]]
47 epilogue.
48 """
50 outfile = """\
51 Some text.
52 //[[[cog
53 import cog
54 cog.outl("This is line one\\n")
55 cog.outl("This is line two")
56 //]]]
57 This is line one
59 This is line two
60 //[[[end]]]
61 epilogue.
62 """
64 self.assertEqual(Cog().process_string(infile), outfile)
66 def test_empty_cog(self):
67 # The cog clause can be totally empty. Not sure why you'd want it,
68 # but it works.
69 infile = """\
70 hello
71 //[[[cog
72 //]]]
73 //[[[end]]]
74 goodbye
75 """
77 infile = reindent_block(infile)
78 self.assertEqual(Cog().process_string(infile), infile)
80 def test_multiple_cogs(self):
81 # One file can have many cog chunks, even abutting each other.
82 infile = """\
83 //[[[cog
84 cog.out("chunk1")
85 //]]]
86 chunk1
87 //[[[end]]]
88 //[[[cog
89 cog.out("chunk2")
90 //]]]
91 chunk2
92 //[[[end]]]
93 between chunks
94 //[[[cog
95 cog.out("chunk3")
96 //]]]
97 chunk3
98 //[[[end]]]
99 """
101 infile = reindent_block(infile)
102 self.assertEqual(Cog().process_string(infile), infile)
104 def test_trim_blank_lines(self):
105 infile = """\
106 //[[[cog
107 cog.out("This is line one\\n", trimblanklines=True)
108 cog.out('''
109 This is line two
110 ''', dedent=True, trimblanklines=True)
111 cog.outl("This is line three", trimblanklines=True)
112 //]]]
113 This is line one
114 This is line two
115 This is line three
116 //[[[end]]]
117 """
119 infile = reindent_block(infile)
120 self.assertEqual(Cog().process_string(infile), infile)
122 def test_trim_empty_blank_lines(self):
123 infile = """\
124 //[[[cog
125 cog.out("This is line one\\n", trimblanklines=True)
126 cog.out('''
127 This is line two
128 ''', dedent=True, trimblanklines=True)
129 cog.out('', dedent=True, trimblanklines=True)
130 cog.outl("This is line three", trimblanklines=True)
131 //]]]
132 This is line one
133 This is line two
134 This is line three
135 //[[[end]]]
136 """
138 infile = reindent_block(infile)
139 self.assertEqual(Cog().process_string(infile), infile)
141 def test_trim_blank_lines_with_last_partial(self):
142 infile = """\
143 //[[[cog
144 cog.out("This is line one\\n", trimblanklines=True)
145 cog.out("\\nLine two\\nLine three", trimblanklines=True)
146 //]]]
147 This is line one
148 Line two
149 Line three
150 //[[[end]]]
151 """
153 infile = reindent_block(infile)
154 self.assertEqual(Cog().process_string(infile), infile)
156 def test_cog_out_dedent(self):
157 infile = """\
158 //[[[cog
159 cog.out("This is the first line\\n")
160 cog.out('''
161 This is dedent=True 1
162 This is dedent=True 2
163 ''', dedent=True, trimblanklines=True)
164 cog.out('''
165 This is dedent=False 1
166 This is dedent=False 2
167 ''', dedent=False, trimblanklines=True)
168 cog.out('''
169 This is dedent=default 1
170 This is dedent=default 2
171 ''', trimblanklines=True)
172 cog.out("This is the last line\\n")
173 //]]]
174 This is the first line
175 This is dedent=True 1
176 This is dedent=True 2
177 This is dedent=False 1
178 This is dedent=False 2
179 This is dedent=default 1
180 This is dedent=default 2
181 This is the last line
182 //[[[end]]]
183 """
185 infile = reindent_block(infile)
186 self.assertEqual(Cog().process_string(infile), infile)
188 def test22_end_of_line(self):
189 # In Python 2.2, this cog file was not parsing because the
190 # last line is indented but didn't end with a newline.
191 infile = """\
192 //[[[cog
193 import cog
194 for i in range(3):
195 cog.out("%d\\n" % i)
196 //]]]
197 0
198 1
199 2
200 //[[[end]]]
201 """
203 infile = reindent_block(infile)
204 self.assertEqual(Cog().process_string(infile), infile)
206 def test_indented_code(self):
207 infile = """\
208 first line
209 [[[cog
210 import cog
211 for i in range(3):
212 cog.out("xx%d\\n" % i)
213 ]]]
214 xx0
215 xx1
216 xx2
217 [[[end]]]
218 last line
219 """
221 infile = reindent_block(infile)
222 self.assertEqual(Cog().process_string(infile), infile)
224 def test_prefixed_code(self):
225 infile = """\
226 --[[[cog
227 --import cog
228 --for i in range(3):
229 -- cog.out("xx%d\\n" % i)
230 --]]]
231 xx0
232 xx1
233 xx2
234 --[[[end]]]
235 """
237 infile = reindent_block(infile)
238 self.assertEqual(Cog().process_string(infile), infile)
240 def test_prefixed_indented_code(self):
241 infile = """\
242 prologue
243 --[[[cog
244 -- import cog
245 -- for i in range(3):
246 -- cog.out("xy%d\\n" % i)
247 --]]]
248 xy0
249 xy1
250 xy2
251 --[[[end]]]
252 """
254 infile = reindent_block(infile)
255 self.assertEqual(Cog().process_string(infile), infile)
257 def test_bogus_prefix_match(self):
258 infile = """\
259 prologue
260 #[[[cog
261 import cog
262 # This comment should not be clobbered by removing the pound sign.
263 for i in range(3):
264 cog.out("xy%d\\n" % i)
265 #]]]
266 xy0
267 xy1
268 xy2
269 #[[[end]]]
270 """
272 infile = reindent_block(infile)
273 self.assertEqual(Cog().process_string(infile), infile)
275 def test_no_final_newline(self):
276 # If the cog'ed output has no final newline,
277 # it shouldn't eat up the cog terminator.
278 infile = """\
279 prologue
280 [[[cog
281 import cog
282 for i in range(3):
283 cog.out("%d" % i)
284 ]]]
285 012
286 [[[end]]]
287 epilogue
288 """
290 infile = reindent_block(infile)
291 self.assertEqual(Cog().process_string(infile), infile)
293 def test_no_output_at_all(self):
294 # If there is absolutely no cog output, that's ok.
295 infile = """\
296 prologue
297 [[[cog
298 i = 1
299 ]]]
300 [[[end]]]
301 epilogue
302 """
304 infile = reindent_block(infile)
305 self.assertEqual(Cog().process_string(infile), infile)
307 def test_purely_blank_line(self):
308 # If there is a blank line in the cog code with no whitespace
309 # prefix, that should be OK.
311 infile = """\
312 prologue
313 [[[cog
314 import sys
315 cog.out("Hello")
316 $
317 cog.out("There")
318 ]]]
319 HelloThere
320 [[[end]]]
321 epilogue
322 """
324 infile = reindent_block(infile.replace("$", ""))
325 self.assertEqual(Cog().process_string(infile), infile)
327 def test_empty_outl(self):
328 # Alexander Belchenko suggested the string argument to outl should
329 # be optional. Does it work?
331 infile = """\
332 prologue
333 [[[cog
334 cog.outl("x")
335 cog.outl()
336 cog.outl("y")
337 cog.out() # Also optional, a complete no-op.
338 cog.outl(trimblanklines=True)
339 cog.outl("z")
340 ]]]
341 x
343 y
345 z
346 [[[end]]]
347 epilogue
348 """
350 infile = reindent_block(infile)
351 self.assertEqual(Cog().process_string(infile), infile)
353 def test_first_line_num(self):
354 infile = """\
355 fooey
356 [[[cog
357 cog.outl("started at line number %d" % cog.firstLineNum)
358 ]]]
359 started at line number 2
360 [[[end]]]
361 blah blah
362 [[[cog
363 cog.outl("and again at line %d" % cog.firstLineNum)
364 ]]]
365 and again at line 8
366 [[[end]]]
367 """
369 infile = reindent_block(infile)
370 self.assertEqual(Cog().process_string(infile), infile)
372 def test_compact_one_line_code(self):
373 infile = """\
374 first line
375 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
376 get rid of this!
377 [[[end]]]
378 last line
379 """
381 outfile = """\
382 first line
383 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
384 hello 81
385 [[[end]]]
386 last line
387 """
389 infile = reindent_block(infile)
390 self.assertEqual(Cog().process_string(infile), reindent_block(outfile))
392 def test_inside_out_compact(self):
393 infile = """\
394 first line
395 hey?: ]]] what is this? [[[cog strange!
396 get rid of this!
397 [[[end]]]
398 last line
399 """
400 with self.assertRaisesRegex(
401 CogError, r"^infile.txt\(2\): Cog code markers inverted$"
402 ):
403 Cog().process_string(reindent_block(infile), "infile.txt")
405 def test_sharing_globals(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 = reindent_block(infile)
428 self.assertEqual(Cog().process_string(infile), reindent_block(outfile))
430 def test_assert_in_cog_code(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 = reindent_block(infile)
439 with self.assertRaisesRegex(CogUserException, "AssertionError: Oops"):
440 Cog().process_string(infile)
442 def test_cog_previous(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 = reindent_block(infile)
466 self.assertEqual(Cog().process_string(infile), reindent_block(outfile))
469class CogOptionsTests(TestCase):
470 """Test the CogOptions class."""
472 def test_equality(self):
473 o = CogOptions()
474 p = CogOptions()
475 self.assertEqual(o, p)
476 o.parse_args(["-r"])
477 self.assertNotEqual(o, p)
478 p.parse_args(["-r"])
479 self.assertEqual(o, p)
481 def test_cloning(self):
482 o = CogOptions()
483 o.parse_args(["-I", "fooey", "-I", "booey", "-s", " /*x*/"])
484 p = o.clone()
485 self.assertEqual(o, p)
486 p.parse_args(["-I", "huey", "-D", "foo=quux"])
487 self.assertNotEqual(o, p)
488 q = CogOptions()
489 q.parse_args(
490 [
491 "-I",
492 "fooey",
493 "-I",
494 "booey",
495 "-s",
496 " /*x*/",
497 "-I",
498 "huey",
499 "-D",
500 "foo=quux",
501 ]
502 )
503 self.assertEqual(p, q)
505 def test_combining_flags(self):
506 # Single-character flags can be combined.
507 o = CogOptions()
508 o.parse_args(["-e", "-r", "-z"])
509 p = CogOptions()
510 p.parse_args(["-erz"])
511 self.assertEqual(o, p)
513 def test_markers(self):
514 o = CogOptions()
515 o._parse_markers("a b c")
516 self.assertEqual("a", o.begin_spec)
517 self.assertEqual("b", o.end_spec)
518 self.assertEqual("c", o.end_output)
520 def test_markers_switch(self):
521 o = CogOptions()
522 o.parse_args(["--markers", "a b c"])
523 self.assertEqual("a", o.begin_spec)
524 self.assertEqual("b", o.end_spec)
525 self.assertEqual("c", o.end_output)
528class FileStructureTests(TestCase):
529 """Test that we're properly strict about the structure of files."""
531 def is_bad(self, infile, msg=None):
532 infile = reindent_block(infile)
533 with self.assertRaisesRegex(CogError, "^" + re.escape(msg) + "$"):
534 Cog().process_string(infile, "infile.txt")
536 def test_begin_no_end(self):
537 infile = """\
538 Fooey
539 #[[[cog
540 cog.outl('hello')
541 """
542 self.is_bad(infile, "infile.txt(2): Cog block begun but never ended.")
544 def test_no_eoo(self):
545 infile = """\
546 Fooey
547 #[[[cog
548 cog.outl('hello')
549 #]]]
550 """
551 self.is_bad(infile, "infile.txt(4): Missing '[[[end]]]' before end of file.")
553 infile2 = """\
554 Fooey
555 #[[[cog
556 cog.outl('hello')
557 #]]]
558 #[[[cog
559 cog.outl('goodbye')
560 #]]]
561 """
562 self.is_bad(infile2, "infile.txt(5): Unexpected '[[[cog'")
564 def test_start_with_end(self):
565 infile = """\
566 #]]]
567 """
568 self.is_bad(infile, "infile.txt(1): Unexpected ']]]'")
570 infile2 = """\
571 #[[[cog
572 cog.outl('hello')
573 #]]]
574 #[[[end]]]
575 #]]]
576 """
577 self.is_bad(infile2, "infile.txt(5): Unexpected ']]]'")
579 def test_start_with_eoo(self):
580 infile = """\
581 #[[[end]]]
582 """
583 self.is_bad(infile, "infile.txt(1): Unexpected '[[[end]]]'")
585 infile2 = """\
586 #[[[cog
587 cog.outl('hello')
588 #]]]
589 #[[[end]]]
590 #[[[end]]]
591 """
592 self.is_bad(infile2, "infile.txt(5): Unexpected '[[[end]]]'")
594 def test_no_end(self):
595 infile = """\
596 #[[[cog
597 cog.outl("hello")
598 #[[[end]]]
599 """
600 self.is_bad(infile, "infile.txt(3): Unexpected '[[[end]]]'")
602 infile2 = """\
603 #[[[cog
604 cog.outl('hello')
605 #]]]
606 #[[[end]]]
607 #[[[cog
608 cog.outl("hello")
609 #[[[end]]]
610 """
611 self.is_bad(infile2, "infile.txt(7): Unexpected '[[[end]]]'")
613 def test_two_begins(self):
614 infile = """\
615 #[[[cog
616 #[[[cog
617 cog.outl("hello")
618 #]]]
619 #[[[end]]]
620 """
621 self.is_bad(infile, "infile.txt(2): Unexpected '[[[cog'")
623 infile2 = """\
624 #[[[cog
625 cog.outl("hello")
626 #]]]
627 #[[[end]]]
628 #[[[cog
629 #[[[cog
630 cog.outl("hello")
631 #]]]
632 #[[[end]]]
633 """
634 self.is_bad(infile2, "infile.txt(6): Unexpected '[[[cog'")
636 def test_two_ends(self):
637 infile = """\
638 #[[[cog
639 cog.outl("hello")
640 #]]]
641 #]]]
642 #[[[end]]]
643 """
644 self.is_bad(infile, "infile.txt(4): Unexpected ']]]'")
646 infile2 = """\
647 #[[[cog
648 cog.outl("hello")
649 #]]]
650 #[[[end]]]
651 #[[[cog
652 cog.outl("hello")
653 #]]]
654 #]]]
655 #[[[end]]]
656 """
657 self.is_bad(infile2, "infile.txt(8): Unexpected ']]]'")
660class CogErrorTests(TestCase):
661 """Test cases for cog.error()."""
663 def test_error_msg(self):
664 infile = """\
665 [[[cog cog.error("This ain't right!")]]]
666 [[[end]]]
667 """
669 infile = reindent_block(infile)
670 with self.assertRaisesRegex(CogGeneratedError, "^This ain't right!$"):
671 Cog().process_string(infile)
673 def test_error_no_msg(self):
674 infile = """\
675 [[[cog cog.error()]]]
676 [[[end]]]
677 """
679 infile = reindent_block(infile)
680 with self.assertRaisesRegex(
681 CogGeneratedError, "^Error raised by cog generator.$"
682 ):
683 Cog().process_string(infile)
685 def test_no_error_if_error_not_called(self):
686 infile = """\
687 --[[[cog
688 --import cog
689 --for i in range(3):
690 -- if i > 10:
691 -- cog.error("Something is amiss!")
692 -- cog.out("xx%d\\n" % i)
693 --]]]
694 xx0
695 xx1
696 xx2
697 --[[[end]]]
698 """
700 infile = reindent_block(infile)
701 self.assertEqual(Cog().process_string(infile), infile)
704class CogGeneratorGetCodeTests(TestCase):
705 """Tests for CogGenerator.getCode()."""
707 def setUp(self):
708 # All tests get a generator to use, and short same-length names for
709 # the functions we're going to use.
710 self.gen = CogGenerator()
711 self.m = self.gen.parse_marker
712 self.parse_line = self.gen.parse_line
714 def test_empty(self):
715 self.m("// [[[cog")
716 self.m("// ]]]")
717 self.assertEqual(self.gen.get_code(), "")
719 def test_simple(self):
720 self.m("// [[[cog")
721 self.parse_line(' print "hello"')
722 self.parse_line(' print "bye"')
723 self.m("// ]]]")
724 self.assertEqual(self.gen.get_code(), 'print "hello"\nprint "bye"')
726 def test_compressed1(self):
727 # For a while, I supported compressed code blocks, but no longer.
728 self.m('// [[[cog: print """')
729 self.parse_line("// hello")
730 self.parse_line("// bye")
731 self.m('// """)]]]')
732 self.assertEqual(self.gen.get_code(), "hello\nbye")
734 def test_compressed2(self):
735 # For a while, I supported compressed code blocks, but no longer.
736 self.m('// [[[cog: print """')
737 self.parse_line("hello")
738 self.parse_line("bye")
739 self.m('// """)]]]')
740 self.assertEqual(self.gen.get_code(), "hello\nbye")
742 def test_compressed3(self):
743 # For a while, I supported compressed code blocks, but no longer.
744 self.m("// [[[cog")
745 self.parse_line('print """hello')
746 self.parse_line("bye")
747 self.m('// """)]]]')
748 self.assertEqual(self.gen.get_code(), 'print """hello\nbye')
750 def test_compressed4(self):
751 # For a while, I supported compressed code blocks, but no longer.
752 self.m('// [[[cog: print """')
753 self.parse_line("hello")
754 self.parse_line('bye""")')
755 self.m("// ]]]")
756 self.assertEqual(self.gen.get_code(), 'hello\nbye""")')
758 def test_no_common_prefix_for_markers(self):
759 # It's important to be able to use #if 0 to hide lines from a
760 # C++ compiler.
761 self.m("#if 0 //[[[cog")
762 self.parse_line("\timport cog, sys")
763 self.parse_line("")
764 self.parse_line("\tprint sys.argv")
765 self.m("#endif //]]]")
766 self.assertEqual(self.gen.get_code(), "import cog, sys\n\nprint sys.argv")
769class TestCaseWithTempDir(TestCase):
770 def new_cog(self):
771 """Initialize the cog members for another run."""
772 # Create a cog engine, and catch its output.
773 self.cog = Cog()
774 self.output = io.StringIO()
775 self.cog.set_output(stdout=self.output, stderr=self.output)
777 def setUp(self):
778 # Create a temporary directory.
779 self.tempdir = os.path.join(
780 tempfile.gettempdir(), "testcog_tempdir_" + str(random.random())[2:]
781 )
782 os.mkdir(self.tempdir)
783 self.olddir = os.getcwd()
784 os.chdir(self.tempdir)
785 self.new_cog()
787 def tearDown(self):
788 os.chdir(self.olddir)
789 # Get rid of the temporary directory.
790 shutil.rmtree(self.tempdir)
792 def assertFilesSame(self, file_name1, file_name2):
793 with open(os.path.join(self.tempdir, file_name1), "rb") as f1:
794 text1 = f1.read()
795 with open(os.path.join(self.tempdir, file_name2), "rb") as f2:
796 text2 = f2.read()
797 self.assertEqual(text1, text2)
799 def assertFileContent(self, fname, content):
800 absname = os.path.join(self.tempdir, fname)
801 with open(absname, "rb") as f:
802 file_content = f.read()
803 self.assertEqual(file_content, content.encode("utf-8"))
806class ArgumentHandlingTests(TestCaseWithTempDir):
807 def test_argument_failure(self):
808 # Return value 2 means usage problem.
809 self.assertEqual(self.cog.main(["argv0", "-j"]), 2)
810 output = self.output.getvalue()
811 self.assertIn("option -j not recognized", output)
812 with self.assertRaisesRegex(CogUsageError, r"^No files to process$"):
813 self.cog.callable_main(["argv0"])
814 with self.assertRaisesRegex(CogUsageError, r"^option -j not recognized$"):
815 self.cog.callable_main(["argv0", "-j"])
817 def test_no_dash_o_and_at_file(self):
818 make_files({"cogfiles.txt": "# Please run cog"})
819 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with @file$"):
820 self.cog.callable_main(["argv0", "-o", "foo", "@cogfiles.txt"])
822 def test_no_dash_o_and_amp_file(self):
823 make_files({"cogfiles.txt": "# Please run cog"})
824 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with &file$"):
825 self.cog.callable_main(["argv0", "-o", "foo", "&cogfiles.txt"])
827 def test_dash_v(self):
828 self.assertEqual(self.cog.main(["argv0", "-v"]), 0)
829 output = self.output.getvalue()
830 self.assertEqual("Cog version %s\n" % __version__, output)
832 def produces_help(self, args):
833 self.new_cog()
834 argv = ["argv0"] + args.split()
835 self.assertEqual(self.cog.main(argv), 0)
836 self.assertEqual(usage, self.output.getvalue())
838 def test_dash_h(self):
839 # -h or -? anywhere on the command line should just print help.
840 self.produces_help("-h")
841 self.produces_help("-?")
842 self.produces_help("fooey.txt -h")
843 self.produces_help("-o -r @fooey.txt -? @booey.txt")
845 def test_dash_o_and_dash_r(self):
846 d = {
847 "cogfile.txt": """\
848 # Please run cog
849 """
850 }
852 make_files(d)
853 with self.assertRaisesRegex(
854 CogUsageError, r"^Can't use -o with -r \(they are opposites\)$"
855 ):
856 self.cog.callable_main(["argv0", "-o", "foo", "-r", "cogfile.txt"])
858 def test_dash_z(self):
859 d = {
860 "test.cog": """\
861 // This is my C++ file.
862 //[[[cog
863 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
864 for fn in fnames:
865 cog.outl("void %s();" % fn)
866 //]]]
867 """,
868 "test.out": """\
869 // This is my C++ file.
870 //[[[cog
871 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
872 for fn in fnames:
873 cog.outl("void %s();" % fn)
874 //]]]
875 void DoSomething();
876 void DoAnotherThing();
877 void DoLastThing();
878 """,
879 }
881 make_files(d)
882 with self.assertRaisesRegex(
883 CogError, r"^test.cog\(6\): Missing '\[\[\[end\]\]\]' before end of file.$"
884 ):
885 self.cog.callable_main(["argv0", "-r", "test.cog"])
886 self.new_cog()
887 self.cog.callable_main(["argv0", "-r", "-z", "test.cog"])
888 self.assertFilesSame("test.cog", "test.out")
890 def test_bad_dash_d(self):
891 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
892 self.cog.callable_main(["argv0", "-Dfooey", "cog.txt"])
893 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
894 self.cog.callable_main(["argv0", "-D", "fooey", "cog.txt"])
896 def test_bad_markers(self):
897 with self.assertRaisesRegex(
898 CogUsageError,
899 r"^--markers requires 3 values separated by spaces, could not parse 'X'$",
900 ):
901 self.cog.callable_main(["argv0", "--markers=X"])
902 with self.assertRaisesRegex(
903 CogUsageError,
904 r"^--markers requires 3 values separated by spaces, could not parse 'A B C D'$",
905 ):
906 self.cog.callable_main(["argv0", "--markers=A B C D"])
909class TestMain(TestCaseWithTempDir):
910 def setUp(self):
911 super().setUp()
912 self.old_argv = sys.argv[:]
913 self.old_stderr = sys.stderr
914 sys.stderr = io.StringIO()
916 def tearDown(self):
917 sys.stderr = self.old_stderr
918 sys.argv = self.old_argv
919 sys.modules.pop("mycode", None)
920 super().tearDown()
922 def test_main_function(self):
923 sys.argv = ["argv0", "-Z"]
924 ret = main()
925 self.assertEqual(ret, 2)
926 stderr = sys.stderr.getvalue()
927 self.assertEqual(stderr, "option -Z not recognized\n(for help use -h)\n")
929 files = {
930 "test.cog": """\
931 //[[[cog
932 def func():
933 import mycode
934 mycode.boom()
935 //]]]
936 //[[[end]]]
937 -----
938 //[[[cog
939 func()
940 //]]]
941 //[[[end]]]
942 """,
943 "mycode.py": """\
944 def boom():
945 [][0]
946 """,
947 }
949 def test_error_report(self):
950 self.check_error_report()
952 def test_error_report_with_prologue(self):
953 self.check_error_report("-p", "#1\n#2")
955 def check_error_report(self, *args):
956 """Check that the error report is right."""
957 make_files(self.files)
958 sys.argv = ["argv0"] + list(args) + ["-r", "test.cog"]
959 main()
960 expected = reindent_block("""\
961 Traceback (most recent call last):
962 File "test.cog", line 9, in <module>
963 func()
964 File "test.cog", line 4, in func
965 mycode.boom()
966 File "MYCODE", line 2, in boom
967 [][0]
968 IndexError: list index out of range
969 """)
970 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
971 assert expected == sys.stderr.getvalue()
973 def test_error_in_prologue(self):
974 make_files(self.files)
975 sys.argv = ["argv0", "-p", "import mycode; mycode.boom()", "-r", "test.cog"]
976 main()
977 expected = reindent_block("""\
978 Traceback (most recent call last):
979 File "<prologue>", line 1, in <module>
980 import mycode; mycode.boom()
981 File "MYCODE", line 2, in boom
982 [][0]
983 IndexError: list index out of range
984 """)
985 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
986 assert expected == sys.stderr.getvalue()
989class TestFileHandling(TestCaseWithTempDir):
990 def test_simple(self):
991 d = {
992 "test.cog": """\
993 // This is my C++ file.
994 //[[[cog
995 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
996 for fn in fnames:
997 cog.outl("void %s();" % fn)
998 //]]]
999 //[[[end]]]
1000 """,
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 make_files(d)
1016 self.cog.callable_main(["argv0", "-r", "test.cog"])
1017 self.assertFilesSame("test.cog", "test.out")
1018 output = self.output.getvalue()
1019 self.assertIn("(changed)", output)
1021 def test_print_output(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 """,
1032 "test.out": """\
1033 // This is my C++ file.
1034 //[[[cog
1035 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1036 for fn in fnames:
1037 print("void %s();" % fn)
1038 //]]]
1039 void DoSomething();
1040 void DoAnotherThing();
1041 void DoLastThing();
1042 //[[[end]]]
1043 """,
1044 }
1046 make_files(d)
1047 self.cog.callable_main(["argv0", "-rP", "test.cog"])
1048 self.assertFilesSame("test.cog", "test.out")
1049 output = self.output.getvalue()
1050 self.assertIn("(changed)", output)
1052 def test_wildcards(self):
1053 d = {
1054 "test.cog": """\
1055 // This is my C++ file.
1056 //[[[cog
1057 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1058 for fn in fnames:
1059 cog.outl("void %s();" % fn)
1060 //]]]
1061 //[[[end]]]
1062 """,
1063 "test2.cog": """\
1064 // This is my C++ file.
1065 //[[[cog
1066 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1067 for fn in fnames:
1068 cog.outl("void %s();" % fn)
1069 //]]]
1070 //[[[end]]]
1071 """,
1072 "test.out": """\
1073 // This is my C++ file.
1074 //[[[cog
1075 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1076 for fn in fnames:
1077 cog.outl("void %s();" % fn)
1078 //]]]
1079 void DoSomething();
1080 void DoAnotherThing();
1081 void DoLastThing();
1082 //[[[end]]]
1083 """,
1084 "not_this_one.cog": """\
1085 // This is my C++ file.
1086 //[[[cog
1087 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1088 for fn in fnames:
1089 cog.outl("void %s();" % fn)
1090 //]]]
1091 //[[[end]]]
1092 """,
1093 "not_this_one.out": """\
1094 // This is my C++ file.
1095 //[[[cog
1096 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1097 for fn in fnames:
1098 cog.outl("void %s();" % fn)
1099 //]]]
1100 //[[[end]]]
1101 """,
1102 }
1104 make_files(d)
1105 self.cog.callable_main(["argv0", "-r", "t*.cog"])
1106 self.assertFilesSame("test.cog", "test.out")
1107 self.assertFilesSame("test2.cog", "test.out")
1108 self.assertFilesSame("not_this_one.cog", "not_this_one.out")
1109 output = self.output.getvalue()
1110 self.assertIn("(changed)", output)
1112 def test_output_file(self):
1113 # -o sets the output file.
1114 d = {
1115 "test.cog": """\
1116 // This is my C++ file.
1117 //[[[cog
1118 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1119 for fn in fnames:
1120 cog.outl("void %s();" % fn)
1121 //]]]
1122 //[[[end]]]
1123 """,
1124 "test.out": """\
1125 // This is my C++ file.
1126 //[[[cog
1127 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1128 for fn in fnames:
1129 cog.outl("void %s();" % fn)
1130 //]]]
1131 void DoSomething();
1132 void DoAnotherThing();
1133 void DoLastThing();
1134 //[[[end]]]
1135 """,
1136 }
1138 make_files(d)
1139 self.cog.callable_main(["argv0", "-o", "in/a/dir/test.cogged", "test.cog"])
1140 self.assertFilesSame("in/a/dir/test.cogged", "test.out")
1142 def test_at_file(self):
1143 d = {
1144 "one.cog": """\
1145 //[[[cog
1146 cog.outl("hello world")
1147 //]]]
1148 //[[[end]]]
1149 """,
1150 "one.out": """\
1151 //[[[cog
1152 cog.outl("hello world")
1153 //]]]
1154 hello world
1155 //[[[end]]]
1156 """,
1157 "two.cog": """\
1158 //[[[cog
1159 cog.outl("goodbye cruel world")
1160 //]]]
1161 //[[[end]]]
1162 """,
1163 "two.out": """\
1164 //[[[cog
1165 cog.outl("goodbye cruel world")
1166 //]]]
1167 goodbye cruel world
1168 //[[[end]]]
1169 """,
1170 "cogfiles.txt": """\
1171 # Please run cog
1172 one.cog
1174 two.cog
1175 """,
1176 }
1178 make_files(d)
1179 self.cog.callable_main(["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 test_nested_at_file(self):
1186 d = {
1187 "one.cog": """\
1188 //[[[cog
1189 cog.outl("hello world")
1190 //]]]
1191 //[[[end]]]
1192 """,
1193 "one.out": """\
1194 //[[[cog
1195 cog.outl("hello world")
1196 //]]]
1197 hello world
1198 //[[[end]]]
1199 """,
1200 "two.cog": """\
1201 //[[[cog
1202 cog.outl("goodbye cruel world")
1203 //]]]
1204 //[[[end]]]
1205 """,
1206 "two.out": """\
1207 //[[[cog
1208 cog.outl("goodbye cruel world")
1209 //]]]
1210 goodbye cruel world
1211 //[[[end]]]
1212 """,
1213 "cogfiles.txt": """\
1214 # Please run cog
1215 one.cog
1216 @cogfiles2.txt
1217 """,
1218 "cogfiles2.txt": """\
1219 # This one too, please.
1220 two.cog
1221 """,
1222 }
1224 make_files(d)
1225 self.cog.callable_main(["argv0", "-r", "@cogfiles.txt"])
1226 self.assertFilesSame("one.cog", "one.out")
1227 self.assertFilesSame("two.cog", "two.out")
1228 output = self.output.getvalue()
1229 self.assertIn("(changed)", output)
1231 def test_at_file_with_args(self):
1232 d = {
1233 "both.cog": """\
1234 //[[[cog
1235 cog.outl("one: %s" % ('one' in globals()))
1236 cog.outl("two: %s" % ('two' in globals()))
1237 //]]]
1238 //[[[end]]]
1239 """,
1240 "one.out": """\
1241 //[[[cog
1242 cog.outl("one: %s" % ('one' in globals()))
1243 cog.outl("two: %s" % ('two' in globals()))
1244 //]]]
1245 one: True // ONE
1246 two: False // ONE
1247 //[[[end]]]
1248 """,
1249 "two.out": """\
1250 //[[[cog
1251 cog.outl("one: %s" % ('one' in globals()))
1252 cog.outl("two: %s" % ('two' in globals()))
1253 //]]]
1254 one: False // TWO
1255 two: True // TWO
1256 //[[[end]]]
1257 """,
1258 "cogfiles.txt": """\
1259 # Please run cog
1260 both.cog -o in/a/dir/both.one -s ' // ONE' -D one=x
1261 both.cog -o in/a/dir/both.two -s ' // TWO' -D two=x
1262 """,
1263 }
1265 make_files(d)
1266 self.cog.callable_main(["argv0", "@cogfiles.txt"])
1267 self.assertFilesSame("in/a/dir/both.one", "one.out")
1268 self.assertFilesSame("in/a/dir/both.two", "two.out")
1270 def test_at_file_with_bad_arg_combo(self):
1271 d = {
1272 "both.cog": """\
1273 //[[[cog
1274 cog.outl("one: %s" % ('one' in globals()))
1275 cog.outl("two: %s" % ('two' in globals()))
1276 //]]]
1277 //[[[end]]]
1278 """,
1279 "cogfiles.txt": """\
1280 # Please run cog
1281 both.cog
1282 both.cog -d # This is bad: -r and -d
1283 """,
1284 }
1286 make_files(d)
1287 with self.assertRaisesRegex(
1288 CogUsageError,
1289 r"^Can't use -d with -r \(or you would delete all your source!\)$",
1290 ):
1291 self.cog.callable_main(["argv0", "-r", "@cogfiles.txt"])
1293 def test_at_file_with_tricky_filenames(self):
1294 def fix_backslashes(files_txt):
1295 """Make the contents of a files.txt sensitive to the platform."""
1296 if sys.platform != "win32":
1297 files_txt = files_txt.replace("\\", "/")
1298 return files_txt
1300 d = {
1301 "one 1.cog": """\
1302 //[[[cog cog.outl("hello world") ]]]
1303 """,
1304 "one.out": """\
1305 //[[[cog cog.outl("hello world") ]]]
1306 hello world //xxx
1307 """,
1308 "subdir": {
1309 "subback.cog": """\
1310 //[[[cog cog.outl("down deep with backslashes") ]]]
1311 """,
1312 "subfwd.cog": """\
1313 //[[[cog cog.outl("down deep with slashes") ]]]
1314 """,
1315 },
1316 "subback.out": """\
1317 //[[[cog cog.outl("down deep with backslashes") ]]]
1318 down deep with backslashes //yyy
1319 """,
1320 "subfwd.out": """\
1321 //[[[cog cog.outl("down deep with slashes") ]]]
1322 down deep with slashes //zzz
1323 """,
1324 "cogfiles.txt": fix_backslashes("""\
1325 # Please run cog
1326 'one 1.cog' -s ' //xxx'
1327 subdir\\subback.cog -s ' //yyy'
1328 subdir/subfwd.cog -s ' //zzz'
1329 """),
1330 }
1332 make_files(d)
1333 self.cog.callable_main(["argv0", "-z", "-r", "@cogfiles.txt"])
1334 self.assertFilesSame("one 1.cog", "one.out")
1335 self.assertFilesSame("subdir/subback.cog", "subback.out")
1336 self.assertFilesSame("subdir/subfwd.cog", "subfwd.out")
1338 def test_amp_file(self):
1339 d = {
1340 "code": {
1341 "files_to_cog": """\
1342 # A locally resolved file name.
1343 test.cog
1344 """,
1345 "test.cog": """\
1346 //[[[cog
1347 import myampsubmodule
1348 //]]]
1349 //[[[end]]]
1350 """,
1351 "test.out": """\
1352 //[[[cog
1353 import myampsubmodule
1354 //]]]
1355 Hello from myampsubmodule
1356 //[[[end]]]
1357 """,
1358 "myampsubmodule.py": """\
1359 import cog
1360 cog.outl("Hello from myampsubmodule")
1361 """,
1362 }
1363 }
1365 make_files(d)
1366 print(os.path.abspath("code/test.out"))
1367 self.cog.callable_main(["argv0", "-r", "&code/files_to_cog"])
1368 self.assertFilesSame("code/test.cog", "code/test.out")
1370 def run_with_verbosity(self, verbosity):
1371 d = {
1372 "unchanged.cog": """\
1373 //[[[cog
1374 cog.outl("hello world")
1375 //]]]
1376 hello world
1377 //[[[end]]]
1378 """,
1379 "changed.cog": """\
1380 //[[[cog
1381 cog.outl("goodbye cruel world")
1382 //]]]
1383 //[[[end]]]
1384 """,
1385 "cogfiles.txt": """\
1386 unchanged.cog
1387 changed.cog
1388 """,
1389 }
1391 make_files(d)
1392 self.cog.callable_main(
1393 ["argv0", "-r", "--verbosity=" + verbosity, "@cogfiles.txt"]
1394 )
1395 output = self.output.getvalue()
1396 return output
1398 def test_verbosity0(self):
1399 output = self.run_with_verbosity("0")
1400 self.assertEqual(output, "")
1402 def test_verbosity1(self):
1403 output = self.run_with_verbosity("1")
1404 self.assertEqual(output, "Cogging changed.cog (changed)\n")
1406 def test_verbosity2(self):
1407 output = self.run_with_verbosity("2")
1408 self.assertEqual(
1409 output, "Cogging unchanged.cog\nCogging changed.cog (changed)\n"
1410 )
1413class CogTestLineEndings(TestCaseWithTempDir):
1414 """Tests for -U option (force LF line-endings in output)."""
1416 lines_in = [
1417 "Some text.",
1418 "//[[[cog",
1419 'cog.outl("Cog text")',
1420 "//]]]",
1421 "gobbledegook.",
1422 "//[[[end]]]",
1423 "epilogue.",
1424 "",
1425 ]
1427 lines_out = [
1428 "Some text.",
1429 "//[[[cog",
1430 'cog.outl("Cog text")',
1431 "//]]]",
1432 "Cog text",
1433 "//[[[end]]]",
1434 "epilogue.",
1435 "",
1436 ]
1438 def test_output_native_eol(self):
1439 make_files({"infile": "\n".join(self.lines_in)})
1440 self.cog.callable_main(["argv0", "-o", "outfile", "infile"])
1441 self.assertFileContent("outfile", os.linesep.join(self.lines_out))
1443 def test_output_lf_eol(self):
1444 make_files({"infile": "\n".join(self.lines_in)})
1445 self.cog.callable_main(["argv0", "-U", "-o", "outfile", "infile"])
1446 self.assertFileContent("outfile", "\n".join(self.lines_out))
1448 def test_replace_native_eol(self):
1449 make_files({"test.cog": "\n".join(self.lines_in)})
1450 self.cog.callable_main(["argv0", "-r", "test.cog"])
1451 self.assertFileContent("test.cog", os.linesep.join(self.lines_out))
1453 def test_replace_lf_eol(self):
1454 make_files({"test.cog": "\n".join(self.lines_in)})
1455 self.cog.callable_main(["argv0", "-U", "-r", "test.cog"])
1456 self.assertFileContent("test.cog", "\n".join(self.lines_out))
1459class CogTestCharacterEncoding(TestCaseWithTempDir):
1460 def test_simple(self):
1461 d = {
1462 "test.cog": b"""\
1463 // This is my C++ file.
1464 //[[[cog
1465 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
1466 //]]]
1467 //[[[end]]]
1468 """,
1469 "test.out": b"""\
1470 // This is my C++ file.
1471 //[[[cog
1472 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
1473 //]]]
1474 // Unicode: \xe1\x88\xb4 (U+1234)
1475 //[[[end]]]
1476 """.replace(b"\n", os.linesep.encode()),
1477 }
1479 make_files(d)
1480 self.cog.callable_main(["argv0", "-r", "test.cog"])
1481 self.assertFilesSame("test.cog", "test.out")
1482 output = self.output.getvalue()
1483 self.assertIn("(changed)", output)
1485 def test_file_encoding_option(self):
1486 d = {
1487 "test.cog": b"""\
1488 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
1489 //[[[cog
1490 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")
1491 //]]]
1492 //[[[end]]]
1493 """,
1494 "test.out": b"""\
1495 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
1496 //[[[cog
1497 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")
1498 //]]]
1499 \xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe
1500 //[[[end]]]
1501 """.replace(b"\n", os.linesep.encode()),
1502 }
1504 make_files(d)
1505 self.cog.callable_main(["argv0", "-n", "cp1251", "-r", "test.cog"])
1506 self.assertFilesSame("test.cog", "test.out")
1507 output = self.output.getvalue()
1508 self.assertIn("(changed)", output)
1511class TestCaseWithImports(TestCaseWithTempDir):
1512 """Automatic resetting of sys.modules for tests that import modules.
1514 When running tests which import modules, the sys.modules list
1515 leaks from one test to the next. This test case class scrubs
1516 the list after each run to keep the tests isolated from each other.
1518 """
1520 def setUp(self):
1521 super().setUp()
1522 self.sysmodulekeys = list(sys.modules)
1524 def tearDown(self):
1525 modstoscrub = [
1526 modname for modname in sys.modules if modname not in self.sysmodulekeys
1527 ]
1528 for modname in modstoscrub:
1529 del sys.modules[modname]
1530 super().tearDown()
1533class CogIncludeTests(TestCaseWithImports):
1534 dincludes = {
1535 "test.cog": """\
1536 //[[[cog
1537 import mymodule
1538 //]]]
1539 //[[[end]]]
1540 """,
1541 "test.out": """\
1542 //[[[cog
1543 import mymodule
1544 //]]]
1545 Hello from mymodule
1546 //[[[end]]]
1547 """,
1548 "test2.out": """\
1549 //[[[cog
1550 import mymodule
1551 //]]]
1552 Hello from mymodule in inc2
1553 //[[[end]]]
1554 """,
1555 "include": {
1556 "mymodule.py": """\
1557 import cog
1558 cog.outl("Hello from mymodule")
1559 """
1560 },
1561 "inc2": {
1562 "mymodule.py": """\
1563 import cog
1564 cog.outl("Hello from mymodule in inc2")
1565 """
1566 },
1567 "inc3": {
1568 "someothermodule.py": """\
1569 import cog
1570 cog.outl("This is some other module.")
1571 """
1572 },
1573 }
1575 def test_need_include_path(self):
1576 # Try it without the -I, to see that an ImportError happens.
1577 make_files(self.dincludes)
1578 msg = "(ImportError|ModuleNotFoundError): No module named '?mymodule'?"
1579 with self.assertRaisesRegex(CogUserException, msg):
1580 self.cog.callable_main(["argv0", "-r", "test.cog"])
1582 def test_include_path(self):
1583 # Test that -I adds include directories properly.
1584 make_files(self.dincludes)
1585 self.cog.callable_main(["argv0", "-r", "-I", "include", "test.cog"])
1586 self.assertFilesSame("test.cog", "test.out")
1588 def test_two_include_paths(self):
1589 # Test that two -I's add include directories properly.
1590 make_files(self.dincludes)
1591 self.cog.callable_main(
1592 ["argv0", "-r", "-I", "include", "-I", "inc2", "test.cog"]
1593 )
1594 self.assertFilesSame("test.cog", "test.out")
1596 def test_two_include_paths2(self):
1597 # Test that two -I's add include directories properly.
1598 make_files(self.dincludes)
1599 self.cog.callable_main(
1600 ["argv0", "-r", "-I", "inc2", "-I", "include", "test.cog"]
1601 )
1602 self.assertFilesSame("test.cog", "test2.out")
1604 def test_useless_include_path(self):
1605 # Test that the search will continue past the first directory.
1606 make_files(self.dincludes)
1607 self.cog.callable_main(
1608 ["argv0", "-r", "-I", "inc3", "-I", "include", "test.cog"]
1609 )
1610 self.assertFilesSame("test.cog", "test.out")
1612 def test_sys_path_is_unchanged(self):
1613 d = {
1614 "bad.cog": """\
1615 //[[[cog cog.error("Oh no!") ]]]
1616 //[[[end]]]
1617 """,
1618 "good.cog": """\
1619 //[[[cog cog.outl("Oh yes!") ]]]
1620 //[[[end]]]
1621 """,
1622 }
1624 make_files(d)
1625 # Is it unchanged just by creating a cog engine?
1626 oldsyspath = sys.path[:]
1627 self.new_cog()
1628 self.assertEqual(oldsyspath, sys.path)
1629 # Is it unchanged for a successful run?
1630 self.new_cog()
1631 self.cog.callable_main(["argv0", "-r", "good.cog"])
1632 self.assertEqual(oldsyspath, sys.path)
1633 # Is it unchanged for a successful run with includes?
1634 self.new_cog()
1635 self.cog.callable_main(["argv0", "-r", "-I", "xyzzy", "good.cog"])
1636 self.assertEqual(oldsyspath, sys.path)
1637 # Is it unchanged for a successful run with two includes?
1638 self.new_cog()
1639 self.cog.callable_main(["argv0", "-r", "-I", "xyzzy", "-I", "quux", "good.cog"])
1640 self.assertEqual(oldsyspath, sys.path)
1641 # Is it unchanged for a failed run?
1642 self.new_cog()
1643 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1644 self.cog.callable_main(["argv0", "-r", "bad.cog"])
1645 self.assertEqual(oldsyspath, sys.path)
1646 # Is it unchanged for a failed run with includes?
1647 self.new_cog()
1648 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1649 self.cog.callable_main(["argv0", "-r", "-I", "xyzzy", "bad.cog"])
1650 self.assertEqual(oldsyspath, sys.path)
1651 # Is it unchanged for a failed run with two includes?
1652 self.new_cog()
1653 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
1654 self.cog.callable_main(
1655 ["argv0", "-r", "-I", "xyzzy", "-I", "quux", "bad.cog"]
1656 )
1657 self.assertEqual(oldsyspath, sys.path)
1659 def test_sub_directories(self):
1660 # Test that relative paths on the command line work, with includes.
1662 d = {
1663 "code": {
1664 "test.cog": """\
1665 //[[[cog
1666 import mysubmodule
1667 //]]]
1668 //[[[end]]]
1669 """,
1670 "test.out": """\
1671 //[[[cog
1672 import mysubmodule
1673 //]]]
1674 Hello from mysubmodule
1675 //[[[end]]]
1676 """,
1677 "mysubmodule.py": """\
1678 import cog
1679 cog.outl("Hello from mysubmodule")
1680 """,
1681 }
1682 }
1684 make_files(d)
1685 # We should be able to invoke cog without the -I switch, and it will
1686 # auto-include the current directory
1687 self.cog.callable_main(["argv0", "-r", "code/test.cog"])
1688 self.assertFilesSame("code/test.cog", "code/test.out")
1691class CogTestsInFiles(TestCaseWithTempDir):
1692 def test_warn_if_no_cog_code(self):
1693 # Test that the -e switch warns if there is no Cog code.
1694 d = {
1695 "with.cog": """\
1696 //[[[cog
1697 cog.outl("hello world")
1698 //]]]
1699 hello world
1700 //[[[end]]]
1701 """,
1702 "without.cog": """\
1703 There's no cog
1704 code in this file.
1705 """,
1706 }
1708 make_files(d)
1709 self.cog.callable_main(["argv0", "-e", "with.cog"])
1710 output = self.output.getvalue()
1711 self.assertNotIn("Warning", output)
1712 self.new_cog()
1713 self.cog.callable_main(["argv0", "-e", "without.cog"])
1714 output = self.output.getvalue()
1715 self.assertIn("Warning: no cog code found in without.cog", output)
1716 self.new_cog()
1717 self.cog.callable_main(["argv0", "without.cog"])
1718 output = self.output.getvalue()
1719 self.assertNotIn("Warning", output)
1721 def test_file_name_props(self):
1722 d = {
1723 "cog1.txt": """\
1724 //[[[cog
1725 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1726 //]]]
1727 this is cog1.txt in, cog1.txt out
1728 [[[end]]]
1729 """,
1730 "cog1.out": """\
1731 //[[[cog
1732 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1733 //]]]
1734 This is cog1.txt in, cog1.txt out
1735 [[[end]]]
1736 """,
1737 "cog1out.out": """\
1738 //[[[cog
1739 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
1740 //]]]
1741 This is cog1.txt in, cog1out.txt out
1742 [[[end]]]
1743 """,
1744 }
1746 make_files(d)
1747 self.cog.callable_main(["argv0", "-r", "cog1.txt"])
1748 self.assertFilesSame("cog1.txt", "cog1.out")
1749 self.new_cog()
1750 self.cog.callable_main(["argv0", "-o", "cog1out.txt", "cog1.txt"])
1751 self.assertFilesSame("cog1out.txt", "cog1out.out")
1753 def test_globals_dont_cross_files(self):
1754 # Make sure that global values don't get shared between files.
1755 d = {
1756 "one.cog": """\
1757 //[[[cog s = "This was set in one.cog" ]]]
1758 //[[[end]]]
1759 //[[[cog cog.outl(s) ]]]
1760 //[[[end]]]
1761 """,
1762 "one.out": """\
1763 //[[[cog s = "This was set in one.cog" ]]]
1764 //[[[end]]]
1765 //[[[cog cog.outl(s) ]]]
1766 This was set in one.cog
1767 //[[[end]]]
1768 """,
1769 "two.cog": """\
1770 //[[[cog
1771 try:
1772 cog.outl(s)
1773 except NameError:
1774 cog.outl("s isn't set!")
1775 //]]]
1776 //[[[end]]]
1777 """,
1778 "two.out": """\
1779 //[[[cog
1780 try:
1781 cog.outl(s)
1782 except NameError:
1783 cog.outl("s isn't set!")
1784 //]]]
1785 s isn't set!
1786 //[[[end]]]
1787 """,
1788 "cogfiles.txt": """\
1789 # Please run cog
1790 one.cog
1792 two.cog
1793 """,
1794 }
1796 make_files(d)
1797 self.cog.callable_main(["argv0", "-r", "@cogfiles.txt"])
1798 self.assertFilesSame("one.cog", "one.out")
1799 self.assertFilesSame("two.cog", "two.out")
1800 output = self.output.getvalue()
1801 self.assertIn("(changed)", output)
1803 def test_remove_generated_output(self):
1804 d = {
1805 "cog1.txt": """\
1806 //[[[cog
1807 cog.outl("This line was generated.")
1808 //]]]
1809 This line was generated.
1810 //[[[end]]]
1811 This line was not.
1812 """,
1813 "cog1.out": """\
1814 //[[[cog
1815 cog.outl("This line was generated.")
1816 //]]]
1817 //[[[end]]]
1818 This line was not.
1819 """,
1820 "cog1.out2": """\
1821 //[[[cog
1822 cog.outl("This line was generated.")
1823 //]]]
1824 This line was generated.
1825 //[[[end]]]
1826 This line was not.
1827 """,
1828 }
1830 make_files(d)
1831 # Remove generated output.
1832 self.cog.callable_main(["argv0", "-r", "-x", "cog1.txt"])
1833 self.assertFilesSame("cog1.txt", "cog1.out")
1834 self.new_cog()
1835 # Regenerate the generated output.
1836 self.cog.callable_main(["argv0", "-r", "cog1.txt"])
1837 self.assertFilesSame("cog1.txt", "cog1.out2")
1838 self.new_cog()
1839 # Remove the generated output again.
1840 self.cog.callable_main(["argv0", "-r", "-x", "cog1.txt"])
1841 self.assertFilesSame("cog1.txt", "cog1.out")
1843 def test_msg_call(self):
1844 infile = """\
1845 #[[[cog
1846 cog.msg("Hello there!")
1847 #]]]
1848 #[[[end]]]
1849 """
1850 infile = reindent_block(infile)
1851 self.assertEqual(self.cog.process_string(infile), infile)
1852 output = self.output.getvalue()
1853 self.assertEqual(output, "Message: Hello there!\n")
1855 def test_error_message_has_no_traceback(self):
1856 # Test that a Cog error is printed to stderr with no traceback.
1858 d = {
1859 "cog1.txt": """\
1860 //[[[cog
1861 cog.outl("This line was newly")
1862 cog.outl("generated by cog")
1863 cog.outl("blah blah.")
1864 //]]]
1865 Xhis line was newly
1866 generated by cog
1867 blah blah.
1868 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
1869 """,
1870 }
1872 make_files(d)
1873 stderr = io.StringIO()
1874 self.cog.set_output(stderr=stderr)
1875 self.cog.main(["argv0", "-c", "-r", "cog1.txt"])
1876 self.assertEqual(self.output.getvalue(), "Cogging cog1.txt\n")
1877 self.assertEqual(
1878 stderr.getvalue(),
1879 "cog1.txt(9): Output has been edited! Delete old checksum to unprotect.\n",
1880 )
1882 def test_dash_d(self):
1883 d = {
1884 "test.cog": """\
1885 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1886 --[[[end]]]
1887 """,
1888 "test.kablooey": """\
1889 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1890 Defined fooey as kablooey
1891 --[[[end]]]
1892 """,
1893 "test.einstein": """\
1894 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
1895 Defined fooey as e=mc2
1896 --[[[end]]]
1897 """,
1898 }
1900 make_files(d)
1901 self.cog.callable_main(["argv0", "-r", "-D", "fooey=kablooey", "test.cog"])
1902 self.assertFilesSame("test.cog", "test.kablooey")
1903 make_files(d)
1904 self.cog.callable_main(["argv0", "-r", "-Dfooey=kablooey", "test.cog"])
1905 self.assertFilesSame("test.cog", "test.kablooey")
1906 make_files(d)
1907 self.cog.callable_main(["argv0", "-r", "-Dfooey=e=mc2", "test.cog"])
1908 self.assertFilesSame("test.cog", "test.einstein")
1909 make_files(d)
1910 self.cog.callable_main(
1911 ["argv0", "-r", "-Dbar=quux", "-Dfooey=kablooey", "test.cog"]
1912 )
1913 self.assertFilesSame("test.cog", "test.kablooey")
1914 make_files(d)
1915 self.cog.callable_main(
1916 ["argv0", "-r", "-Dfooey=kablooey", "-Dbar=quux", "test.cog"]
1917 )
1918 self.assertFilesSame("test.cog", "test.kablooey")
1919 make_files(d)
1920 self.cog.callable_main(
1921 ["argv0", "-r", "-Dfooey=gooey", "-Dfooey=kablooey", "test.cog"]
1922 )
1923 self.assertFilesSame("test.cog", "test.kablooey")
1925 def test_output_to_stdout(self):
1926 d = {
1927 "test.cog": """\
1928 --[[[cog cog.outl('Hey there!') ]]]
1929 --[[[end]]]
1930 """
1931 }
1933 make_files(d)
1934 stderr = io.StringIO()
1935 self.cog.set_output(stderr=stderr)
1936 self.cog.callable_main(["argv0", "test.cog"])
1937 output = self.output.getvalue()
1938 outerr = stderr.getvalue()
1939 self.assertEqual(
1940 output, "--[[[cog cog.outl('Hey there!') ]]]\nHey there!\n--[[[end]]]\n"
1941 )
1942 self.assertEqual(outerr, "")
1944 def test_read_from_stdin(self):
1945 stdin = io.StringIO("--[[[cog cog.outl('Wow') ]]]\n--[[[end]]]\n")
1947 def restore_stdin(old_stdin):
1948 sys.stdin = old_stdin
1950 self.addCleanup(restore_stdin, sys.stdin)
1951 sys.stdin = stdin
1953 stderr = io.StringIO()
1954 self.cog.set_output(stderr=stderr)
1955 self.cog.callable_main(["argv0", "-"])
1956 output = self.output.getvalue()
1957 outerr = stderr.getvalue()
1958 self.assertEqual(output, "--[[[cog cog.outl('Wow') ]]]\nWow\n--[[[end]]]\n")
1959 self.assertEqual(outerr, "")
1961 def test_suffix_output_lines(self):
1962 d = {
1963 "test.cog": """\
1964 Hey there.
1965 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
1966 ;[[[end]]]
1967 Good bye.
1968 """,
1969 "test.out": """\
1970 Hey there.
1971 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
1972 a (foo)
1973 b (foo)
1974 """ # These three trailing spaces are important.
1975 # The suffix is not applied to completely blank lines.
1976 """
1977 c (foo)
1978 ;[[[end]]]
1979 Good bye.
1980 """,
1981 }
1983 make_files(d)
1984 self.cog.callable_main(["argv0", "-r", "-s", " (foo)", "test.cog"])
1985 self.assertFilesSame("test.cog", "test.out")
1987 def test_empty_suffix(self):
1988 d = {
1989 "test.cog": """\
1990 ;[[[cog cog.outl('a\\nb\\nc') ]]]
1991 ;[[[end]]]
1992 """,
1993 "test.out": """\
1994 ;[[[cog cog.outl('a\\nb\\nc') ]]]
1995 a
1996 b
1997 c
1998 ;[[[end]]]
1999 """,
2000 }
2002 make_files(d)
2003 self.cog.callable_main(["argv0", "-r", "-s", "", "test.cog"])
2004 self.assertFilesSame("test.cog", "test.out")
2006 def test_hellish_suffix(self):
2007 d = {
2008 "test.cog": """\
2009 ;[[[cog cog.outl('a\\n\\nb') ]]]
2010 """,
2011 "test.out": """\
2012 ;[[[cog cog.outl('a\\n\\nb') ]]]
2013 a /\\n*+([)]><
2015 b /\\n*+([)]><
2016 """,
2017 }
2019 make_files(d)
2020 self.cog.callable_main(["argv0", "-z", "-r", "-s", r" /\n*+([)]><", "test.cog"])
2021 self.assertFilesSame("test.cog", "test.out")
2023 def test_prologue(self):
2024 d = {
2025 "test.cog": """\
2026 Some text.
2027 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
2028 //[[[end]]]
2029 epilogue.
2030 """,
2031 "test.out": """\
2032 Some text.
2033 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
2034 1.4142135623
2035 //[[[end]]]
2036 epilogue.
2037 """,
2038 }
2040 make_files(d)
2041 self.cog.callable_main(["argv0", "-r", "-p", "import math", "test.cog"])
2042 self.assertFilesSame("test.cog", "test.out")
2044 def test_threads(self):
2045 # Test that the implicitly imported cog module is actually different for
2046 # different threads.
2047 numthreads = 20
2049 d = {}
2050 for i in range(numthreads):
2051 d[f"f{i}.cog"] = (
2052 "x\n" * i
2053 + "[[[cog\n"
2054 + f"assert cog.firstLineNum == int(FIRST) == {i+1}\n"
2055 + "]]]\n"
2056 + "[[[end]]]\n"
2057 )
2058 make_files(d)
2060 results = []
2062 def thread_main(num):
2063 try:
2064 ret = Cog().main(
2065 ["cog.py", "-r", "-D", f"FIRST={num+1}", f"f{num}.cog"]
2066 )
2067 assert ret == 0
2068 except Exception as exc: # pragma: no cover (only happens on test failure)
2069 results.append(exc)
2070 else:
2071 results.append(None)
2073 ts = [
2074 threading.Thread(target=thread_main, args=(i,)) for i in range(numthreads)
2075 ]
2076 for t in ts:
2077 t.start()
2078 for t in ts:
2079 t.join()
2080 assert results == [None] * numthreads
2083class CheckTests(TestCaseWithTempDir):
2084 def run_check(self, args, status=0):
2085 actual_status = self.cog.main(["argv0", "--check"] + args)
2086 print(self.output.getvalue())
2087 self.assertEqual(status, actual_status)
2089 def assert_made_files_unchanged(self, d):
2090 for name, content in d.items():
2091 content = reindent_block(content)
2092 if os.name == "nt":
2093 content = content.replace("\n", "\r\n")
2094 self.assertFileContent(name, content)
2096 def test_check_no_cog(self):
2097 d = {
2098 "hello.txt": """\
2099 Hello.
2100 """,
2101 }
2102 make_files(d)
2103 self.run_check(["hello.txt"], status=0)
2104 self.assertEqual(self.output.getvalue(), "Checking hello.txt\n")
2105 self.assert_made_files_unchanged(d)
2107 def test_check_good(self):
2108 d = {
2109 "unchanged.cog": """\
2110 //[[[cog
2111 cog.outl("hello world")
2112 //]]]
2113 hello world
2114 //[[[end]]]
2115 """,
2116 }
2117 make_files(d)
2118 self.run_check(["unchanged.cog"], status=0)
2119 self.assertEqual(self.output.getvalue(), "Checking unchanged.cog\n")
2120 self.assert_made_files_unchanged(d)
2122 def test_check_bad(self):
2123 d = {
2124 "changed.cog": """\
2125 //[[[cog
2126 cog.outl("goodbye world")
2127 //]]]
2128 hello world
2129 //[[[end]]]
2130 """,
2131 }
2132 make_files(d)
2133 self.run_check(["changed.cog"], status=5)
2134 self.assertEqual(
2135 self.output.getvalue(), "Checking changed.cog (changed)\nCheck failed\n"
2136 )
2137 self.assert_made_files_unchanged(d)
2139 def test_check_mixed(self):
2140 d = {
2141 "unchanged.cog": """\
2142 //[[[cog
2143 cog.outl("hello world")
2144 //]]]
2145 hello world
2146 //[[[end]]]
2147 """,
2148 "changed.cog": """\
2149 //[[[cog
2150 cog.outl("goodbye world")
2151 //]]]
2152 hello world
2153 //[[[end]]]
2154 """,
2155 }
2156 make_files(d)
2157 for verbosity, output in [
2158 ("0", "Check failed\n"),
2159 ("1", "Checking changed.cog (changed)\nCheck failed\n"),
2160 (
2161 "2",
2162 "Checking unchanged.cog\nChecking changed.cog (changed)\nCheck failed\n",
2163 ),
2164 ]:
2165 self.new_cog()
2166 self.run_check(
2167 ["--verbosity=%s" % verbosity, "unchanged.cog", "changed.cog"], status=5
2168 )
2169 self.assertEqual(self.output.getvalue(), output)
2170 self.assert_made_files_unchanged(d)
2172 def test_check_with_good_checksum(self):
2173 d = {
2174 "good.txt": """\
2175 //[[[cog
2176 cog.outl("This line was newly")
2177 cog.outl("generated by cog")
2178 cog.outl("blah blah.")
2179 //]]]
2180 This line was newly
2181 generated by cog
2182 blah blah.
2183 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2184 """,
2185 }
2186 make_files(d)
2187 # Have to use -c with --check if there are checksums in the file.
2188 self.run_check(["-c", "good.txt"], status=0)
2189 self.assertEqual(self.output.getvalue(), "Checking good.txt\n")
2190 self.assert_made_files_unchanged(d)
2192 def test_check_with_bad_checksum(self):
2193 d = {
2194 "bad.txt": """\
2195 //[[[cog
2196 cog.outl("This line was newly")
2197 cog.outl("generated by cog")
2198 cog.outl("blah blah.")
2199 //]]]
2200 This line was newly
2201 generated by cog
2202 blah blah.
2203 //[[[end]]] (checksum: a9999999e5ad6b95c9e9a184b26f4346)
2204 """,
2205 }
2206 make_files(d)
2207 # Have to use -c with --check if there are checksums in the file.
2208 self.run_check(["-c", "bad.txt"], status=1)
2209 self.assertEqual(
2210 self.output.getvalue(),
2211 "Checking bad.txt\nbad.txt(9): Output has been edited! Delete old checksum to unprotect.\n",
2212 )
2213 self.assert_made_files_unchanged(d)
2216class WritabilityTests(TestCaseWithTempDir):
2217 d = {
2218 "test.cog": """\
2219 //[[[cog
2220 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
2221 cog.outl("void %s();" % fn)
2222 //]]]
2223 //[[[end]]]
2224 """,
2225 "test.out": """\
2226 //[[[cog
2227 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
2228 cog.outl("void %s();" % fn)
2229 //]]]
2230 void DoSomething();
2231 void DoAnotherThing();
2232 void DoLastThing();
2233 //[[[end]]]
2234 """,
2235 }
2237 if os.name == "nt": 2237 ↛ 2239line 2237 didn't jump to line 2239 because the condition on line 2237 was never true
2238 # for Windows
2239 cmd_w_args = "attrib -R %s"
2240 cmd_w_asterisk = "attrib -R *"
2241 else:
2242 # for unix-like
2243 cmd_w_args = "chmod +w %s"
2244 cmd_w_asterisk = "chmod +w *"
2246 def setUp(self):
2247 super().setUp()
2248 make_files(self.d)
2249 self.testcog = os.path.join(self.tempdir, "test.cog")
2250 os.chmod(self.testcog, stat.S_IREAD) # Make the file readonly.
2251 assert not os.access(self.testcog, os.W_OK)
2253 def tearDown(self):
2254 os.chmod(self.testcog, stat.S_IWRITE) # Make the file writable again.
2255 super().tearDown()
2257 def test_readonly_no_command(self):
2258 with self.assertRaisesRegex(CogError, "^Can't overwrite test.cog$"):
2259 self.cog.callable_main(["argv0", "-r", "test.cog"])
2260 assert not os.access(self.testcog, os.W_OK)
2262 def test_readonly_with_command(self):
2263 self.cog.callable_main(["argv0", "-r", "-w", self.cmd_w_args, "test.cog"])
2264 self.assertFilesSame("test.cog", "test.out")
2265 assert os.access(self.testcog, os.W_OK)
2267 def test_readonly_with_command_with_no_slot(self):
2268 self.cog.callable_main(["argv0", "-r", "-w", self.cmd_w_asterisk, "test.cog"])
2269 self.assertFilesSame("test.cog", "test.out")
2270 assert os.access(self.testcog, os.W_OK)
2272 def test_readonly_with_ineffectual_command(self):
2273 with self.assertRaisesRegex(CogError, "^Couldn't make test.cog writable$"):
2274 self.cog.callable_main(["argv0", "-r", "-w", "echo %s", "test.cog"])
2275 assert not os.access(self.testcog, os.W_OK)
2278class ChecksumTests(TestCaseWithTempDir):
2279 def test_create_checksum_output(self):
2280 d = {
2281 "cog1.txt": """\
2282 //[[[cog
2283 cog.outl("This line was generated.")
2284 //]]]
2285 This line was generated.
2286 //[[[end]]]
2287 This line was not.
2288 """,
2289 "cog1.out": """\
2290 //[[[cog
2291 cog.outl("This line was generated.")
2292 //]]]
2293 This line was generated.
2294 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
2295 This line was not.
2296 """,
2297 }
2299 make_files(d)
2300 self.cog.callable_main(["argv0", "-r", "-c", "cog1.txt"])
2301 self.assertFilesSame("cog1.txt", "cog1.out")
2303 def test_check_checksum_output(self):
2304 d = {
2305 "cog1.txt": """\
2306 //[[[cog
2307 cog.outl("This line was newly")
2308 cog.outl("generated by cog")
2309 cog.outl("blah blah.")
2310 //]]]
2311 This line was generated.
2312 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
2313 """,
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]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2324 """,
2325 }
2327 make_files(d)
2328 self.cog.callable_main(["argv0", "-r", "-c", "cog1.txt"])
2329 self.assertFilesSame("cog1.txt", "cog1.out")
2331 def test_remove_checksum_output(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 This line was generated.
2340 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) fooey
2341 """,
2342 "cog1.out": """\
2343 //[[[cog
2344 cog.outl("This line was newly")
2345 cog.outl("generated by cog")
2346 cog.outl("blah blah.")
2347 //]]]
2348 This line was newly
2349 generated by cog
2350 blah blah.
2351 //[[[end]]] fooey
2352 """,
2353 }
2355 make_files(d)
2356 self.cog.callable_main(["argv0", "-r", "cog1.txt"])
2357 self.assertFilesSame("cog1.txt", "cog1.out")
2359 def test_tampered_checksum_output(self):
2360 d = {
2361 "cog1.txt": """\
2362 //[[[cog
2363 cog.outl("This line was newly")
2364 cog.outl("generated by cog")
2365 cog.outl("blah blah.")
2366 //]]]
2367 Xhis line was newly
2368 generated by cog
2369 blah blah.
2370 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2371 """,
2372 "cog2.txt": """\
2373 //[[[cog
2374 cog.outl("This line was newly")
2375 cog.outl("generated by cog")
2376 cog.outl("blah blah.")
2377 //]]]
2378 This line was newly
2379 generated by cog
2380 blah blah!
2381 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2382 """,
2383 "cog3.txt": """\
2384 //[[[cog
2385 cog.outl("This line was newly")
2386 cog.outl("generated by cog")
2387 cog.outl("blah blah.")
2388 //]]]
2390 This line was newly
2391 generated by cog
2392 blah blah.
2393 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2394 """,
2395 "cog4.txt": """\
2396 //[[[cog
2397 cog.outl("This line was newly")
2398 cog.outl("generated by cog")
2399 cog.outl("blah blah.")
2400 //]]]
2401 This line was newly
2402 generated by cog
2403 blah blah..
2404 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2405 """,
2406 "cog5.txt": """\
2407 //[[[cog
2408 cog.outl("This line was newly")
2409 cog.outl("generated by cog")
2410 cog.outl("blah blah.")
2411 //]]]
2412 This line was newly
2413 generated by cog
2414 blah blah.
2415 extra
2416 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2417 """,
2418 "cog6.txt": """\
2419 //[[[cog
2420 cog.outl("This line was newly")
2421 cog.outl("generated by cog")
2422 cog.outl("blah blah.")
2423 //]]]
2424 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
2425 """,
2426 }
2428 make_files(d)
2429 with self.assertRaisesRegex(
2430 CogError,
2431 r"^cog1.txt\(9\): Output has been edited! Delete old checksum to unprotect.$",
2432 ):
2433 self.cog.callable_main(["argv0", "-c", "cog1.txt"])
2434 with self.assertRaisesRegex(
2435 CogError,
2436 r"^cog2.txt\(9\): Output has been edited! Delete old checksum to unprotect.$",
2437 ):
2438 self.cog.callable_main(["argv0", "-c", "cog2.txt"])
2439 with self.assertRaisesRegex(
2440 CogError,
2441 r"^cog3.txt\(10\): Output has been edited! Delete old checksum to unprotect.$",
2442 ):
2443 self.cog.callable_main(["argv0", "-c", "cog3.txt"])
2444 with self.assertRaisesRegex(
2445 CogError,
2446 r"^cog4.txt\(9\): Output has been edited! Delete old checksum to unprotect.$",
2447 ):
2448 self.cog.callable_main(["argv0", "-c", "cog4.txt"])
2449 with self.assertRaisesRegex(
2450 CogError,
2451 r"^cog5.txt\(10\): Output has been edited! Delete old checksum to unprotect.$",
2452 ):
2453 self.cog.callable_main(["argv0", "-c", "cog5.txt"])
2454 with self.assertRaisesRegex(
2455 CogError,
2456 r"^cog6.txt\(6\): Output has been edited! Delete old checksum to unprotect.$",
2457 ):
2458 self.cog.callable_main(["argv0", "-c", "cog6.txt"])
2460 def test_argv_isnt_modified(self):
2461 argv = ["argv0", "-v"]
2462 orig_argv = argv[:]
2463 self.cog.callable_main(argv)
2464 self.assertEqual(argv, orig_argv)
2467class CustomMarkerTests(TestCaseWithTempDir):
2468 def test_customer_markers(self):
2469 d = {
2470 "test.cog": """\
2471 //{{
2472 cog.outl("void %s();" % "MyFunction")
2473 //}}
2474 //{{end}}
2475 """,
2476 "test.out": """\
2477 //{{
2478 cog.outl("void %s();" % "MyFunction")
2479 //}}
2480 void MyFunction();
2481 //{{end}}
2482 """,
2483 }
2485 make_files(d)
2486 self.cog.callable_main(["argv0", "-r", "--markers={{ }} {{end}}", "test.cog"])
2487 self.assertFilesSame("test.cog", "test.out")
2489 def test_truly_wacky_markers(self):
2490 # Make sure the markers are properly re-escaped.
2491 d = {
2492 "test.cog": """\
2493 //**(
2494 cog.outl("void %s();" % "MyFunction")
2495 //**)
2496 //**(end)**
2497 """,
2498 "test.out": """\
2499 //**(
2500 cog.outl("void %s();" % "MyFunction")
2501 //**)
2502 void MyFunction();
2503 //**(end)**
2504 """,
2505 }
2507 make_files(d)
2508 self.cog.callable_main(
2509 ["argv0", "-r", "--markers=**( **) **(end)**", "test.cog"]
2510 )
2511 self.assertFilesSame("test.cog", "test.out")
2513 def test_change_just_one_marker(self):
2514 d = {
2515 "test.cog": """\
2516 //**(
2517 cog.outl("void %s();" % "MyFunction")
2518 //]]]
2519 //[[[end]]]
2520 """,
2521 "test.out": """\
2522 //**(
2523 cog.outl("void %s();" % "MyFunction")
2524 //]]]
2525 void MyFunction();
2526 //[[[end]]]
2527 """,
2528 }
2530 make_files(d)
2531 self.cog.callable_main(
2532 ["argv0", "-r", "--markers=**( ]]] [[[end]]]", "test.cog"]
2533 )
2534 self.assertFilesSame("test.cog", "test.out")
2537class BlakeTests(TestCaseWithTempDir):
2538 # Blake Winton's contributions.
2539 def test_delete_code(self):
2540 # -o sets the output file.
2541 d = {
2542 "test.cog": """\
2543 // This is my C++ file.
2544 //[[[cog
2545 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
2546 for fn in fnames:
2547 cog.outl("void %s();" % fn)
2548 //]]]
2549 Some Sample Code Here
2550 //[[[end]]]Data Data
2551 And Some More
2552 """,
2553 "test.out": """\
2554 // This is my C++ file.
2555 void DoSomething();
2556 void DoAnotherThing();
2557 void DoLastThing();
2558 And Some More
2559 """,
2560 }
2562 make_files(d)
2563 self.cog.callable_main(["argv0", "-d", "-o", "test.cogged", "test.cog"])
2564 self.assertFilesSame("test.cogged", "test.out")
2566 def test_delete_code_with_dash_r_fails(self):
2567 d = {
2568 "test.cog": """\
2569 // This is my C++ file.
2570 """
2571 }
2573 make_files(d)
2574 with self.assertRaisesRegex(
2575 CogUsageError,
2576 r"^Can't use -d with -r \(or you would delete all your source!\)$",
2577 ):
2578 self.cog.callable_main(["argv0", "-r", "-d", "test.cog"])
2580 def test_setting_globals(self):
2581 # Blake Winton contributed a way to set the globals that will be used in
2582 # processFile().
2583 d = {
2584 "test.cog": """\
2585 // This is my C++ file.
2586 //[[[cog
2587 for fn in fnames:
2588 cog.outl("void %s();" % fn)
2589 //]]]
2590 Some Sample Code Here
2591 //[[[end]]]""",
2592 "test.out": """\
2593 // This is my C++ file.
2594 void DoBlake();
2595 void DoWinton();
2596 void DoContribution();
2597 """,
2598 }
2600 make_files(d)
2601 globals = {}
2602 globals["fnames"] = ["DoBlake", "DoWinton", "DoContribution"]
2603 self.cog.options.delete_code = True
2604 self.cog.process_file("test.cog", "test.cogged", globals=globals)
2605 self.assertFilesSame("test.cogged", "test.out")
2608class ErrorCallTests(TestCaseWithTempDir):
2609 def test_error_call_has_no_traceback(self):
2610 # Test that cog.error() doesn't show a traceback.
2611 d = {
2612 "error.cog": """\
2613 //[[[cog
2614 cog.error("Something Bad!")
2615 //]]]
2616 //[[[end]]]
2617 """,
2618 }
2620 make_files(d)
2621 self.cog.main(["argv0", "-r", "error.cog"])
2622 output = self.output.getvalue()
2623 self.assertEqual(output, "Cogging error.cog\nError: Something Bad!\n")
2625 def test_real_error_has_traceback(self):
2626 # Test that a genuine error does show a traceback.
2627 d = {
2628 "error.cog": """\
2629 //[[[cog
2630 raise RuntimeError("Hey!")
2631 //]]]
2632 //[[[end]]]
2633 """,
2634 }
2636 make_files(d)
2637 self.cog.main(["argv0", "-r", "error.cog"])
2638 output = self.output.getvalue()
2639 msg = "Actual output:\n" + output
2640 self.assertTrue(
2641 output.startswith("Cogging error.cog\nTraceback (most recent"), msg
2642 )
2643 self.assertIn("RuntimeError: Hey!", output)
2646# Things not yet tested:
2647# - A bad -w command (currently fails silently).