Coverage for cogapp/test_cogapp.py: 29.57%

845 statements  

« prev     ^ index     » next       coverage.py v7.2.4, created at 2023-04-28 05:33 -0400

1""" Test cogapp. 

2""" 

3 

4import io 

5import os 

6import os.path 

7import random 

8import re 

9import shutil 

10import stat 

11import sys 

12import tempfile 

13import threading 

14from unittest import TestCase 

15 

16from .cogapp import Cog, CogOptions, CogGenerator 

17from .cogapp import CogError, CogUsageError, CogGeneratedError, CogUserException 

18from .cogapp import usage, __version__, main 

19from .makefiles import makeFiles 

20from .whiteutils import reindentBlock 

21 

22 

23class CogTestsInMemory(TestCase): 

24 """ Test cases for cogapp.Cog() 

25 """ 

26 

27 def testNoCog(self): 

28 strings = [ 

29 '', 

30 ' ', 

31 ' \t \t \tx', 

32 'hello', 

33 'the cat\nin the\nhat.', 

34 'Horton\n\tHears A\n\t\tWho' 

35 ] 

36 for s in strings: 

37 self.assertEqual(Cog().processString(s), s) 

38 

39 def testSimple(self): 

40 infile = """\ 

41 Some text. 

42 //[[[cog 

43 import cog 

44 cog.outl("This is line one\\n") 

45 cog.outl("This is line two") 

46 //]]] 

47 gobbledegook. 

48 //[[[end]]] 

49 epilogue. 

50 """ 

51 

52 outfile = """\ 

53 Some text. 

54 //[[[cog 

55 import cog 

56 cog.outl("This is line one\\n") 

57 cog.outl("This is line two") 

58 //]]] 

59 This is line one 

60 

61 This is line two 

62 //[[[end]]] 

63 epilogue. 

64 """ 

65 

66 self.assertEqual(Cog().processString(infile), outfile) 

67 

68 def testEmptyCog(self): 

69 # The cog clause can be totally empty. Not sure why you'd want it, 

70 # but it works. 

71 infile = """\ 

72 hello 

73 //[[[cog 

74 //]]] 

75 //[[[end]]] 

76 goodbye 

77 """ 

78 

79 infile = reindentBlock(infile) 

80 self.assertEqual(Cog().processString(infile), infile) 

81 

82 def testMultipleCogs(self): 

83 # One file can have many cog chunks, even abutting each other. 

84 infile = """\ 

85 //[[[cog 

86 cog.out("chunk1") 

87 //]]] 

88 chunk1 

89 //[[[end]]] 

90 //[[[cog 

91 cog.out("chunk2") 

92 //]]] 

93 chunk2 

94 //[[[end]]] 

95 between chunks 

96 //[[[cog 

97 cog.out("chunk3") 

98 //]]] 

99 chunk3 

100 //[[[end]]] 

101 """ 

102 

103 infile = reindentBlock(infile) 

104 self.assertEqual(Cog().processString(infile), infile) 

105 

106 def testTrimBlankLines(self): 

107 infile = """\ 

108 //[[[cog 

109 cog.out("This is line one\\n", trimblanklines=True) 

110 cog.out(''' 

111 This is line two 

112 ''', dedent=True, trimblanklines=True) 

113 cog.outl("This is line three", trimblanklines=True) 

114 //]]] 

115 This is line one 

116 This is line two 

117 This is line three 

118 //[[[end]]] 

119 """ 

120 

121 infile = reindentBlock(infile) 

122 self.assertEqual(Cog().processString(infile), infile) 

123 

124 def testTrimEmptyBlankLines(self): 

125 infile = """\ 

126 //[[[cog 

127 cog.out("This is line one\\n", trimblanklines=True) 

128 cog.out(''' 

129 This is line two 

130 ''', dedent=True, trimblanklines=True) 

131 cog.out('', dedent=True, trimblanklines=True) 

132 cog.outl("This is line three", trimblanklines=True) 

133 //]]] 

134 This is line one 

135 This is line two 

136 This is line three 

137 //[[[end]]] 

138 """ 

139 

140 infile = reindentBlock(infile) 

141 self.assertEqual(Cog().processString(infile), infile) 

142 

143 def testTrimBlankLinesWithLastPartial(self): 

144 infile = """\ 

145 //[[[cog 

146 cog.out("This is line one\\n", trimblanklines=True) 

147 cog.out("\\nLine two\\nLine three", trimblanklines=True) 

148 //]]] 

149 This is line one 

150 Line two 

151 Line three 

152 //[[[end]]] 

153 """ 

154 

155 infile = reindentBlock(infile) 

156 self.assertEqual(Cog().processString(infile), infile) 

157 

158 def testCogOutDedent(self): 

159 infile = """\ 

160 //[[[cog 

161 cog.out("This is the first line\\n") 

162 cog.out(''' 

163 This is dedent=True 1 

164 This is dedent=True 2 

165 ''', dedent=True, trimblanklines=True) 

166 cog.out(''' 

167 This is dedent=False 1 

168 This is dedent=False 2 

169 ''', dedent=False, trimblanklines=True) 

170 cog.out(''' 

171 This is dedent=default 1 

172 This is dedent=default 2 

173 ''', trimblanklines=True) 

174 cog.out("This is the last line\\n") 

175 //]]] 

176 This is the first line 

177 This is dedent=True 1 

178 This is dedent=True 2 

179 This is dedent=False 1 

180 This is dedent=False 2 

181 This is dedent=default 1 

182 This is dedent=default 2 

183 This is the last line 

184 //[[[end]]] 

185 """ 

186 

187 infile = reindentBlock(infile) 

188 self.assertEqual(Cog().processString(infile), infile) 

189 

190 def test22EndOfLine(self): 

191 # In Python 2.2, this cog file was not parsing because the 

192 # last line is indented but didn't end with a newline. 

193 infile = """\ 

194 //[[[cog 

195 import cog 

196 for i in range(3): 

197 cog.out("%d\\n" % i) 

198 //]]] 

199 0 

200 1 

201 2 

202 //[[[end]]] 

203 """ 

204 

205 infile = reindentBlock(infile) 

206 self.assertEqual(Cog().processString(infile), infile) 

207 

208 def testIndentedCode(self): 

209 infile = """\ 

210 first line 

211 [[[cog 

212 import cog 

213 for i in range(3): 

214 cog.out("xx%d\\n" % i) 

215 ]]] 

216 xx0 

217 xx1 

218 xx2 

219 [[[end]]] 

220 last line 

221 """ 

222 

223 infile = reindentBlock(infile) 

224 self.assertEqual(Cog().processString(infile), infile) 

225 

226 def testPrefixedCode(self): 

227 infile = """\ 

228 --[[[cog 

229 --import cog 

230 --for i in range(3): 

231 -- cog.out("xx%d\\n" % i) 

232 --]]] 

233 xx0 

234 xx1 

235 xx2 

236 --[[[end]]] 

237 """ 

238 

239 infile = reindentBlock(infile) 

240 self.assertEqual(Cog().processString(infile), infile) 

241 

242 def testPrefixedIndentedCode(self): 

243 infile = """\ 

244 prologue 

245 --[[[cog 

246 -- import cog 

247 -- for i in range(3): 

248 -- cog.out("xy%d\\n" % i) 

249 --]]] 

250 xy0 

251 xy1 

252 xy2 

253 --[[[end]]] 

254 """ 

255 

256 infile = reindentBlock(infile) 

257 self.assertEqual(Cog().processString(infile), infile) 

258 

259 def testBogusPrefixMatch(self): 

260 infile = """\ 

261 prologue 

262 #[[[cog 

263 import cog 

264 # This comment should not be clobbered by removing the pound sign. 

265 for i in range(3): 

266 cog.out("xy%d\\n" % i) 

267 #]]] 

268 xy0 

269 xy1 

270 xy2 

271 #[[[end]]] 

272 """ 

273 

274 infile = reindentBlock(infile) 

275 self.assertEqual(Cog().processString(infile), infile) 

276 

277 def testNoFinalNewline(self): 

278 # If the cog'ed output has no final newline, 

279 # it shouldn't eat up the cog terminator. 

280 infile = """\ 

281 prologue 

282 [[[cog 

283 import cog 

284 for i in range(3): 

285 cog.out("%d" % i) 

286 ]]] 

287 012 

288 [[[end]]] 

289 epilogue 

290 """ 

291 

292 infile = reindentBlock(infile) 

293 self.assertEqual(Cog().processString(infile), infile) 

294 

295 def testNoOutputAtAll(self): 

296 # If there is absolutely no cog output, that's ok. 

297 infile = """\ 

298 prologue 

299 [[[cog 

300 i = 1 

301 ]]] 

302 [[[end]]] 

303 epilogue 

304 """ 

305 

306 infile = reindentBlock(infile) 

307 self.assertEqual(Cog().processString(infile), infile) 

308 

309 def testPurelyBlankLine(self): 

310 # If there is a blank line in the cog code with no whitespace 

311 # prefix, that should be OK. 

312 

313 infile = """\ 

314 prologue 

315 [[[cog 

316 import sys 

317 cog.out("Hello") 

318 $ 

319 cog.out("There") 

320 ]]] 

321 HelloThere 

322 [[[end]]] 

323 epilogue 

324 """ 

325 

326 infile = reindentBlock(infile.replace('$', '')) 

327 self.assertEqual(Cog().processString(infile), infile) 

328 

329 def testEmptyOutl(self): 

330 # Alexander Belchenko suggested the string argument to outl should 

331 # be optional. Does it work? 

332 

333 infile = """\ 

334 prologue 

335 [[[cog 

336 cog.outl("x") 

337 cog.outl() 

338 cog.outl("y") 

339 cog.out() # Also optional, a complete no-op. 

340 cog.outl(trimblanklines=True) 

341 cog.outl("z") 

342 ]]] 

343 x 

344 

345 y 

346 

347 z 

348 [[[end]]] 

349 epilogue 

350 """ 

351 

352 infile = reindentBlock(infile) 

353 self.assertEqual(Cog().processString(infile), infile) 

354 

355 def testFirstLineNum(self): 

356 infile = """\ 

357 fooey 

358 [[[cog 

359 cog.outl("started at line number %d" % cog.firstLineNum) 

360 ]]] 

361 started at line number 2 

362 [[[end]]] 

363 blah blah 

364 [[[cog 

365 cog.outl("and again at line %d" % cog.firstLineNum) 

366 ]]] 

367 and again at line 8 

368 [[[end]]] 

369 """ 

370 

371 infile = reindentBlock(infile) 

372 self.assertEqual(Cog().processString(infile), infile) 

373 

374 def testCompactOneLineCode(self): 

375 infile = """\ 

376 first line 

377 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky! 

378 get rid of this! 

379 [[[end]]] 

380 last line 

381 """ 

382 

383 outfile = """\ 

384 first line 

385 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky! 

386 hello 81 

387 [[[end]]] 

388 last line 

389 """ 

390 

391 infile = reindentBlock(infile) 

392 self.assertEqual(Cog().processString(infile), reindentBlock(outfile)) 

393 

394 def testInsideOutCompact(self): 

395 infile = """\ 

396 first line 

397 hey?: ]]] what is this? [[[cog strange! 

398 get rid of this! 

399 [[[end]]] 

400 last line 

401 """ 

402 with self.assertRaisesRegex(CogError, r"^infile.txt\(2\): Cog code markers inverted$"): 

403 Cog().processString(reindentBlock(infile), "infile.txt") 

404 

405 def testSharingGlobals(self): 

406 infile = """\ 

407 first line 

408 hey: [[[cog s="hey there" ]]] looky! 

409 [[[end]]] 

410 more literal junk. 

411 [[[cog cog.outl(s) ]]] 

412 [[[end]]] 

413 last line 

414 """ 

415 

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 """ 

426 

427 infile = reindentBlock(infile) 

428 self.assertEqual(Cog().processString(infile), reindentBlock(outfile)) 

429 

430 def testAssertInCogCode(self): 

431 # Check that we can test assertions in cog code in the test framework. 

432 infile = """\ 

433 [[[cog 

434 assert 1 == 2, "Oops" 

435 ]]] 

436 [[[end]]] 

437 """ 

438 infile = reindentBlock(infile) 

439 with self.assertRaisesRegex(CogUserException, "AssertionError: Oops"): 

440 Cog().processString(infile) 

441 

442 def testCogPrevious(self): 

443 # Check that we can access the previous run's output. 

444 infile = """\ 

445 [[[cog 

446 assert cog.previous == "Hello there!\\n", "WTF??" 

447 cog.out(cog.previous) 

448 cog.outl("Ran again!") 

449 ]]] 

450 Hello there! 

451 [[[end]]] 

452 """ 

453 

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 """ 

464 

465 infile = reindentBlock(infile) 

466 self.assertEqual(Cog().processString(infile), reindentBlock(outfile)) 

467 

468 

469class CogOptionsTests(TestCase): 

470 """ Test the CogOptions class. 

471 """ 

472 

473 def testEquality(self): 

474 o = CogOptions() 

475 p = CogOptions() 

476 self.assertEqual(o, p) 

477 o.parseArgs(['-r']) 

478 self.assertNotEqual(o, p) 

479 p.parseArgs(['-r']) 

480 self.assertEqual(o, p) 

481 

482 def testCloning(self): 

483 o = CogOptions() 

484 o.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/']) 

485 p = o.clone() 

486 self.assertEqual(o, p) 

487 p.parseArgs(['-I', 'huey', '-D', 'foo=quux']) 

488 self.assertNotEqual(o, p) 

489 q = CogOptions() 

490 q.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/', '-I', 'huey', '-D', 'foo=quux']) 

491 self.assertEqual(p, q) 

492 

493 def testCombiningFlags(self): 

494 # Single-character flags can be combined. 

495 o = CogOptions() 

496 o.parseArgs(['-e', '-r', '-z']) 

497 p = CogOptions() 

498 p.parseArgs(['-erz']) 

499 self.assertEqual(o, p) 

500 

501 def testMarkers(self): 

502 o = CogOptions() 

503 o._parse_markers('a b c') 

504 self.assertEqual('a', o.sBeginSpec) 

505 self.assertEqual('b', o.sEndSpec) 

506 self.assertEqual('c', o.sEndOutput) 

507 

508 def testMarkersSwitch(self): 

509 o = CogOptions() 

510 o.parseArgs(['--markers', 'a b c']) 

511 self.assertEqual('a', o.sBeginSpec) 

512 self.assertEqual('b', o.sEndSpec) 

513 self.assertEqual('c', o.sEndOutput) 

514 

515 

516class FileStructureTests(TestCase): 

517 """ Test cases to check that we're properly strict about the structure 

518 of files. 

519 """ 

520 

521 def isBad(self, infile, msg=None): 

522 infile = reindentBlock(infile) 

523 with self.assertRaisesRegex(CogError, "^"+re.escape(msg)+"$"): 

524 Cog().processString(infile, 'infile.txt') 

525 

526 def testBeginNoEnd(self): 

527 infile = """\ 

528 Fooey 

529 #[[[cog 

530 cog.outl('hello') 

531 """ 

532 self.isBad(infile, "infile.txt(2): Cog block begun but never ended.") 

533 

534 def testNoEoo(self): 

535 infile = """\ 

536 Fooey 

537 #[[[cog 

538 cog.outl('hello') 

539 #]]] 

540 """ 

541 self.isBad(infile, "infile.txt(4): Missing '[[[end]]]' before end of file.") 

542 

543 infile2 = """\ 

544 Fooey 

545 #[[[cog 

546 cog.outl('hello') 

547 #]]] 

548 #[[[cog 

549 cog.outl('goodbye') 

550 #]]] 

551 """ 

552 self.isBad(infile2, "infile.txt(5): Unexpected '[[[cog'") 

553 

554 def testStartWithEnd(self): 

555 infile = """\ 

556 #]]] 

557 """ 

558 self.isBad(infile, "infile.txt(1): Unexpected ']]]'") 

559 

560 infile2 = """\ 

561 #[[[cog 

562 cog.outl('hello') 

563 #]]] 

564 #[[[end]]] 

565 #]]] 

566 """ 

567 self.isBad(infile2, "infile.txt(5): Unexpected ']]]'") 

568 

569 def testStartWithEoo(self): 

570 infile = """\ 

571 #[[[end]]] 

572 """ 

573 self.isBad(infile, "infile.txt(1): Unexpected '[[[end]]]'") 

574 

575 infile2 = """\ 

576 #[[[cog 

577 cog.outl('hello') 

578 #]]] 

579 #[[[end]]] 

580 #[[[end]]] 

581 """ 

582 self.isBad(infile2, "infile.txt(5): Unexpected '[[[end]]]'") 

583 

584 def testNoEnd(self): 

585 infile = """\ 

586 #[[[cog 

587 cog.outl("hello") 

588 #[[[end]]] 

589 """ 

590 self.isBad(infile, "infile.txt(3): Unexpected '[[[end]]]'") 

591 

592 infile2 = """\ 

593 #[[[cog 

594 cog.outl('hello') 

595 #]]] 

596 #[[[end]]] 

597 #[[[cog 

598 cog.outl("hello") 

599 #[[[end]]] 

600 """ 

601 self.isBad(infile2, "infile.txt(7): Unexpected '[[[end]]]'") 

602 

603 def testTwoBegins(self): 

604 infile = """\ 

605 #[[[cog 

606 #[[[cog 

607 cog.outl("hello") 

608 #]]] 

609 #[[[end]]] 

610 """ 

611 self.isBad(infile, "infile.txt(2): Unexpected '[[[cog'") 

612 

613 infile2 = """\ 

614 #[[[cog 

615 cog.outl("hello") 

616 #]]] 

617 #[[[end]]] 

618 #[[[cog 

619 #[[[cog 

620 cog.outl("hello") 

621 #]]] 

622 #[[[end]]] 

623 """ 

624 self.isBad(infile2, "infile.txt(6): Unexpected '[[[cog'") 

625 

626 def testTwoEnds(self): 

627 infile = """\ 

628 #[[[cog 

629 cog.outl("hello") 

630 #]]] 

631 #]]] 

632 #[[[end]]] 

633 """ 

634 self.isBad(infile, "infile.txt(4): Unexpected ']]]'") 

635 

636 infile2 = """\ 

637 #[[[cog 

638 cog.outl("hello") 

639 #]]] 

640 #[[[end]]] 

641 #[[[cog 

642 cog.outl("hello") 

643 #]]] 

644 #]]] 

645 #[[[end]]] 

646 """ 

647 self.isBad(infile2, "infile.txt(8): Unexpected ']]]'") 

648 

649 

650class CogErrorTests(TestCase): 

651 """ Test cases for cog.error(). 

652 """ 

653 

654 def testErrorMsg(self): 

655 infile = """\ 

656 [[[cog cog.error("This ain't right!")]]] 

657 [[[end]]] 

658 """ 

659 

660 infile = reindentBlock(infile) 

661 with self.assertRaisesRegex(CogGeneratedError, "^This ain't right!$"): 

662 Cog().processString(infile) 

663 

664 def testErrorNoMsg(self): 

665 infile = """\ 

666 [[[cog cog.error()]]] 

667 [[[end]]] 

668 """ 

669 

670 infile = reindentBlock(infile) 

671 with self.assertRaisesRegex(CogGeneratedError, "^Error raised by cog generator.$"): 

672 Cog().processString(infile) 

673 

674 def testNoErrorIfErrorNotCalled(self): 

675 infile = """\ 

676 --[[[cog 

677 --import cog 

678 --for i in range(3): 

679 -- if i > 10: 

680 -- cog.error("Something is amiss!") 

681 -- cog.out("xx%d\\n" % i) 

682 --]]] 

683 xx0 

684 xx1 

685 xx2 

686 --[[[end]]] 

687 """ 

688 

689 infile = reindentBlock(infile) 

690 self.assertEqual(Cog().processString(infile), infile) 

691 

692 

693class CogGeneratorGetCodeTests(TestCase): 

694 """ Unit tests against CogGenerator to see if its getCode() method works 

695 properly. 

696 """ 

697 

698 def setUp(self): 

699 """ All tests get a generator to use, and short same-length names for 

700 the functions we're going to use. 

701 """ 

702 self.gen = CogGenerator() 

703 self.m = self.gen.parseMarker 

704 self.l = self.gen.parseLine 

705 

706 def testEmpty(self): 

707 self.m('// [[[cog') 

708 self.m('// ]]]') 

709 self.assertEqual(self.gen.getCode(), '') 

710 

711 def testSimple(self): 

712 self.m('// [[[cog') 

713 self.l(' print "hello"') 

714 self.l(' print "bye"') 

715 self.m('// ]]]') 

716 self.assertEqual(self.gen.getCode(), 'print "hello"\nprint "bye"') 

717 

718 def testCompressed1(self): 

719 # For a while, I supported compressed code blocks, but no longer. 

720 self.m('// [[[cog: print """') 

721 self.l('// hello') 

722 self.l('// bye') 

723 self.m('// """)]]]') 

724 self.assertEqual(self.gen.getCode(), 'hello\nbye') 

725 

726 def testCompressed2(self): 

727 # For a while, I supported compressed code blocks, but no longer. 

728 self.m('// [[[cog: print """') 

729 self.l('hello') 

730 self.l('bye') 

731 self.m('// """)]]]') 

732 self.assertEqual(self.gen.getCode(), 'hello\nbye') 

733 

734 def testCompressed3(self): 

735 # For a while, I supported compressed code blocks, but no longer. 

736 self.m('// [[[cog') 

737 self.l('print """hello') 

738 self.l('bye') 

739 self.m('// """)]]]') 

740 self.assertEqual(self.gen.getCode(), 'print """hello\nbye') 

741 

742 def testCompressed4(self): 

743 # For a while, I supported compressed code blocks, but no longer. 

744 self.m('// [[[cog: print """') 

745 self.l('hello') 

746 self.l('bye""")') 

747 self.m('// ]]]') 

748 self.assertEqual(self.gen.getCode(), 'hello\nbye""")') 

749 

750 def testNoCommonPrefixForMarkers(self): 

751 # It's important to be able to use #if 0 to hide lines from a 

752 # C++ compiler. 

753 self.m('#if 0 //[[[cog') 

754 self.l('\timport cog, sys') 

755 self.l('') 

756 self.l('\tprint sys.argv') 

757 self.m('#endif //]]]') 

758 self.assertEqual(self.gen.getCode(), 'import cog, sys\n\nprint sys.argv') 

759 

760 

761class TestCaseWithTempDir(TestCase): 

762 

763 def newCog(self): 

764 """ Initialize the cog members for another run. 

765 """ 

766 # Create a cog engine, and catch its output. 

767 self.cog = Cog() 

768 self.output = io.StringIO() 

769 self.cog.setOutput(stdout=self.output, stderr=self.output) 

770 

771 def setUp(self): 

772 # Create a temporary directory. 

773 self.tempdir = os.path.join(tempfile.gettempdir(), 'testcog_tempdir_' + str(random.random())[2:]) 

774 os.mkdir(self.tempdir) 

775 self.olddir = os.getcwd() 

776 os.chdir(self.tempdir) 

777 self.newCog() 

778 

779 def tearDown(self): 

780 os.chdir(self.olddir) 

781 # Get rid of the temporary directory. 

782 shutil.rmtree(self.tempdir) 

783 

784 def assertFilesSame(self, sFName1, sFName2): 

785 text1 = open(os.path.join(self.tempdir, sFName1), 'rb').read() 

786 text2 = open(os.path.join(self.tempdir, sFName2), 'rb').read() 

787 self.assertEqual(text1, text2) 

788 

789 def assertFileContent(self, sFName, sContent): 

790 sAbsName = os.path.join(self.tempdir, sFName) 

791 f = open(sAbsName, 'rb') 

792 try: 

793 sFileContent = f.read() 

794 finally: 

795 f.close() 

796 self.assertEqual(sFileContent, sContent.encode("utf-8")) 

797 

798 

799class ArgumentHandlingTests(TestCaseWithTempDir): 

800 

801 def testArgumentFailure(self): 

802 # Return value 2 means usage problem. 

803 self.assertEqual(self.cog.main(['argv0', '-j']), 2) 

804 output = self.output.getvalue() 

805 self.assertIn("option -j not recognized", output) 

806 with self.assertRaisesRegex(CogUsageError, r"^No files to process$"): 

807 self.cog.callableMain(['argv0']) 

808 with self.assertRaisesRegex(CogUsageError, r"^option -j not recognized$"): 

809 self.cog.callableMain(['argv0', '-j']) 

810 

811 def testNoDashOAndAtFile(self): 

812 d = { 

813 'cogfiles.txt': """\ 

814 # Please run cog 

815 """ 

816 } 

817 

818 makeFiles(d) 

819 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with @file$"): 

820 self.cog.callableMain(['argv0', '-o', 'foo', '@cogfiles.txt']) 

821 

822 def testDashV(self): 

823 self.assertEqual(self.cog.main(['argv0', '-v']), 0) 

824 output = self.output.getvalue() 

825 self.assertEqual('Cog version %s\n' % __version__, output) 

826 

827 def producesHelp(self, args): 

828 self.newCog() 

829 argv = ['argv0'] + args.split() 

830 self.assertEqual(self.cog.main(argv), 0) 

831 self.assertEqual(usage, self.output.getvalue()) 

832 

833 def testDashH(self): 

834 # -h or -? anywhere on the command line should just print help. 

835 self.producesHelp("-h") 

836 self.producesHelp("-?") 

837 self.producesHelp("fooey.txt -h") 

838 self.producesHelp("-o -r @fooey.txt -? @booey.txt") 

839 

840 def testDashOAndDashR(self): 

841 d = { 

842 'cogfile.txt': """\ 

843 # Please run cog 

844 """ 

845 } 

846 

847 makeFiles(d) 

848 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with -r \(they are opposites\)$"): 

849 self.cog.callableMain(['argv0', '-o', 'foo', '-r', 'cogfile.txt']) 

850 

851 def testDashZ(self): 

852 d = { 

853 'test.cog': """\ 

854 // This is my C++ file. 

855 //[[[cog 

856 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

857 for fn in fnames: 

858 cog.outl("void %s();" % fn) 

859 //]]] 

860 """, 

861 

862 'test.out': """\ 

863 // This is my C++ file. 

864 //[[[cog 

865 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

866 for fn in fnames: 

867 cog.outl("void %s();" % fn) 

868 //]]] 

869 void DoSomething(); 

870 void DoAnotherThing(); 

871 void DoLastThing(); 

872 """, 

873 } 

874 

875 makeFiles(d) 

876 with self.assertRaisesRegex(CogError, r"^test.cog\(6\): Missing '\[\[\[end\]\]\]' before end of file.$"): 

877 self.cog.callableMain(['argv0', '-r', 'test.cog']) 

878 self.newCog() 

879 self.cog.callableMain(['argv0', '-r', '-z', 'test.cog']) 

880 self.assertFilesSame('test.cog', 'test.out') 

881 

882 def testBadDashD(self): 

883 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"): 

884 self.cog.callableMain(['argv0', '-Dfooey', 'cog.txt']) 

885 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"): 

886 self.cog.callableMain(['argv0', '-D', 'fooey', 'cog.txt']) 

887 

888 def testBadMarkers(self): 

889 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'X'$"): 

890 self.cog.callableMain(['argv0', '--markers=X']) 

891 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'A B C D'$"): 

892 self.cog.callableMain(['argv0', '--markers=A B C D']) 

893 

894 

895class TestMain(TestCaseWithTempDir): 

896 def setUp(self): 

897 super().setUp() 

898 self.old_argv = sys.argv[:] 

899 self.old_stderr = sys.stderr 

900 sys.stderr = io.StringIO() 

901 

902 def tearDown(self): 

903 sys.stderr = self.old_stderr 

904 sys.argv = self.old_argv 

905 sys.modules.pop('mycode', None) 

906 super().tearDown() 

907 

908 def test_main_function(self): 

909 sys.argv = ["argv0", "-Z"] 

910 ret = main() 

911 self.assertEqual(ret, 2) 

912 stderr = sys.stderr.getvalue() 

913 self.assertEqual(stderr, 'option -Z not recognized\n(for help use -h)\n') 

914 

915 files = { 

916 'test.cog': """\ 

917 //[[[cog 

918 def func(): 

919 import mycode 

920 mycode.boom() 

921 //]]] 

922 //[[[end]]] 

923 ----- 

924 //[[[cog 

925 func() 

926 //]]] 

927 //[[[end]]] 

928 """, 

929 

930 'mycode.py': """\ 

931 def boom(): 

932 [][0] 

933 """, 

934 } 

935 

936 def test_error_report(self): 

937 self.check_error_report() 

938 

939 def test_error_report_with_prologue(self): 

940 self.check_error_report("-p", "#1\n#2") 

941 

942 def check_error_report(self, *args): 

943 """Check that the error report is right.""" 

944 makeFiles(self.files) 

945 sys.argv = ["argv0"] + list(args) + ["-r", "test.cog"] 

946 main() 

947 expected = reindentBlock("""\ 

948 Traceback (most recent call last): 

949 File "test.cog", line 9, in <module> 

950 func() 

951 File "test.cog", line 4, in func 

952 mycode.boom() 

953 File "MYCODE", line 2, in boom 

954 [][0] 

955 IndexError: list index out of range 

956 """) 

957 expected = expected.replace("MYCODE", os.path.abspath("mycode.py")) 

958 assert expected == sys.stderr.getvalue() 

959 

960 def test_error_in_prologue(self): 

961 makeFiles(self.files) 

962 sys.argv = ["argv0", "-p", "import mycode; mycode.boom()", "-r", "test.cog"] 

963 main() 

964 expected = reindentBlock("""\ 

965 Traceback (most recent call last): 

966 File "<prologue>", line 1, in <module> 

967 import mycode; mycode.boom() 

968 File "MYCODE", line 2, in boom 

969 [][0] 

970 IndexError: list index out of range 

971 """) 

972 expected = expected.replace("MYCODE", os.path.abspath("mycode.py")) 

973 assert expected == sys.stderr.getvalue() 

974 

975 

976 

977class TestFileHandling(TestCaseWithTempDir): 

978 

979 def testSimple(self): 

980 d = { 

981 'test.cog': """\ 

982 // This is my C++ file. 

983 //[[[cog 

984 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

985 for fn in fnames: 

986 cog.outl("void %s();" % fn) 

987 //]]] 

988 //[[[end]]] 

989 """, 

990 

991 'test.out': """\ 

992 // This is my C++ file. 

993 //[[[cog 

994 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

995 for fn in fnames: 

996 cog.outl("void %s();" % fn) 

997 //]]] 

998 void DoSomething(); 

999 void DoAnotherThing(); 

1000 void DoLastThing(); 

1001 //[[[end]]] 

1002 """, 

1003 } 

1004 

1005 makeFiles(d) 

1006 self.cog.callableMain(['argv0', '-r', 'test.cog']) 

1007 self.assertFilesSame('test.cog', 'test.out') 

1008 output = self.output.getvalue() 

1009 self.assertIn("(changed)", output) 

1010 

1011 def testPrintOutput(self): 

1012 d = { 

1013 'test.cog': """\ 

1014 // This is my C++ file. 

1015 //[[[cog 

1016 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

1017 for fn in fnames: 

1018 print("void %s();" % fn) 

1019 //]]] 

1020 //[[[end]]] 

1021 """, 

1022 

1023 'test.out': """\ 

1024 // This is my C++ file. 

1025 //[[[cog 

1026 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

1027 for fn in fnames: 

1028 print("void %s();" % fn) 

1029 //]]] 

1030 void DoSomething(); 

1031 void DoAnotherThing(); 

1032 void DoLastThing(); 

1033 //[[[end]]] 

1034 """, 

1035 } 

1036 

1037 makeFiles(d) 

1038 self.cog.callableMain(['argv0', '-rP', 'test.cog']) 

1039 self.assertFilesSame('test.cog', 'test.out') 

1040 output = self.output.getvalue() 

1041 self.assertIn("(changed)", output) 

1042 

1043 def testWildcards(self): 

1044 d = { 

1045 'test.cog': """\ 

1046 // This is my C++ file. 

1047 //[[[cog 

1048 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

1049 for fn in fnames: 

1050 cog.outl("void %s();" % fn) 

1051 //]]] 

1052 //[[[end]]] 

1053 """, 

1054 

1055 'test2.cog': """\ 

1056 // This is my C++ file. 

1057 //[[[cog 

1058 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

1059 for fn in fnames: 

1060 cog.outl("void %s();" % fn) 

1061 //]]] 

1062 //[[[end]]] 

1063 """, 

1064 

1065 'test.out': """\ 

1066 // This is my C++ file. 

1067 //[[[cog 

1068 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

1069 for fn in fnames: 

1070 cog.outl("void %s();" % fn) 

1071 //]]] 

1072 void DoSomething(); 

1073 void DoAnotherThing(); 

1074 void DoLastThing(); 

1075 //[[[end]]] 

1076 """, 

1077 

1078 'not_this_one.cog': """\ 

1079 // This is my C++ file. 

1080 //[[[cog 

1081 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

1082 for fn in fnames: 

1083 cog.outl("void %s();" % fn) 

1084 //]]] 

1085 //[[[end]]] 

1086 """, 

1087 

1088 'not_this_one.out': """\ 

1089 // This is my C++ file. 

1090 //[[[cog 

1091 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

1092 for fn in fnames: 

1093 cog.outl("void %s();" % fn) 

1094 //]]] 

1095 //[[[end]]] 

1096 """, 

1097 } 

1098 

1099 makeFiles(d) 

1100 self.cog.callableMain(['argv0', '-r', 't*.cog']) 

1101 self.assertFilesSame('test.cog', 'test.out') 

1102 self.assertFilesSame('test2.cog', 'test.out') 

1103 self.assertFilesSame('not_this_one.cog', 'not_this_one.out') 

1104 output = self.output.getvalue() 

1105 self.assertIn("(changed)", output) 

1106 

1107 def testOutputFile(self): 

1108 # -o sets the output file. 

1109 d = { 

1110 'test.cog': """\ 

1111 // This is my C++ file. 

1112 //[[[cog 

1113 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

1114 for fn in fnames: 

1115 cog.outl("void %s();" % fn) 

1116 //]]] 

1117 //[[[end]]] 

1118 """, 

1119 

1120 'test.out': """\ 

1121 // This is my C++ file. 

1122 //[[[cog 

1123 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

1124 for fn in fnames: 

1125 cog.outl("void %s();" % fn) 

1126 //]]] 

1127 void DoSomething(); 

1128 void DoAnotherThing(); 

1129 void DoLastThing(); 

1130 //[[[end]]] 

1131 """, 

1132 } 

1133 

1134 makeFiles(d) 

1135 self.cog.callableMain(['argv0', '-o', 'in/a/dir/test.cogged', 'test.cog']) 

1136 self.assertFilesSame('in/a/dir/test.cogged', 'test.out') 

1137 

1138 def testAtFile(self): 

1139 d = { 

1140 'one.cog': """\ 

1141 //[[[cog 

1142 cog.outl("hello world") 

1143 //]]] 

1144 //[[[end]]] 

1145 """, 

1146 

1147 'one.out': """\ 

1148 //[[[cog 

1149 cog.outl("hello world") 

1150 //]]] 

1151 hello world 

1152 //[[[end]]] 

1153 """, 

1154 

1155 'two.cog': """\ 

1156 //[[[cog 

1157 cog.outl("goodbye cruel world") 

1158 //]]] 

1159 //[[[end]]] 

1160 """, 

1161 

1162 'two.out': """\ 

1163 //[[[cog 

1164 cog.outl("goodbye cruel world") 

1165 //]]] 

1166 goodbye cruel world 

1167 //[[[end]]] 

1168 """, 

1169 

1170 'cogfiles.txt': """\ 

1171 # Please run cog 

1172 one.cog 

1173 

1174 two.cog 

1175 """ 

1176 } 

1177 

1178 makeFiles(d) 

1179 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt']) 

1180 self.assertFilesSame('one.cog', 'one.out') 

1181 self.assertFilesSame('two.cog', 'two.out') 

1182 output = self.output.getvalue() 

1183 self.assertIn("(changed)", output) 

1184 

1185 def testNestedAtFile(self): 

1186 d = { 

1187 'one.cog': """\ 

1188 //[[[cog 

1189 cog.outl("hello world") 

1190 //]]] 

1191 //[[[end]]] 

1192 """, 

1193 

1194 'one.out': """\ 

1195 //[[[cog 

1196 cog.outl("hello world") 

1197 //]]] 

1198 hello world 

1199 //[[[end]]] 

1200 """, 

1201 

1202 'two.cog': """\ 

1203 //[[[cog 

1204 cog.outl("goodbye cruel world") 

1205 //]]] 

1206 //[[[end]]] 

1207 """, 

1208 

1209 'two.out': """\ 

1210 //[[[cog 

1211 cog.outl("goodbye cruel world") 

1212 //]]] 

1213 goodbye cruel world 

1214 //[[[end]]] 

1215 """, 

1216 

1217 'cogfiles.txt': """\ 

1218 # Please run cog 

1219 one.cog 

1220 @cogfiles2.txt 

1221 """, 

1222 

1223 'cogfiles2.txt': """\ 

1224 # This one too, please. 

1225 two.cog 

1226 """, 

1227 } 

1228 

1229 makeFiles(d) 

1230 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt']) 

1231 self.assertFilesSame('one.cog', 'one.out') 

1232 self.assertFilesSame('two.cog', 'two.out') 

1233 output = self.output.getvalue() 

1234 self.assertIn("(changed)", output) 

1235 

1236 def testAtFileWithArgs(self): 

1237 d = { 

1238 'both.cog': """\ 

1239 //[[[cog 

1240 cog.outl("one: %s" % ('one' in globals())) 

1241 cog.outl("two: %s" % ('two' in globals())) 

1242 //]]] 

1243 //[[[end]]] 

1244 """, 

1245 

1246 'one.out': """\ 

1247 //[[[cog 

1248 cog.outl("one: %s" % ('one' in globals())) 

1249 cog.outl("two: %s" % ('two' in globals())) 

1250 //]]] 

1251 one: True // ONE 

1252 two: False // ONE 

1253 //[[[end]]] 

1254 """, 

1255 

1256 'two.out': """\ 

1257 //[[[cog 

1258 cog.outl("one: %s" % ('one' in globals())) 

1259 cog.outl("two: %s" % ('two' in globals())) 

1260 //]]] 

1261 one: False // TWO 

1262 two: True // TWO 

1263 //[[[end]]] 

1264 """, 

1265 

1266 'cogfiles.txt': """\ 

1267 # Please run cog 

1268 both.cog -o in/a/dir/both.one -s ' // ONE' -D one=x 

1269 both.cog -o in/a/dir/both.two -s ' // TWO' -D two=x 

1270 """ 

1271 } 

1272 

1273 makeFiles(d) 

1274 self.cog.callableMain(['argv0', '@cogfiles.txt']) 

1275 self.assertFilesSame('in/a/dir/both.one', 'one.out') 

1276 self.assertFilesSame('in/a/dir/both.two', 'two.out') 

1277 

1278 def testAtFileWithBadArgCombo(self): 

1279 d = { 

1280 'both.cog': """\ 

1281 //[[[cog 

1282 cog.outl("one: %s" % ('one' in globals())) 

1283 cog.outl("two: %s" % ('two' in globals())) 

1284 //]]] 

1285 //[[[end]]] 

1286 """, 

1287 

1288 'cogfiles.txt': """\ 

1289 # Please run cog 

1290 both.cog 

1291 both.cog -d # This is bad: -r and -d 

1292 """ 

1293 } 

1294 

1295 makeFiles(d) 

1296 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"): 

1297 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt']) 

1298 

1299 def testAtFileWithTrickyFilenames(self): 

1300 def fix_backslashes(files_txt): 

1301 """Make the contents of a files.txt sensitive to the platform.""" 

1302 if sys.platform != "win32": 

1303 files_txt = files_txt.replace("\\", "/") 

1304 return files_txt 

1305 

1306 d = { 

1307 'one 1.cog': """\ 

1308 //[[[cog cog.outl("hello world") ]]] 

1309 """, 

1310 

1311 'one.out': """\ 

1312 //[[[cog cog.outl("hello world") ]]] 

1313 hello world //xxx 

1314 """, 

1315 

1316 'subdir': { 

1317 'subback.cog': """\ 

1318 //[[[cog cog.outl("down deep with backslashes") ]]] 

1319 """, 

1320 

1321 'subfwd.cog': """\ 

1322 //[[[cog cog.outl("down deep with slashes") ]]] 

1323 """, 

1324 }, 

1325 

1326 'subback.out': """\ 

1327 //[[[cog cog.outl("down deep with backslashes") ]]] 

1328 down deep with backslashes //yyy 

1329 """, 

1330 

1331 'subfwd.out': """\ 

1332 //[[[cog cog.outl("down deep with slashes") ]]] 

1333 down deep with slashes //zzz 

1334 """, 

1335 

1336 'cogfiles.txt': fix_backslashes("""\ 

1337 # Please run cog 

1338 'one 1.cog' -s ' //xxx' 

1339 subdir\\subback.cog -s ' //yyy' 

1340 subdir/subfwd.cog -s ' //zzz' 

1341 """) 

1342 } 

1343 

1344 makeFiles(d) 

1345 self.cog.callableMain(['argv0', '-z', '-r', '@cogfiles.txt']) 

1346 self.assertFilesSame('one 1.cog', 'one.out') 

1347 self.assertFilesSame('subdir/subback.cog', 'subback.out') 

1348 self.assertFilesSame('subdir/subfwd.cog', 'subfwd.out') 

1349 

1350 def run_with_verbosity(self, verbosity): 

1351 d = { 

1352 'unchanged.cog': """\ 

1353 //[[[cog 

1354 cog.outl("hello world") 

1355 //]]] 

1356 hello world 

1357 //[[[end]]] 

1358 """, 

1359 

1360 'changed.cog': """\ 

1361 //[[[cog 

1362 cog.outl("goodbye cruel world") 

1363 //]]] 

1364 //[[[end]]] 

1365 """, 

1366 

1367 'cogfiles.txt': """\ 

1368 unchanged.cog 

1369 changed.cog 

1370 """ 

1371 } 

1372 

1373 makeFiles(d) 

1374 self.cog.callableMain(['argv0', '-r', '--verbosity='+verbosity, '@cogfiles.txt']) 

1375 output = self.output.getvalue() 

1376 return output 

1377 

1378 def test_verbosity0(self): 

1379 output = self.run_with_verbosity("0") 

1380 self.assertEqual(output, "") 

1381 

1382 def test_verbosity1(self): 

1383 output = self.run_with_verbosity("1") 

1384 self.assertEqual(output, "Cogging changed.cog (changed)\n") 

1385 

1386 def test_verbosity2(self): 

1387 output = self.run_with_verbosity("2") 

1388 self.assertEqual(output, "Cogging unchanged.cog\nCogging changed.cog (changed)\n") 

1389 

1390 

1391class CogTestLineEndings(TestCaseWithTempDir): 

1392 """Tests for -U option (force LF line-endings in output).""" 

1393 

1394 lines_in = ['Some text.', 

1395 '//[[[cog', 

1396 'cog.outl("Cog text")', 

1397 '//]]]', 

1398 'gobbledegook.', 

1399 '//[[[end]]]', 

1400 'epilogue.', 

1401 ''] 

1402 

1403 lines_out = ['Some text.', 

1404 '//[[[cog', 

1405 'cog.outl("Cog text")', 

1406 '//]]]', 

1407 'Cog text', 

1408 '//[[[end]]]', 

1409 'epilogue.', 

1410 ''] 

1411 

1412 def testOutputNativeEol(self): 

1413 makeFiles({'infile': '\n'.join(self.lines_in)}) 

1414 self.cog.callableMain(['argv0', '-o', 'outfile', 'infile']) 

1415 self.assertFileContent('outfile', os.linesep.join(self.lines_out)) 

1416 

1417 def testOutputLfEol(self): 

1418 makeFiles({'infile': '\n'.join(self.lines_in)}) 

1419 self.cog.callableMain(['argv0', '-U', '-o', 'outfile', 'infile']) 

1420 self.assertFileContent('outfile', '\n'.join(self.lines_out)) 

1421 

1422 def testReplaceNativeEol(self): 

1423 makeFiles({'test.cog': '\n'.join(self.lines_in)}) 

1424 self.cog.callableMain(['argv0', '-r', 'test.cog']) 

1425 self.assertFileContent('test.cog', os.linesep.join(self.lines_out)) 

1426 

1427 def testReplaceLfEol(self): 

1428 makeFiles({'test.cog': '\n'.join(self.lines_in)}) 

1429 self.cog.callableMain(['argv0', '-U', '-r', 'test.cog']) 

1430 self.assertFileContent('test.cog', '\n'.join(self.lines_out)) 

1431 

1432 

1433class CogTestCharacterEncoding(TestCaseWithTempDir): 

1434 

1435 def testSimple(self): 

1436 d = { 

1437 'test.cog': b"""\ 

1438 // This is my C++ file. 

1439 //[[[cog 

1440 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)") 

1441 //]]] 

1442 //[[[end]]] 

1443 """, 

1444 

1445 'test.out': b"""\ 

1446 // This is my C++ file. 

1447 //[[[cog 

1448 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)") 

1449 //]]] 

1450 // Unicode: \xe1\x88\xb4 (U+1234) 

1451 //[[[end]]] 

1452 """.replace(b"\n", os.linesep.encode()), 

1453 } 

1454 

1455 makeFiles(d) 

1456 self.cog.callableMain(['argv0', '-r', 'test.cog']) 

1457 self.assertFilesSame('test.cog', 'test.out') 

1458 output = self.output.getvalue() 

1459 self.assertIn("(changed)", output) 

1460 

1461 def testFileEncodingOption(self): 

1462 d = { 

1463 'test.cog': b"""\ 

1464 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows 

1465 //[[[cog 

1466 cog.outl("\xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe") 

1467 //]]] 

1468 //[[[end]]] 

1469 """, 

1470 

1471 'test.out': b"""\ 

1472 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows 

1473 //[[[cog 

1474 cog.outl("\xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe") 

1475 //]]] 

1476 \xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe 

1477 //[[[end]]] 

1478 """.replace(b"\n", os.linesep.encode()), 

1479 } 

1480 

1481 makeFiles(d) 

1482 self.cog.callableMain(['argv0', '-n', 'cp1251', '-r', 'test.cog']) 

1483 self.assertFilesSame('test.cog', 'test.out') 

1484 output = self.output.getvalue() 

1485 self.assertIn("(changed)", output) 

1486 

1487 

1488class TestCaseWithImports(TestCaseWithTempDir): 

1489 """ When running tests which import modules, the sys.modules list 

1490 leaks from one test to the next. This test case class scrubs 

1491 the list after each run to keep the tests isolated from each other. 

1492 """ 

1493 

1494 def setUp(self): 

1495 super().setUp() 

1496 self.sysmodulekeys = list(sys.modules) 

1497 

1498 def tearDown(self): 

1499 modstoscrub = [ 

1500 modname 

1501 for modname in sys.modules 

1502 if modname not in self.sysmodulekeys 

1503 ] 

1504 for modname in modstoscrub: 

1505 del sys.modules[modname] 

1506 super().tearDown() 

1507 

1508 

1509class CogIncludeTests(TestCaseWithImports): 

1510 dincludes = { 

1511 'test.cog': """\ 

1512 //[[[cog 

1513 import mymodule 

1514 //]]] 

1515 //[[[end]]] 

1516 """, 

1517 

1518 'test.out': """\ 

1519 //[[[cog 

1520 import mymodule 

1521 //]]] 

1522 Hello from mymodule 

1523 //[[[end]]] 

1524 """, 

1525 

1526 'test2.out': """\ 

1527 //[[[cog 

1528 import mymodule 

1529 //]]] 

1530 Hello from mymodule in inc2 

1531 //[[[end]]] 

1532 """, 

1533 

1534 'include': { 

1535 'mymodule.py': """\ 

1536 import cog 

1537 cog.outl("Hello from mymodule") 

1538 """ 

1539 }, 

1540 

1541 'inc2': { 

1542 'mymodule.py': """\ 

1543 import cog 

1544 cog.outl("Hello from mymodule in inc2") 

1545 """ 

1546 }, 

1547 

1548 'inc3': { 

1549 'someothermodule.py': """\ 

1550 import cog 

1551 cog.outl("This is some other module.") 

1552 """ 

1553 }, 

1554 } 

1555 

1556 def testNeedIncludePath(self): 

1557 # Try it without the -I, to see that an ImportError happens. 

1558 makeFiles(self.dincludes) 

1559 msg = "(ImportError|ModuleNotFoundError): No module named '?mymodule'?" 

1560 with self.assertRaisesRegex(CogUserException, msg): 

1561 self.cog.callableMain(['argv0', '-r', 'test.cog']) 

1562 

1563 def testIncludePath(self): 

1564 # Test that -I adds include directories properly. 

1565 makeFiles(self.dincludes) 

1566 self.cog.callableMain(['argv0', '-r', '-I', 'include', 'test.cog']) 

1567 self.assertFilesSame('test.cog', 'test.out') 

1568 

1569 def testTwoIncludePaths(self): 

1570 # Test that two -I's add include directories properly. 

1571 makeFiles(self.dincludes) 

1572 self.cog.callableMain(['argv0', '-r', '-I', 'include', '-I', 'inc2', 'test.cog']) 

1573 self.assertFilesSame('test.cog', 'test.out') 

1574 

1575 def testTwoIncludePaths2(self): 

1576 # Test that two -I's add include directories properly. 

1577 makeFiles(self.dincludes) 

1578 self.cog.callableMain(['argv0', '-r', '-I', 'inc2', '-I', 'include', 'test.cog']) 

1579 self.assertFilesSame('test.cog', 'test2.out') 

1580 

1581 def testUselessIncludePath(self): 

1582 # Test that the search will continue past the first directory. 

1583 makeFiles(self.dincludes) 

1584 self.cog.callableMain(['argv0', '-r', '-I', 'inc3', '-I', 'include', 'test.cog']) 

1585 self.assertFilesSame('test.cog', 'test.out') 

1586 

1587 def testSysPathIsUnchanged(self): 

1588 d = { 

1589 'bad.cog': """\ 

1590 //[[[cog cog.error("Oh no!") ]]] 

1591 //[[[end]]] 

1592 """, 

1593 'good.cog': """\ 

1594 //[[[cog cog.outl("Oh yes!") ]]] 

1595 //[[[end]]] 

1596 """, 

1597 } 

1598 

1599 makeFiles(d) 

1600 # Is it unchanged just by creating a cog engine? 

1601 oldsyspath = sys.path[:] 

1602 self.newCog() 

1603 self.assertEqual(oldsyspath, sys.path) 

1604 # Is it unchanged for a successful run? 

1605 self.newCog() 

1606 self.cog.callableMain(['argv0', '-r', 'good.cog']) 

1607 self.assertEqual(oldsyspath, sys.path) 

1608 # Is it unchanged for a successful run with includes? 

1609 self.newCog() 

1610 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'good.cog']) 

1611 self.assertEqual(oldsyspath, sys.path) 

1612 # Is it unchanged for a successful run with two includes? 

1613 self.newCog() 

1614 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'good.cog']) 

1615 self.assertEqual(oldsyspath, sys.path) 

1616 # Is it unchanged for a failed run? 

1617 self.newCog() 

1618 with self.assertRaisesRegex(CogError, r"^Oh no!$"): 

1619 self.cog.callableMain(['argv0', '-r', 'bad.cog']) 

1620 self.assertEqual(oldsyspath, sys.path) 

1621 # Is it unchanged for a failed run with includes? 

1622 self.newCog() 

1623 with self.assertRaisesRegex(CogError, r"^Oh no!$"): 

1624 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'bad.cog']) 

1625 self.assertEqual(oldsyspath, sys.path) 

1626 # Is it unchanged for a failed run with two includes? 

1627 self.newCog() 

1628 with self.assertRaisesRegex(CogError, r"^Oh no!$"): 

1629 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'bad.cog']) 

1630 self.assertEqual(oldsyspath, sys.path) 

1631 

1632 def testSubDirectories(self): 

1633 # Test that relative paths on the command line work, with includes. 

1634 

1635 d = { 

1636 'code': { 

1637 'test.cog': """\ 

1638 //[[[cog 

1639 import mysubmodule 

1640 //]]] 

1641 //[[[end]]] 

1642 """, 

1643 

1644 'test.out': """\ 

1645 //[[[cog 

1646 import mysubmodule 

1647 //]]] 

1648 Hello from mysubmodule 

1649 //[[[end]]] 

1650 """, 

1651 

1652 'mysubmodule.py': """\ 

1653 import cog 

1654 cog.outl("Hello from mysubmodule") 

1655 """ 

1656 } 

1657 } 

1658 

1659 makeFiles(d) 

1660 # We should be able to invoke cog without the -I switch, and it will 

1661 # auto-include the current directory 

1662 self.cog.callableMain(['argv0', '-r', 'code/test.cog']) 

1663 self.assertFilesSame('code/test.cog', 'code/test.out') 

1664 

1665 

1666class CogTestsInFiles(TestCaseWithTempDir): 

1667 

1668 def testWarnIfNoCogCode(self): 

1669 # Test that the -e switch warns if there is no Cog code. 

1670 d = { 

1671 'with.cog': """\ 

1672 //[[[cog 

1673 cog.outl("hello world") 

1674 //]]] 

1675 hello world 

1676 //[[[end]]] 

1677 """, 

1678 

1679 'without.cog': """\ 

1680 There's no cog 

1681 code in this file. 

1682 """, 

1683 } 

1684 

1685 makeFiles(d) 

1686 self.cog.callableMain(['argv0', '-e', 'with.cog']) 

1687 output = self.output.getvalue() 

1688 self.assertNotIn("Warning", output) 

1689 self.newCog() 

1690 self.cog.callableMain(['argv0', '-e', 'without.cog']) 

1691 output = self.output.getvalue() 

1692 self.assertIn("Warning: no cog code found in without.cog", output) 

1693 self.newCog() 

1694 self.cog.callableMain(['argv0', 'without.cog']) 

1695 output = self.output.getvalue() 

1696 self.assertNotIn("Warning", output) 

1697 

1698 def testFileNameProps(self): 

1699 d = { 

1700 'cog1.txt': """\ 

1701 //[[[cog 

1702 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile)) 

1703 //]]] 

1704 this is cog1.txt in, cog1.txt out 

1705 [[[end]]] 

1706 """, 

1707 

1708 'cog1.out': """\ 

1709 //[[[cog 

1710 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile)) 

1711 //]]] 

1712 This is cog1.txt in, cog1.txt out 

1713 [[[end]]] 

1714 """, 

1715 

1716 'cog1out.out': """\ 

1717 //[[[cog 

1718 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile)) 

1719 //]]] 

1720 This is cog1.txt in, cog1out.txt out 

1721 [[[end]]] 

1722 """, 

1723 } 

1724 

1725 makeFiles(d) 

1726 self.cog.callableMain(['argv0', '-r', 'cog1.txt']) 

1727 self.assertFilesSame('cog1.txt', 'cog1.out') 

1728 self.newCog() 

1729 self.cog.callableMain(['argv0', '-o', 'cog1out.txt', 'cog1.txt']) 

1730 self.assertFilesSame('cog1out.txt', 'cog1out.out') 

1731 

1732 def testGlobalsDontCrossFiles(self): 

1733 # Make sure that global values don't get shared between files. 

1734 d = { 

1735 'one.cog': """\ 

1736 //[[[cog s = "This was set in one.cog" ]]] 

1737 //[[[end]]] 

1738 //[[[cog cog.outl(s) ]]] 

1739 //[[[end]]] 

1740 """, 

1741 

1742 'one.out': """\ 

1743 //[[[cog s = "This was set in one.cog" ]]] 

1744 //[[[end]]] 

1745 //[[[cog cog.outl(s) ]]] 

1746 This was set in one.cog 

1747 //[[[end]]] 

1748 """, 

1749 

1750 'two.cog': """\ 

1751 //[[[cog 

1752 try: 

1753 cog.outl(s) 

1754 except NameError: 

1755 cog.outl("s isn't set!") 

1756 //]]] 

1757 //[[[end]]] 

1758 """, 

1759 

1760 'two.out': """\ 

1761 //[[[cog 

1762 try: 

1763 cog.outl(s) 

1764 except NameError: 

1765 cog.outl("s isn't set!") 

1766 //]]] 

1767 s isn't set! 

1768 //[[[end]]] 

1769 """, 

1770 

1771 'cogfiles.txt': """\ 

1772 # Please run cog 

1773 one.cog 

1774 

1775 two.cog 

1776 """ 

1777 } 

1778 

1779 makeFiles(d) 

1780 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt']) 

1781 self.assertFilesSame('one.cog', 'one.out') 

1782 self.assertFilesSame('two.cog', 'two.out') 

1783 output = self.output.getvalue() 

1784 self.assertIn("(changed)", output) 

1785 

1786 def testRemoveGeneratedOutput(self): 

1787 d = { 

1788 'cog1.txt': """\ 

1789 //[[[cog 

1790 cog.outl("This line was generated.") 

1791 //]]] 

1792 This line was generated. 

1793 //[[[end]]] 

1794 This line was not. 

1795 """, 

1796 

1797 'cog1.out': """\ 

1798 //[[[cog 

1799 cog.outl("This line was generated.") 

1800 //]]] 

1801 //[[[end]]] 

1802 This line was not. 

1803 """, 

1804 

1805 'cog1.out2': """\ 

1806 //[[[cog 

1807 cog.outl("This line was generated.") 

1808 //]]] 

1809 This line was generated. 

1810 //[[[end]]] 

1811 This line was not. 

1812 """, 

1813 } 

1814 

1815 makeFiles(d) 

1816 # Remove generated output. 

1817 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt']) 

1818 self.assertFilesSame('cog1.txt', 'cog1.out') 

1819 self.newCog() 

1820 # Regenerate the generated output. 

1821 self.cog.callableMain(['argv0', '-r', 'cog1.txt']) 

1822 self.assertFilesSame('cog1.txt', 'cog1.out2') 

1823 self.newCog() 

1824 # Remove the generated output again. 

1825 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt']) 

1826 self.assertFilesSame('cog1.txt', 'cog1.out') 

1827 

1828 def testMsgCall(self): 

1829 infile = """\ 

1830 #[[[cog 

1831 cog.msg("Hello there!") 

1832 #]]] 

1833 #[[[end]]] 

1834 """ 

1835 infile = reindentBlock(infile) 

1836 self.assertEqual(self.cog.processString(infile), infile) 

1837 output = self.output.getvalue() 

1838 self.assertEqual(output, "Message: Hello there!\n") 

1839 

1840 def testErrorMessageHasNoTraceback(self): 

1841 # Test that a Cog error is printed to stderr with no traceback. 

1842 

1843 d = { 

1844 'cog1.txt': """\ 

1845 //[[[cog 

1846 cog.outl("This line was newly") 

1847 cog.outl("generated by cog") 

1848 cog.outl("blah blah.") 

1849 //]]] 

1850 Xhis line was newly 

1851 generated by cog 

1852 blah blah. 

1853 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) 

1854 """, 

1855 } 

1856 

1857 makeFiles(d) 

1858 stderr = io.StringIO() 

1859 self.cog.setOutput(stderr=stderr) 

1860 self.cog.main(['argv0', '-c', '-r', "cog1.txt"]) 

1861 self.assertEqual(self.output.getvalue(), "Cogging cog1.txt\n") 

1862 self.assertEqual(stderr.getvalue(), "cog1.txt(9): Output has been edited! Delete old checksum to unprotect.\n") 

1863 

1864 def testDashD(self): 

1865 d = { 

1866 'test.cog': """\ 

1867 --[[[cog cog.outl("Defined fooey as " + fooey) ]]] 

1868 --[[[end]]] 

1869 """, 

1870 

1871 'test.kablooey': """\ 

1872 --[[[cog cog.outl("Defined fooey as " + fooey) ]]] 

1873 Defined fooey as kablooey 

1874 --[[[end]]] 

1875 """, 

1876 

1877 'test.einstein': """\ 

1878 --[[[cog cog.outl("Defined fooey as " + fooey) ]]] 

1879 Defined fooey as e=mc2 

1880 --[[[end]]] 

1881 """, 

1882 } 

1883 

1884 makeFiles(d) 

1885 self.cog.callableMain(['argv0', '-r', '-D', 'fooey=kablooey', 'test.cog']) 

1886 self.assertFilesSame('test.cog', 'test.kablooey') 

1887 makeFiles(d) 

1888 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', 'test.cog']) 

1889 self.assertFilesSame('test.cog', 'test.kablooey') 

1890 makeFiles(d) 

1891 self.cog.callableMain(['argv0', '-r', '-Dfooey=e=mc2', 'test.cog']) 

1892 self.assertFilesSame('test.cog', 'test.einstein') 

1893 makeFiles(d) 

1894 self.cog.callableMain(['argv0', '-r', '-Dbar=quux', '-Dfooey=kablooey', 'test.cog']) 

1895 self.assertFilesSame('test.cog', 'test.kablooey') 

1896 makeFiles(d) 

1897 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', '-Dbar=quux', 'test.cog']) 

1898 self.assertFilesSame('test.cog', 'test.kablooey') 

1899 makeFiles(d) 

1900 self.cog.callableMain(['argv0', '-r', '-Dfooey=gooey', '-Dfooey=kablooey', 'test.cog']) 

1901 self.assertFilesSame('test.cog', 'test.kablooey') 

1902 

1903 def testOutputToStdout(self): 

1904 d = { 

1905 'test.cog': """\ 

1906 --[[[cog cog.outl('Hey there!') ]]] 

1907 --[[[end]]] 

1908 """ 

1909 } 

1910 

1911 makeFiles(d) 

1912 stderr = io.StringIO() 

1913 self.cog.setOutput(stderr=stderr) 

1914 self.cog.callableMain(['argv0', 'test.cog']) 

1915 output = self.output.getvalue() 

1916 outerr = stderr.getvalue() 

1917 self.assertEqual(output, "--[[[cog cog.outl('Hey there!') ]]]\nHey there!\n--[[[end]]]\n") 

1918 self.assertEqual(outerr, "") 

1919 

1920 def testReadFromStdin(self): 

1921 stdin = io.StringIO("--[[[cog cog.outl('Wow') ]]]\n--[[[end]]]\n") 

1922 def restore_stdin(old_stdin): 

1923 sys.stdin = old_stdin 

1924 self.addCleanup(restore_stdin, sys.stdin) 

1925 sys.stdin = stdin 

1926 

1927 stderr = io.StringIO() 

1928 self.cog.setOutput(stderr=stderr) 

1929 self.cog.callableMain(['argv0', '-']) 

1930 output = self.output.getvalue() 

1931 outerr = stderr.getvalue() 

1932 self.assertEqual(output, "--[[[cog cog.outl('Wow') ]]]\nWow\n--[[[end]]]\n") 

1933 self.assertEqual(outerr, "") 

1934 

1935 def testSuffixOutputLines(self): 

1936 d = { 

1937 'test.cog': """\ 

1938 Hey there. 

1939 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]] 

1940 ;[[[end]]] 

1941 Good bye. 

1942 """, 

1943 

1944 'test.out': """\ 

1945 Hey there. 

1946 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]] 

1947 a (foo) 

1948 b (foo) 

1949 """ # These three trailing spaces are important. 

1950 # The suffix is not applied to completely blank lines. 

1951 """ 

1952 c (foo) 

1953 ;[[[end]]] 

1954 Good bye. 

1955 """, 

1956 } 

1957 

1958 makeFiles(d) 

1959 self.cog.callableMain(['argv0', '-r', '-s', ' (foo)', 'test.cog']) 

1960 self.assertFilesSame('test.cog', 'test.out') 

1961 

1962 def testEmptySuffix(self): 

1963 d = { 

1964 'test.cog': """\ 

1965 ;[[[cog cog.outl('a\\nb\\nc') ]]] 

1966 ;[[[end]]] 

1967 """, 

1968 

1969 'test.out': """\ 

1970 ;[[[cog cog.outl('a\\nb\\nc') ]]] 

1971 a 

1972 b 

1973 c 

1974 ;[[[end]]] 

1975 """, 

1976 } 

1977 

1978 makeFiles(d) 

1979 self.cog.callableMain(['argv0', '-r', '-s', '', 'test.cog']) 

1980 self.assertFilesSame('test.cog', 'test.out') 

1981 

1982 def testHellishSuffix(self): 

1983 d = { 

1984 'test.cog': """\ 

1985 ;[[[cog cog.outl('a\\n\\nb') ]]] 

1986 """, 

1987 

1988 'test.out': """\ 

1989 ;[[[cog cog.outl('a\\n\\nb') ]]] 

1990 a /\\n*+([)]>< 

1991 

1992 b /\\n*+([)]>< 

1993 """, 

1994 } 

1995 

1996 makeFiles(d) 

1997 self.cog.callableMain(['argv0', '-z', '-r', '-s', r' /\n*+([)]><', 'test.cog']) 

1998 self.assertFilesSame('test.cog', 'test.out') 

1999 

2000 def testPrologue(self): 

2001 d = { 

2002 'test.cog': """\ 

2003 Some text. 

2004 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]] 

2005 //[[[end]]] 

2006 epilogue. 

2007 """, 

2008 

2009 'test.out': """\ 

2010 Some text. 

2011 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]] 

2012 1.4142135623 

2013 //[[[end]]] 

2014 epilogue. 

2015 """, 

2016 } 

2017 

2018 makeFiles(d) 

2019 self.cog.callableMain(['argv0', '-r', '-p', 'import math', 'test.cog']) 

2020 self.assertFilesSame('test.cog', 'test.out') 

2021 

2022 def testThreads(self): 

2023 # Test that the implicitly imported cog module is actually different for 

2024 # different threads. 

2025 numthreads = 20 

2026 

2027 d = {} 

2028 for i in range(numthreads): 

2029 d[f'f{i}.cog'] = ( 

2030 "x\n" * i + 

2031 "[[[cog\n" + 

2032 f"assert cog.firstLineNum == int(FIRST) == {i+1}\n" + 

2033 "]]]\n" + 

2034 "[[[end]]]\n" 

2035 ) 

2036 makeFiles(d) 

2037 

2038 results = [] 

2039 

2040 def thread_main(num): 

2041 try: 

2042 ret = Cog().main( 

2043 ['cog.py', '-r', '-D', f'FIRST={num+1}', f'f{num}.cog'] 

2044 ) 

2045 assert ret == 0 

2046 except Exception as exc: # pragma: no cover (only happens on test failure) 

2047 results.append(exc) 

2048 else: 

2049 results.append(None) 

2050 

2051 ts = [threading.Thread(target=thread_main, args=(i,)) for i in range(numthreads)] 

2052 for t in ts: 

2053 t.start() 

2054 for t in ts: 

2055 t.join() 

2056 assert results == [None] * numthreads 

2057 

2058 

2059class CheckTests(TestCaseWithTempDir): 

2060 def run_check(self, args, status=0): 

2061 actual_status = self.cog.main(['argv0', '--check'] + args) 

2062 print(self.output.getvalue()) 

2063 self.assertEqual(status, actual_status) 

2064 

2065 def assert_made_files_unchanged(self, d): 

2066 for name, content in d.items(): 

2067 content = reindentBlock(content) 

2068 if os.name == 'nt': 

2069 content = content.replace("\n", "\r\n") 

2070 self.assertFileContent(name, content) 

2071 

2072 def test_check_no_cog(self): 

2073 d = { 

2074 'hello.txt': """\ 

2075 Hello. 

2076 """, 

2077 } 

2078 makeFiles(d) 

2079 self.run_check(['hello.txt'], status=0) 

2080 self.assertEqual(self.output.getvalue(), "Checking hello.txt\n") 

2081 self.assert_made_files_unchanged(d) 

2082 

2083 def test_check_good(self): 

2084 d = { 

2085 'unchanged.cog': """\ 

2086 //[[[cog 

2087 cog.outl("hello world") 

2088 //]]] 

2089 hello world 

2090 //[[[end]]] 

2091 """, 

2092 } 

2093 makeFiles(d) 

2094 self.run_check(['unchanged.cog'], status=0) 

2095 self.assertEqual(self.output.getvalue(), "Checking unchanged.cog\n") 

2096 self.assert_made_files_unchanged(d) 

2097 

2098 def test_check_bad(self): 

2099 d = { 

2100 'changed.cog': """\ 

2101 //[[[cog 

2102 cog.outl("goodbye world") 

2103 //]]] 

2104 hello world 

2105 //[[[end]]] 

2106 """, 

2107 } 

2108 makeFiles(d) 

2109 self.run_check(['changed.cog'], status=5) 

2110 self.assertEqual(self.output.getvalue(), "Checking changed.cog (changed)\nCheck failed\n") 

2111 self.assert_made_files_unchanged(d) 

2112 

2113 def test_check_mixed(self): 

2114 d = { 

2115 'unchanged.cog': """\ 

2116 //[[[cog 

2117 cog.outl("hello world") 

2118 //]]] 

2119 hello world 

2120 //[[[end]]] 

2121 """, 

2122 'changed.cog': """\ 

2123 //[[[cog 

2124 cog.outl("goodbye world") 

2125 //]]] 

2126 hello world 

2127 //[[[end]]] 

2128 """, 

2129 } 

2130 makeFiles(d) 

2131 for verbosity, output in [ 

2132 ("0", "Check failed\n"), 

2133 ("1", "Checking changed.cog (changed)\nCheck failed\n"), 

2134 ("2", "Checking unchanged.cog\nChecking changed.cog (changed)\nCheck failed\n"), 

2135 ]: 

2136 self.newCog() 

2137 self.run_check(['--verbosity=%s' % verbosity, 'unchanged.cog', 'changed.cog'], status=5) 

2138 self.assertEqual(self.output.getvalue(), output) 

2139 self.assert_made_files_unchanged(d) 

2140 

2141 def test_check_with_good_checksum(self): 

2142 d = { 

2143 'good.txt': """\ 

2144 //[[[cog 

2145 cog.outl("This line was newly") 

2146 cog.outl("generated by cog") 

2147 cog.outl("blah blah.") 

2148 //]]] 

2149 This line was newly 

2150 generated by cog 

2151 blah blah. 

2152 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) 

2153 """, 

2154 } 

2155 makeFiles(d) 

2156 # Have to use -c with --check if there are checksums in the file. 

2157 self.run_check(['-c', 'good.txt'], status=0) 

2158 self.assertEqual(self.output.getvalue(), "Checking good.txt\n") 

2159 self.assert_made_files_unchanged(d) 

2160 

2161 def test_check_with_bad_checksum(self): 

2162 d = { 

2163 'bad.txt': """\ 

2164 //[[[cog 

2165 cog.outl("This line was newly") 

2166 cog.outl("generated by cog") 

2167 cog.outl("blah blah.") 

2168 //]]] 

2169 This line was newly 

2170 generated by cog 

2171 blah blah. 

2172 //[[[end]]] (checksum: a9999999e5ad6b95c9e9a184b26f4346) 

2173 """, 

2174 } 

2175 makeFiles(d) 

2176 # Have to use -c with --check if there are checksums in the file. 

2177 self.run_check(['-c', 'bad.txt'], status=1) 

2178 self.assertEqual(self.output.getvalue(), "Checking bad.txt\nbad.txt(9): Output has been edited! Delete old checksum to unprotect.\n") 

2179 self.assert_made_files_unchanged(d) 

2180 

2181 

2182class WritabilityTests(TestCaseWithTempDir): 

2183 

2184 d = { 

2185 'test.cog': """\ 

2186 //[[[cog 

2187 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']: 

2188 cog.outl("void %s();" % fn) 

2189 //]]] 

2190 //[[[end]]] 

2191 """, 

2192 

2193 'test.out': """\ 

2194 //[[[cog 

2195 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']: 

2196 cog.outl("void %s();" % fn) 

2197 //]]] 

2198 void DoSomething(); 

2199 void DoAnotherThing(); 

2200 void DoLastThing(); 

2201 //[[[end]]] 

2202 """, 

2203 } 

2204 

2205 if os.name == 'nt': 2205 ↛ 2207line 2205 didn't jump to line 2207, because the condition on line 2205 was never true

2206 # for Windows 

2207 cmd_w_args = 'attrib -R %s' 

2208 cmd_w_asterisk = 'attrib -R *' 

2209 else: 

2210 # for unix-like 

2211 cmd_w_args = 'chmod +w %s' 

2212 cmd_w_asterisk = 'chmod +w *' 

2213 

2214 def setUp(self): 

2215 super().setUp() 

2216 makeFiles(self.d) 

2217 self.testcog = os.path.join(self.tempdir, 'test.cog') 

2218 os.chmod(self.testcog, stat.S_IREAD) # Make the file readonly. 

2219 assert not os.access(self.testcog, os.W_OK) 

2220 

2221 def tearDown(self): 

2222 os.chmod(self.testcog, stat.S_IWRITE) # Make the file writable again. 

2223 super().tearDown() 

2224 

2225 def testReadonlyNoCommand(self): 

2226 with self.assertRaisesRegex(CogError, "^Can't overwrite test.cog$"): 

2227 self.cog.callableMain(['argv0', '-r', 'test.cog']) 

2228 assert not os.access(self.testcog, os.W_OK) 

2229 

2230 def testReadonlyWithCommand(self): 

2231 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_args, 'test.cog']) 

2232 self.assertFilesSame('test.cog', 'test.out') 

2233 assert os.access(self.testcog, os.W_OK) 

2234 

2235 def testReadonlyWithCommandWithNoSlot(self): 

2236 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_asterisk, 'test.cog']) 

2237 self.assertFilesSame('test.cog', 'test.out') 

2238 assert os.access(self.testcog, os.W_OK) 

2239 

2240 def testReadonlyWithIneffectualCommand(self): 

2241 with self.assertRaisesRegex(CogError, "^Couldn't make test.cog writable$"): 

2242 self.cog.callableMain(['argv0', '-r', '-w', 'echo %s', 'test.cog']) 

2243 assert not os.access(self.testcog, os.W_OK) 

2244 

2245 

2246class ChecksumTests(TestCaseWithTempDir): 

2247 

2248 def testCreateChecksumOutput(self): 

2249 d = { 

2250 'cog1.txt': """\ 

2251 //[[[cog 

2252 cog.outl("This line was generated.") 

2253 //]]] 

2254 This line was generated. 

2255 //[[[end]]] 

2256 This line was not. 

2257 """, 

2258 

2259 'cog1.out': """\ 

2260 //[[[cog 

2261 cog.outl("This line was generated.") 

2262 //]]] 

2263 This line was generated. 

2264 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) 

2265 This line was not. 

2266 """, 

2267 } 

2268 

2269 makeFiles(d) 

2270 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt']) 

2271 self.assertFilesSame('cog1.txt', 'cog1.out') 

2272 

2273 def testCheckChecksumOutput(self): 

2274 d = { 

2275 'cog1.txt': """\ 

2276 //[[[cog 

2277 cog.outl("This line was newly") 

2278 cog.outl("generated by cog") 

2279 cog.outl("blah blah.") 

2280 //]]] 

2281 This line was generated. 

2282 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) 

2283 """, 

2284 

2285 'cog1.out': """\ 

2286 //[[[cog 

2287 cog.outl("This line was newly") 

2288 cog.outl("generated by cog") 

2289 cog.outl("blah blah.") 

2290 //]]] 

2291 This line was newly 

2292 generated by cog 

2293 blah blah. 

2294 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) 

2295 """, 

2296 } 

2297 

2298 makeFiles(d) 

2299 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt']) 

2300 self.assertFilesSame('cog1.txt', 'cog1.out') 

2301 

2302 def testRemoveChecksumOutput(self): 

2303 d = { 

2304 'cog1.txt': """\ 

2305 //[[[cog 

2306 cog.outl("This line was newly") 

2307 cog.outl("generated by cog") 

2308 cog.outl("blah blah.") 

2309 //]]] 

2310 This line was generated. 

2311 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) fooey 

2312 """, 

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]]] fooey 

2324 """, 

2325 } 

2326 

2327 makeFiles(d) 

2328 self.cog.callableMain(['argv0', '-r', 'cog1.txt']) 

2329 self.assertFilesSame('cog1.txt', 'cog1.out') 

2330 

2331 def testTamperedChecksumOutput(self): 

2332 d = { 

2333 'cog1.txt': """\ 

2334 //[[[cog 

2335 cog.outl("This line was newly") 

2336 cog.outl("generated by cog") 

2337 cog.outl("blah blah.") 

2338 //]]] 

2339 Xhis line was newly 

2340 generated by cog 

2341 blah blah. 

2342 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) 

2343 """, 

2344 

2345 'cog2.txt': """\ 

2346 //[[[cog 

2347 cog.outl("This line was newly") 

2348 cog.outl("generated by cog") 

2349 cog.outl("blah blah.") 

2350 //]]] 

2351 This line was newly 

2352 generated by cog 

2353 blah blah! 

2354 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) 

2355 """, 

2356 

2357 'cog3.txt': """\ 

2358 //[[[cog 

2359 cog.outl("This line was newly") 

2360 cog.outl("generated by cog") 

2361 cog.outl("blah blah.") 

2362 //]]] 

2363 

2364 This line was newly 

2365 generated by cog 

2366 blah blah. 

2367 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) 

2368 """, 

2369 

2370 'cog4.txt': """\ 

2371 //[[[cog 

2372 cog.outl("This line was newly") 

2373 cog.outl("generated by cog") 

2374 cog.outl("blah blah.") 

2375 //]]] 

2376 This line was newly 

2377 generated by cog 

2378 blah blah.. 

2379 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) 

2380 """, 

2381 

2382 'cog5.txt': """\ 

2383 //[[[cog 

2384 cog.outl("This line was newly") 

2385 cog.outl("generated by cog") 

2386 cog.outl("blah blah.") 

2387 //]]] 

2388 This line was newly 

2389 generated by cog 

2390 blah blah. 

2391 extra 

2392 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) 

2393 """, 

2394 

2395 'cog6.txt': """\ 

2396 //[[[cog 

2397 cog.outl("This line was newly") 

2398 cog.outl("generated by cog") 

2399 cog.outl("blah blah.") 

2400 //]]] 

2401 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) 

2402 """, 

2403 } 

2404 

2405 makeFiles(d) 

2406 with self.assertRaisesRegex(CogError, 

2407 r"^cog1.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"): 

2408 self.cog.callableMain(['argv0', '-c', "cog1.txt"]) 

2409 with self.assertRaisesRegex(CogError, 

2410 r"^cog2.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"): 

2411 self.cog.callableMain(['argv0', '-c', "cog2.txt"]) 

2412 with self.assertRaisesRegex(CogError, 

2413 r"^cog3.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"): 

2414 self.cog.callableMain(['argv0', '-c', "cog3.txt"]) 

2415 with self.assertRaisesRegex(CogError, 

2416 r"^cog4.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"): 

2417 self.cog.callableMain(['argv0', '-c', "cog4.txt"]) 

2418 with self.assertRaisesRegex(CogError, 

2419 r"^cog5.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"): 

2420 self.cog.callableMain(['argv0', '-c', "cog5.txt"]) 

2421 with self.assertRaisesRegex(CogError, 

2422 r"^cog6.txt\(6\): Output has been edited! Delete old checksum to unprotect.$"): 

2423 self.cog.callableMain(['argv0', '-c', "cog6.txt"]) 

2424 

2425 def testArgvIsntModified(self): 

2426 argv = ['argv0', '-v'] 

2427 orig_argv = argv[:] 

2428 self.cog.callableMain(argv) 

2429 self.assertEqual(argv, orig_argv) 

2430 

2431 

2432class CustomMarkerTests(TestCaseWithTempDir): 

2433 

2434 def testCustomerMarkers(self): 

2435 d = { 

2436 'test.cog': """\ 

2437 //{{ 

2438 cog.outl("void %s();" % "MyFunction") 

2439 //}} 

2440 //{{end}} 

2441 """, 

2442 

2443 'test.out': """\ 

2444 //{{ 

2445 cog.outl("void %s();" % "MyFunction") 

2446 //}} 

2447 void MyFunction(); 

2448 //{{end}} 

2449 """, 

2450 } 

2451 

2452 makeFiles(d) 

2453 self.cog.callableMain([ 

2454 'argv0', '-r', 

2455 '--markers={{ }} {{end}}', 

2456 'test.cog' 

2457 ]) 

2458 self.assertFilesSame('test.cog', 'test.out') 

2459 

2460 def testTrulyWackyMarkers(self): 

2461 # Make sure the markers are properly re-escaped. 

2462 d = { 

2463 'test.cog': """\ 

2464 //**( 

2465 cog.outl("void %s();" % "MyFunction") 

2466 //**) 

2467 //**(end)** 

2468 """, 

2469 

2470 'test.out': """\ 

2471 //**( 

2472 cog.outl("void %s();" % "MyFunction") 

2473 //**) 

2474 void MyFunction(); 

2475 //**(end)** 

2476 """, 

2477 } 

2478 

2479 makeFiles(d) 

2480 self.cog.callableMain([ 

2481 'argv0', '-r', 

2482 '--markers=**( **) **(end)**', 

2483 'test.cog' 

2484 ]) 

2485 self.assertFilesSame('test.cog', 'test.out') 

2486 

2487 def testChangeJustOneMarker(self): 

2488 d = { 

2489 'test.cog': """\ 

2490 //**( 

2491 cog.outl("void %s();" % "MyFunction") 

2492 //]]] 

2493 //[[[end]]] 

2494 """, 

2495 

2496 'test.out': """\ 

2497 //**( 

2498 cog.outl("void %s();" % "MyFunction") 

2499 //]]] 

2500 void MyFunction(); 

2501 //[[[end]]] 

2502 """, 

2503 } 

2504 

2505 makeFiles(d) 

2506 self.cog.callableMain([ 

2507 'argv0', '-r', 

2508 '--markers=**( ]]] [[[end]]]', 

2509 'test.cog' 

2510 ]) 

2511 self.assertFilesSame('test.cog', 'test.out') 

2512 

2513 

2514class BlakeTests(TestCaseWithTempDir): 

2515 

2516 # Blake Winton's contributions. 

2517 def testDeleteCode(self): 

2518 # -o sets the output file. 

2519 d = { 

2520 'test.cog': """\ 

2521 // This is my C++ file. 

2522 //[[[cog 

2523 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] 

2524 for fn in fnames: 

2525 cog.outl("void %s();" % fn) 

2526 //]]] 

2527 Some Sample Code Here 

2528 //[[[end]]]Data Data 

2529 And Some More 

2530 """, 

2531 

2532 'test.out': """\ 

2533 // This is my C++ file. 

2534 void DoSomething(); 

2535 void DoAnotherThing(); 

2536 void DoLastThing(); 

2537 And Some More 

2538 """, 

2539 } 

2540 

2541 makeFiles(d) 

2542 self.cog.callableMain(['argv0', '-d', '-o', 'test.cogged', 'test.cog']) 

2543 self.assertFilesSame('test.cogged', 'test.out') 

2544 

2545 def testDeleteCodeWithDashRFails(self): 

2546 d = { 

2547 'test.cog': """\ 

2548 // This is my C++ file. 

2549 """ 

2550 } 

2551 

2552 makeFiles(d) 

2553 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"): 

2554 self.cog.callableMain(['argv0', '-r', '-d', 'test.cog']) 

2555 

2556 def testSettingGlobals(self): 

2557 # Blake Winton contributed a way to set the globals that will be used in 

2558 # processFile(). 

2559 d = { 

2560 'test.cog': """\ 

2561 // This is my C++ file. 

2562 //[[[cog 

2563 for fn in fnames: 

2564 cog.outl("void %s();" % fn) 

2565 //]]] 

2566 Some Sample Code Here 

2567 //[[[end]]]""", 

2568 

2569 'test.out': """\ 

2570 // This is my C++ file. 

2571 void DoBlake(); 

2572 void DoWinton(); 

2573 void DoContribution(); 

2574 """, 

2575 } 

2576 

2577 makeFiles(d) 

2578 globals = {} 

2579 globals['fnames'] = ['DoBlake', 'DoWinton', 'DoContribution'] 

2580 self.cog.options.bDeleteCode = True 

2581 self.cog.processFile('test.cog', 'test.cogged', globals=globals) 

2582 self.assertFilesSame('test.cogged', 'test.out') 

2583 

2584 

2585class ErrorCallTests(TestCaseWithTempDir): 

2586 

2587 def testErrorCallHasNoTraceback(self): 

2588 # Test that cog.error() doesn't show a traceback. 

2589 d = { 

2590 'error.cog': """\ 

2591 //[[[cog 

2592 cog.error("Something Bad!") 

2593 //]]] 

2594 //[[[end]]] 

2595 """, 

2596 } 

2597 

2598 makeFiles(d) 

2599 self.cog.main(['argv0', '-r', 'error.cog']) 

2600 output = self.output.getvalue() 

2601 self.assertEqual(output, "Cogging error.cog\nError: Something Bad!\n") 

2602 

2603 def testRealErrorHasTraceback(self): 

2604 # Test that a genuine error does show a traceback. 

2605 d = { 

2606 'error.cog': """\ 

2607 //[[[cog 

2608 raise RuntimeError("Hey!") 

2609 //]]] 

2610 //[[[end]]] 

2611 """, 

2612 } 

2613 

2614 makeFiles(d) 

2615 self.cog.main(['argv0', '-r', 'error.cog']) 

2616 output = self.output.getvalue() 

2617 msg = 'Actual output:\n' + output 

2618 self.assertTrue(output.startswith("Cogging error.cog\nTraceback (most recent"), msg) 

2619 self.assertIn("RuntimeError: Hey!", output) 

2620 

2621 

2622# Things not yet tested: 

2623# - A bad -w command (currently fails silently).