Coverage for cogapp/test_cogapp.py: 29.30%

849 statements  

« prev     ^ index     » next       coverage.py v7.1.0, created at 2023-01-24 19:37 -0500

1""" Test cogapp. 

2 http://nedbatchelder.com/code/cog 

3 

4 Copyright 2004-2021, Ned Batchelder. 

5""" 

6 

7from __future__ import absolute_import 

8 

9import os 

10import os.path 

11import random 

12import re 

13import shutil 

14import stat 

15import sys 

16import tempfile 

17import threading 

18 

19from .backward import StringIO, to_bytes, TestCase, PY3 

20from .cogapp import Cog, CogOptions, CogGenerator 

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

22from .cogapp import usage, __version__, main 

23from .makefiles import * 

24from .whiteutils import reindentBlock 

25 

26 

27class CogTestsInMemory(TestCase): 

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

29 """ 

30 

31 def testNoCog(self): 

32 strings = [ 

33 '', 

34 ' ', 

35 ' \t \t \tx', 

36 'hello', 

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

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

39 ] 

40 for s in strings: 

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

42 

43 def testSimple(self): 

44 infile = """\ 

45 Some text. 

46 //[[[cog 

47 import cog 

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

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

50 //]]] 

51 gobbledegook. 

52 //[[[end]]] 

53 epilogue. 

54 """ 

55 

56 outfile = """\ 

57 Some text. 

58 //[[[cog 

59 import cog 

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

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

62 //]]] 

63 This is line one 

64 

65 This is line two 

66 //[[[end]]] 

67 epilogue. 

68 """ 

69 

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

71 

72 def testEmptyCog(self): 

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

74 # but it works. 

75 infile = """\ 

76 hello 

77 //[[[cog 

78 //]]] 

79 //[[[end]]] 

80 goodbye 

81 """ 

82 

83 infile = reindentBlock(infile) 

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

85 

86 def testMultipleCogs(self): 

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

88 infile = """\ 

89 //[[[cog 

90 cog.out("chunk1") 

91 //]]] 

92 chunk1 

93 //[[[end]]] 

94 //[[[cog 

95 cog.out("chunk2") 

96 //]]] 

97 chunk2 

98 //[[[end]]] 

99 between chunks 

100 //[[[cog 

101 cog.out("chunk3") 

102 //]]] 

103 chunk3 

104 //[[[end]]] 

105 """ 

106 

107 infile = reindentBlock(infile) 

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

109 

110 def testTrimBlankLines(self): 

111 infile = """\ 

112 //[[[cog 

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

114 cog.out(''' 

115 This is line two 

116 ''', dedent=True, trimblanklines=True) 

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

118 //]]] 

119 This is line one 

120 This is line two 

121 This is line three 

122 //[[[end]]] 

123 """ 

124 

125 infile = reindentBlock(infile) 

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

127 

128 def testTrimEmptyBlankLines(self): 

129 infile = """\ 

130 //[[[cog 

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

132 cog.out(''' 

133 This is line two 

134 ''', dedent=True, trimblanklines=True) 

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

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

137 //]]] 

138 This is line one 

139 This is line two 

140 This is line three 

141 //[[[end]]] 

142 """ 

143 

144 infile = reindentBlock(infile) 

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

146 

147 def testTrimBlankLinesWithLastPartial(self): 

148 infile = """\ 

149 //[[[cog 

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

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

152 //]]] 

153 This is line one 

154 Line two 

155 Line three 

156 //[[[end]]] 

157 """ 

158 

159 infile = reindentBlock(infile) 

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

161 

162 def testCogOutDedent(self): 

163 infile = """\ 

164 //[[[cog 

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

166 cog.out(''' 

167 This is dedent=True 1 

168 This is dedent=True 2 

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

170 cog.out(''' 

171 This is dedent=False 1 

172 This is dedent=False 2 

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

174 cog.out(''' 

175 This is dedent=default 1 

176 This is dedent=default 2 

177 ''', trimblanklines=True) 

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

179 //]]] 

180 This is the first line 

181 This is dedent=True 1 

182 This is dedent=True 2 

183 This is dedent=False 1 

184 This is dedent=False 2 

185 This is dedent=default 1 

186 This is dedent=default 2 

187 This is the last line 

188 //[[[end]]] 

189 """ 

190 

191 infile = reindentBlock(infile) 

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

193 

194 def test22EndOfLine(self): 

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

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

197 infile = """\ 

198 //[[[cog 

199 import cog 

200 for i in range(3): 

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

202 //]]] 

203 0 

204 1 

205 2 

206 //[[[end]]] 

207 """ 

208 

209 infile = reindentBlock(infile) 

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

211 

212 def testIndentedCode(self): 

213 infile = """\ 

214 first line 

215 [[[cog 

216 import cog 

217 for i in range(3): 

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

219 ]]] 

220 xx0 

221 xx1 

222 xx2 

223 [[[end]]] 

224 last line 

225 """ 

226 

227 infile = reindentBlock(infile) 

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

229 

230 def testPrefixedCode(self): 

231 infile = """\ 

232 --[[[cog 

233 --import cog 

234 --for i in range(3): 

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

236 --]]] 

237 xx0 

238 xx1 

239 xx2 

240 --[[[end]]] 

241 """ 

242 

243 infile = reindentBlock(infile) 

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

245 

246 def testPrefixedIndentedCode(self): 

247 infile = """\ 

248 prologue 

249 --[[[cog 

250 -- import cog 

251 -- for i in range(3): 

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

253 --]]] 

254 xy0 

255 xy1 

256 xy2 

257 --[[[end]]] 

258 """ 

259 

260 infile = reindentBlock(infile) 

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

262 

263 def testBogusPrefixMatch(self): 

264 infile = """\ 

265 prologue 

266 #[[[cog 

267 import cog 

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

269 for i in range(3): 

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

271 #]]] 

272 xy0 

273 xy1 

274 xy2 

275 #[[[end]]] 

276 """ 

277 

278 infile = reindentBlock(infile) 

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

280 

281 def testNoFinalNewline(self): 

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

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

284 infile = """\ 

285 prologue 

286 [[[cog 

287 import cog 

288 for i in range(3): 

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

290 ]]] 

291 012 

292 [[[end]]] 

293 epilogue 

294 """ 

295 

296 infile = reindentBlock(infile) 

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

298 

299 def testNoOutputAtAll(self): 

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

301 infile = """\ 

302 prologue 

303 [[[cog 

304 i = 1 

305 ]]] 

306 [[[end]]] 

307 epilogue 

308 """ 

309 

310 infile = reindentBlock(infile) 

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

312 

313 def testPurelyBlankLine(self): 

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

315 # prefix, that should be OK. 

316 

317 infile = """\ 

318 prologue 

319 [[[cog 

320 import sys 

321 cog.out("Hello") 

322 $ 

323 cog.out("There") 

324 ]]] 

325 HelloThere 

326 [[[end]]] 

327 epilogue 

328 """ 

329 

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

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

332 

333 def testEmptyOutl(self): 

334 # Alexander Belchenko suggested the string argument to outl should 

335 # be optional. Does it work? 

336 

337 infile = """\ 

338 prologue 

339 [[[cog 

340 cog.outl("x") 

341 cog.outl() 

342 cog.outl("y") 

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

344 cog.outl(trimblanklines=True) 

345 cog.outl("z") 

346 ]]] 

347 x 

348 

349 y 

350 

351 z 

352 [[[end]]] 

353 epilogue 

354 """ 

355 

356 infile = reindentBlock(infile) 

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

358 

359 def testFirstLineNum(self): 

360 infile = """\ 

361 fooey 

362 [[[cog 

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

364 ]]] 

365 started at line number 2 

366 [[[end]]] 

367 blah blah 

368 [[[cog 

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

370 ]]] 

371 and again at line 8 

372 [[[end]]] 

373 """ 

374 

375 infile = reindentBlock(infile) 

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

377 

378 def testCompactOneLineCode(self): 

379 infile = """\ 

380 first line 

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

382 get rid of this! 

383 [[[end]]] 

384 last line 

385 """ 

386 

387 outfile = """\ 

388 first line 

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

390 hello 81 

391 [[[end]]] 

392 last line 

393 """ 

394 

395 infile = reindentBlock(infile) 

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

397 

398 def testInsideOutCompact(self): 

399 infile = """\ 

400 first line 

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

402 get rid of this! 

403 [[[end]]] 

404 last line 

405 """ 

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

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

408 

409 def testSharingGlobals(self): 

410 infile = """\ 

411 first line 

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

413 [[[end]]] 

414 more literal junk. 

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

416 [[[end]]] 

417 last line 

418 """ 

419 

420 outfile = """\ 

421 first line 

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

423 [[[end]]] 

424 more literal junk. 

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

426 hey there 

427 [[[end]]] 

428 last line 

429 """ 

430 

431 infile = reindentBlock(infile) 

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

433 

434 def testAssertInCogCode(self): 

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

436 infile = """\ 

437 [[[cog 

438 assert 1 == 2, "Oops" 

439 ]]] 

440 [[[end]]] 

441 """ 

442 infile = reindentBlock(infile) 

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

444 Cog().processString(infile) 

445 

446 def testCogPrevious(self): 

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

448 infile = """\ 

449 [[[cog 

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

451 cog.out(cog.previous) 

452 cog.outl("Ran again!") 

453 ]]] 

454 Hello there! 

455 [[[end]]] 

456 """ 

457 

458 outfile = """\ 

459 [[[cog 

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

461 cog.out(cog.previous) 

462 cog.outl("Ran again!") 

463 ]]] 

464 Hello there! 

465 Ran again! 

466 [[[end]]] 

467 """ 

468 

469 infile = reindentBlock(infile) 

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

471 

472 

473class CogOptionsTests(TestCase): 

474 """ Test the CogOptions class. 

475 """ 

476 

477 def testEquality(self): 

478 o = CogOptions() 

479 p = CogOptions() 

480 self.assertEqual(o, p) 

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

482 self.assertNotEqual(o, p) 

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

484 self.assertEqual(o, p) 

485 

486 def testCloning(self): 

487 o = CogOptions() 

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

489 p = o.clone() 

490 self.assertEqual(o, p) 

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

492 self.assertNotEqual(o, p) 

493 q = CogOptions() 

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

495 self.assertEqual(p, q) 

496 

497 def testCombiningFlags(self): 

498 # Single-character flags can be combined. 

499 o = CogOptions() 

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

501 p = CogOptions() 

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

503 self.assertEqual(o, p) 

504 

505 def testMarkers(self): 

506 o = CogOptions() 

507 o._parse_markers('a b c') 

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

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

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

511 

512 def testMarkersSwitch(self): 

513 o = CogOptions() 

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

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

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

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

518 

519 

520class FileStructureTests(TestCase): 

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

522 of files. 

523 """ 

524 

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

526 infile = reindentBlock(infile) 

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

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

529 

530 def testBeginNoEnd(self): 

531 infile = """\ 

532 Fooey 

533 #[[[cog 

534 cog.outl('hello') 

535 """ 

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

537 

538 def testNoEoo(self): 

539 infile = """\ 

540 Fooey 

541 #[[[cog 

542 cog.outl('hello') 

543 #]]] 

544 """ 

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

546 

547 infile2 = """\ 

548 Fooey 

549 #[[[cog 

550 cog.outl('hello') 

551 #]]] 

552 #[[[cog 

553 cog.outl('goodbye') 

554 #]]] 

555 """ 

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

557 

558 def testStartWithEnd(self): 

559 infile = """\ 

560 #]]] 

561 """ 

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

563 

564 infile2 = """\ 

565 #[[[cog 

566 cog.outl('hello') 

567 #]]] 

568 #[[[end]]] 

569 #]]] 

570 """ 

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

572 

573 def testStartWithEoo(self): 

574 infile = """\ 

575 #[[[end]]] 

576 """ 

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

578 

579 infile2 = """\ 

580 #[[[cog 

581 cog.outl('hello') 

582 #]]] 

583 #[[[end]]] 

584 #[[[end]]] 

585 """ 

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

587 

588 def testNoEnd(self): 

589 infile = """\ 

590 #[[[cog 

591 cog.outl("hello") 

592 #[[[end]]] 

593 """ 

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

595 

596 infile2 = """\ 

597 #[[[cog 

598 cog.outl('hello') 

599 #]]] 

600 #[[[end]]] 

601 #[[[cog 

602 cog.outl("hello") 

603 #[[[end]]] 

604 """ 

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

606 

607 def testTwoBegins(self): 

608 infile = """\ 

609 #[[[cog 

610 #[[[cog 

611 cog.outl("hello") 

612 #]]] 

613 #[[[end]]] 

614 """ 

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

616 

617 infile2 = """\ 

618 #[[[cog 

619 cog.outl("hello") 

620 #]]] 

621 #[[[end]]] 

622 #[[[cog 

623 #[[[cog 

624 cog.outl("hello") 

625 #]]] 

626 #[[[end]]] 

627 """ 

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

629 

630 def testTwoEnds(self): 

631 infile = """\ 

632 #[[[cog 

633 cog.outl("hello") 

634 #]]] 

635 #]]] 

636 #[[[end]]] 

637 """ 

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

639 

640 infile2 = """\ 

641 #[[[cog 

642 cog.outl("hello") 

643 #]]] 

644 #[[[end]]] 

645 #[[[cog 

646 cog.outl("hello") 

647 #]]] 

648 #]]] 

649 #[[[end]]] 

650 """ 

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

652 

653 

654class CogErrorTests(TestCase): 

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

656 """ 

657 

658 def testErrorMsg(self): 

659 infile = """\ 

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

661 [[[end]]] 

662 """ 

663 

664 infile = reindentBlock(infile) 

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

666 Cog().processString(infile) 

667 

668 def testErrorNoMsg(self): 

669 infile = """\ 

670 [[[cog cog.error()]]] 

671 [[[end]]] 

672 """ 

673 

674 infile = reindentBlock(infile) 

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

676 Cog().processString(infile) 

677 

678 def testNoErrorIfErrorNotCalled(self): 

679 infile = """\ 

680 --[[[cog 

681 --import cog 

682 --for i in range(3): 

683 -- if i > 10: 

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

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

686 --]]] 

687 xx0 

688 xx1 

689 xx2 

690 --[[[end]]] 

691 """ 

692 

693 infile = reindentBlock(infile) 

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

695 

696 

697class CogGeneratorGetCodeTests(TestCase): 

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

699 properly. 

700 """ 

701 

702 def setUp(self): 

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

704 the functions we're going to use. 

705 """ 

706 self.gen = CogGenerator() 

707 self.m = self.gen.parseMarker 

708 self.l = self.gen.parseLine 

709 

710 def testEmpty(self): 

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

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

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

714 

715 def testSimple(self): 

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

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

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

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

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

721 

722 def testCompressed1(self): 

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

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

725 self.l('// hello') 

726 self.l('// bye') 

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

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

729 

730 def testCompressed2(self): 

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

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

733 self.l('hello') 

734 self.l('bye') 

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

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

737 

738 def testCompressed3(self): 

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

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

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

742 self.l('bye') 

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

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

745 

746 def testCompressed4(self): 

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

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

749 self.l('hello') 

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

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

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

753 

754 def testNoCommonPrefixForMarkers(self): 

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

756 # C++ compiler. 

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

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

759 self.l('') 

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

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

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

763 

764 

765class TestCaseWithTempDir(TestCase): 

766 

767 def newCog(self): 

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

769 """ 

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

771 self.cog = Cog() 

772 self.output = StringIO() 

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

774 

775 def setUp(self): 

776 # Create a temporary directory. 

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

778 os.mkdir(self.tempdir) 

779 self.olddir = os.getcwd() 

780 os.chdir(self.tempdir) 

781 self.newCog() 

782 

783 def tearDown(self): 

784 os.chdir(self.olddir) 

785 # Get rid of the temporary directory. 

786 shutil.rmtree(self.tempdir) 

787 

788 def assertFilesSame(self, sFName1, sFName2): 

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

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

791 self.assertEqual(text1, text2) 

792 

793 def assertFileContent(self, sFName, sContent): 

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

795 f = open(sAbsName, 'rb') 

796 try: 

797 sFileContent = f.read() 

798 finally: 

799 f.close() 

800 self.assertEqual(sFileContent, to_bytes(sContent)) 

801 

802 

803class ArgumentHandlingTests(TestCaseWithTempDir): 

804 

805 def testArgumentFailure(self): 

806 # Return value 2 means usage problem. 

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

808 output = self.output.getvalue() 

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

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

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

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

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

814 

815 def testNoDashOAndAtFile(self): 

816 d = { 

817 'cogfiles.txt': """\ 

818 # Please run cog 

819 """ 

820 } 

821 

822 makeFiles(d) 

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

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

825 

826 def testDashV(self): 

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

828 output = self.output.getvalue() 

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

830 

831 def producesHelp(self, args): 

832 self.newCog() 

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

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

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

836 

837 def testDashH(self): 

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

839 self.producesHelp("-h") 

840 self.producesHelp("-?") 

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

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

843 

844 def testDashOAndDashR(self): 

845 d = { 

846 'cogfile.txt': """\ 

847 # Please run cog 

848 """ 

849 } 

850 

851 makeFiles(d) 

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

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

854 

855 def testDashZ(self): 

856 d = { 

857 'test.cog': """\ 

858 // This is my C++ file. 

859 //[[[cog 

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

861 for fn in fnames: 

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

863 //]]] 

864 """, 

865 

866 'test.out': """\ 

867 // This is my C++ file. 

868 //[[[cog 

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

870 for fn in fnames: 

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

872 //]]] 

873 void DoSomething(); 

874 void DoAnotherThing(); 

875 void DoLastThing(); 

876 """, 

877 } 

878 

879 makeFiles(d) 

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

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

882 self.newCog() 

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

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

885 

886 def testBadDashD(self): 

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

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

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

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

891 

892 def testBadMarkers(self): 

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

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

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

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

897 

898 

899class TestMain(TestCaseWithTempDir): 

900 def setUp(self): 

901 super(TestMain, self).setUp() 

902 self.old_argv = sys.argv[:] 

903 self.old_stderr = sys.stderr 

904 sys.stderr = StringIO() 

905 

906 def tearDown(self): 

907 sys.stderr = self.old_stderr 

908 sys.argv = self.old_argv 

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

910 super(TestMain, self).tearDown() 

911 

912 def test_main_function(self): 

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

914 ret = main() 

915 self.assertEqual(ret, 2) 

916 stderr = sys.stderr.getvalue() 

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

918 

919 files = { 

920 'test.cog': """\ 

921 //[[[cog 

922 def func(): 

923 import mycode 

924 mycode.boom() 

925 //]]] 

926 //[[[end]]] 

927 ----- 

928 //[[[cog 

929 func() 

930 //]]] 

931 //[[[end]]] 

932 """, 

933 

934 'mycode.py': """\ 

935 def boom(): 

936 [][0] 

937 """, 

938 } 

939 

940 def test_error_report(self): 

941 self.check_error_report() 

942 

943 def test_error_report_with_prologue(self): 

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

945 

946 def check_error_report(self, *args): 

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

948 makeFiles(self.files) 

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

950 main() 

951 expected = reindentBlock("""\ 

952 Traceback (most recent call last): 

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

954 func() 

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

956 mycode.boom() 

957 File "MYCODE", line 2, in boom 

958 [][0] 

959 IndexError: list index out of range 

960 """) 

961 if PY3: 

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

963 else: 

964 expected = expected.replace("MYCODE", "mycode.py") 

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

966 

967 def test_error_in_prologue(self): 

968 makeFiles(self.files) 

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

970 main() 

971 expected = reindentBlock("""\ 

972 Traceback (most recent call last): 

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

974 import mycode; mycode.boom() 

975 File "MYCODE", line 2, in boom 

976 [][0] 

977 IndexError: list index out of range 

978 """) 

979 if PY3: 

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

981 else: 

982 expected = expected.replace("MYCODE", "mycode.py") 

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

984 

985 

986 

987class TestFileHandling(TestCaseWithTempDir): 

988 

989 def testSimple(self): 

990 d = { 

991 'test.cog': """\ 

992 // This is my C++ file. 

993 //[[[cog 

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

995 for fn in fnames: 

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

997 //]]] 

998 //[[[end]]] 

999 """, 

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 } 

1014 

1015 makeFiles(d) 

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

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

1018 output = self.output.getvalue() 

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

1020 

1021 def testPrintOutput(self): 

1022 d = { 

1023 'test.cog': """\ 

1024 // This is my C++ file. 

1025 //[[[cog 

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

1027 for fn in fnames: 

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

1029 //]]] 

1030 //[[[end]]] 

1031 """, 

1032 

1033 'test.out': """\ 

1034 // This is my C++ file. 

1035 //[[[cog 

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

1037 for fn in fnames: 

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

1039 //]]] 

1040 void DoSomething(); 

1041 void DoAnotherThing(); 

1042 void DoLastThing(); 

1043 //[[[end]]] 

1044 """, 

1045 } 

1046 

1047 makeFiles(d) 

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

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

1050 output = self.output.getvalue() 

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

1052 

1053 def testWildcards(self): 

1054 d = { 

1055 'test.cog': """\ 

1056 // This is my C++ file. 

1057 //[[[cog 

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

1059 for fn in fnames: 

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

1061 //]]] 

1062 //[[[end]]] 

1063 """, 

1064 

1065 'test2.cog': """\ 

1066 // This is my C++ file. 

1067 //[[[cog 

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

1069 for fn in fnames: 

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

1071 //]]] 

1072 //[[[end]]] 

1073 """, 

1074 

1075 'test.out': """\ 

1076 // This is my C++ file. 

1077 //[[[cog 

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

1079 for fn in fnames: 

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

1081 //]]] 

1082 void DoSomething(); 

1083 void DoAnotherThing(); 

1084 void DoLastThing(); 

1085 //[[[end]]] 

1086 """, 

1087 

1088 'not_this_one.cog': """\ 

1089 // This is my C++ file. 

1090 //[[[cog 

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

1092 for fn in fnames: 

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

1094 //]]] 

1095 //[[[end]]] 

1096 """, 

1097 

1098 'not_this_one.out': """\ 

1099 // This is my C++ file. 

1100 //[[[cog 

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

1102 for fn in fnames: 

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

1104 //]]] 

1105 //[[[end]]] 

1106 """, 

1107 } 

1108 

1109 makeFiles(d) 

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

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

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

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

1114 output = self.output.getvalue() 

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

1116 

1117 def testOutputFile(self): 

1118 # -o sets the output file. 

1119 d = { 

1120 'test.cog': """\ 

1121 // This is my C++ file. 

1122 //[[[cog 

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

1124 for fn in fnames: 

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

1126 //]]] 

1127 //[[[end]]] 

1128 """, 

1129 

1130 'test.out': """\ 

1131 // This is my C++ file. 

1132 //[[[cog 

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

1134 for fn in fnames: 

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

1136 //]]] 

1137 void DoSomething(); 

1138 void DoAnotherThing(); 

1139 void DoLastThing(); 

1140 //[[[end]]] 

1141 """, 

1142 } 

1143 

1144 makeFiles(d) 

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

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

1147 

1148 def testAtFile(self): 

1149 d = { 

1150 'one.cog': """\ 

1151 //[[[cog 

1152 cog.outl("hello world") 

1153 //]]] 

1154 //[[[end]]] 

1155 """, 

1156 

1157 'one.out': """\ 

1158 //[[[cog 

1159 cog.outl("hello world") 

1160 //]]] 

1161 hello world 

1162 //[[[end]]] 

1163 """, 

1164 

1165 'two.cog': """\ 

1166 //[[[cog 

1167 cog.outl("goodbye cruel world") 

1168 //]]] 

1169 //[[[end]]] 

1170 """, 

1171 

1172 'two.out': """\ 

1173 //[[[cog 

1174 cog.outl("goodbye cruel world") 

1175 //]]] 

1176 goodbye cruel world 

1177 //[[[end]]] 

1178 """, 

1179 

1180 'cogfiles.txt': """\ 

1181 # Please run cog 

1182 one.cog 

1183 

1184 two.cog 

1185 """ 

1186 } 

1187 

1188 makeFiles(d) 

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

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

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

1192 output = self.output.getvalue() 

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

1194 

1195 def testNestedAtFile(self): 

1196 d = { 

1197 'one.cog': """\ 

1198 //[[[cog 

1199 cog.outl("hello world") 

1200 //]]] 

1201 //[[[end]]] 

1202 """, 

1203 

1204 'one.out': """\ 

1205 //[[[cog 

1206 cog.outl("hello world") 

1207 //]]] 

1208 hello world 

1209 //[[[end]]] 

1210 """, 

1211 

1212 'two.cog': """\ 

1213 //[[[cog 

1214 cog.outl("goodbye cruel world") 

1215 //]]] 

1216 //[[[end]]] 

1217 """, 

1218 

1219 'two.out': """\ 

1220 //[[[cog 

1221 cog.outl("goodbye cruel world") 

1222 //]]] 

1223 goodbye cruel world 

1224 //[[[end]]] 

1225 """, 

1226 

1227 'cogfiles.txt': """\ 

1228 # Please run cog 

1229 one.cog 

1230 @cogfiles2.txt 

1231 """, 

1232 

1233 'cogfiles2.txt': """\ 

1234 # This one too, please. 

1235 two.cog 

1236 """, 

1237 } 

1238 

1239 makeFiles(d) 

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

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

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

1243 output = self.output.getvalue() 

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

1245 

1246 def testAtFileWithArgs(self): 

1247 d = { 

1248 'both.cog': """\ 

1249 //[[[cog 

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

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

1252 //]]] 

1253 //[[[end]]] 

1254 """, 

1255 

1256 'one.out': """\ 

1257 //[[[cog 

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

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

1260 //]]] 

1261 one: True // ONE 

1262 two: False // ONE 

1263 //[[[end]]] 

1264 """, 

1265 

1266 'two.out': """\ 

1267 //[[[cog 

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

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

1270 //]]] 

1271 one: False // TWO 

1272 two: True // TWO 

1273 //[[[end]]] 

1274 """, 

1275 

1276 'cogfiles.txt': """\ 

1277 # Please run cog 

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

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

1280 """ 

1281 } 

1282 

1283 makeFiles(d) 

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

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

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

1287 

1288 def testAtFileWithBadArgCombo(self): 

1289 d = { 

1290 'both.cog': """\ 

1291 //[[[cog 

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

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

1294 //]]] 

1295 //[[[end]]] 

1296 """, 

1297 

1298 'cogfiles.txt': """\ 

1299 # Please run cog 

1300 both.cog 

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

1302 """ 

1303 } 

1304 

1305 makeFiles(d) 

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

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

1308 

1309 def testAtFileWithTrickyFilenames(self): 

1310 def fix_backslashes(files_txt): 

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

1312 if sys.platform != "win32": 

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

1314 return files_txt 

1315 

1316 d = { 

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

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

1319 """, 

1320 

1321 'one.out': """\ 

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

1323 hello world //xxx 

1324 """, 

1325 

1326 'subdir': { 

1327 'subback.cog': """\ 

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

1329 """, 

1330 

1331 'subfwd.cog': """\ 

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

1333 """, 

1334 }, 

1335 

1336 'subback.out': """\ 

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

1338 down deep with backslashes //yyy 

1339 """, 

1340 

1341 'subfwd.out': """\ 

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

1343 down deep with slashes //zzz 

1344 """, 

1345 

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

1347 # Please run cog 

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

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

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

1351 """) 

1352 } 

1353 

1354 makeFiles(d) 

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

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

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

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

1359 

1360 def run_with_verbosity(self, verbosity): 

1361 d = { 

1362 'unchanged.cog': """\ 

1363 //[[[cog 

1364 cog.outl("hello world") 

1365 //]]] 

1366 hello world 

1367 //[[[end]]] 

1368 """, 

1369 

1370 'changed.cog': """\ 

1371 //[[[cog 

1372 cog.outl("goodbye cruel world") 

1373 //]]] 

1374 //[[[end]]] 

1375 """, 

1376 

1377 'cogfiles.txt': """\ 

1378 unchanged.cog 

1379 changed.cog 

1380 """ 

1381 } 

1382 

1383 makeFiles(d) 

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

1385 output = self.output.getvalue() 

1386 return output 

1387 

1388 def test_verbosity0(self): 

1389 output = self.run_with_verbosity("0") 

1390 self.assertEqual(output, "") 

1391 

1392 def test_verbosity1(self): 

1393 output = self.run_with_verbosity("1") 

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

1395 

1396 def test_verbosity2(self): 

1397 output = self.run_with_verbosity("2") 

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

1399 

1400 

1401class CogTestLineEndings(TestCaseWithTempDir): 

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

1403 

1404 lines_in = ['Some text.', 

1405 '//[[[cog', 

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

1407 '//]]]', 

1408 'gobbledegook.', 

1409 '//[[[end]]]', 

1410 'epilogue.', 

1411 ''] 

1412 

1413 lines_out = ['Some text.', 

1414 '//[[[cog', 

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

1416 '//]]]', 

1417 'Cog text', 

1418 '//[[[end]]]', 

1419 'epilogue.', 

1420 ''] 

1421 

1422 def testOutputNativeEol(self): 

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

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

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

1426 

1427 def testOutputLfEol(self): 

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

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

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

1431 

1432 def testReplaceNativeEol(self): 

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

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

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

1436 

1437 def testReplaceLfEol(self): 

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

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

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

1441 

1442 

1443class CogTestCharacterEncoding(TestCaseWithTempDir): 

1444 

1445 def testSimple(self): 

1446 d = { 

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

1448 // This is my C++ file. 

1449 //[[[cog 

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

1451 //]]] 

1452 //[[[end]]] 

1453 """, 

1454 

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

1456 // This is my C++ file. 

1457 //[[[cog 

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

1459 //]]] 

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

1461 //[[[end]]] 

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

1463 } 

1464 

1465 makeFiles(d, bytes=True) 

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

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

1468 output = self.output.getvalue() 

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

1470 

1471 def testFileEncodingOption(self): 

1472 d = { 

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

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

1475 //[[[cog 

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

1477 //]]] 

1478 //[[[end]]] 

1479 """, 

1480 

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

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

1483 //[[[cog 

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

1485 //]]] 

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

1487 //[[[end]]] 

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

1489 } 

1490 

1491 makeFiles(d, bytes=True) 

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

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

1494 output = self.output.getvalue() 

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

1496 

1497 

1498class TestCaseWithImports(TestCaseWithTempDir): 

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

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

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

1502 """ 

1503 

1504 def setUp(self): 

1505 super(TestCaseWithImports, self).setUp() 

1506 self.sysmodulekeys = list(sys.modules) 

1507 

1508 def tearDown(self): 

1509 modstoscrub = [ 

1510 modname 

1511 for modname in sys.modules 

1512 if modname not in self.sysmodulekeys 

1513 ] 

1514 for modname in modstoscrub: 

1515 del sys.modules[modname] 

1516 super(TestCaseWithImports, self).tearDown() 

1517 

1518 

1519class CogIncludeTests(TestCaseWithImports): 

1520 dincludes = { 

1521 'test.cog': """\ 

1522 //[[[cog 

1523 import mymodule 

1524 //]]] 

1525 //[[[end]]] 

1526 """, 

1527 

1528 'test.out': """\ 

1529 //[[[cog 

1530 import mymodule 

1531 //]]] 

1532 Hello from mymodule 

1533 //[[[end]]] 

1534 """, 

1535 

1536 'test2.out': """\ 

1537 //[[[cog 

1538 import mymodule 

1539 //]]] 

1540 Hello from mymodule in inc2 

1541 //[[[end]]] 

1542 """, 

1543 

1544 'include': { 

1545 'mymodule.py': """\ 

1546 import cog 

1547 cog.outl("Hello from mymodule") 

1548 """ 

1549 }, 

1550 

1551 'inc2': { 

1552 'mymodule.py': """\ 

1553 import cog 

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

1555 """ 

1556 }, 

1557 

1558 'inc3': { 

1559 'someothermodule.py': """\ 

1560 import cog 

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

1562 """ 

1563 }, 

1564 } 

1565 

1566 def testNeedIncludePath(self): 

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

1568 makeFiles(self.dincludes) 

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

1570 with self.assertRaisesRegex(CogUserException, msg): 

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

1572 

1573 def testIncludePath(self): 

1574 # Test that -I adds include directories properly. 

1575 makeFiles(self.dincludes) 

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

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

1578 

1579 def testTwoIncludePaths(self): 

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

1581 makeFiles(self.dincludes) 

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

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

1584 

1585 def testTwoIncludePaths2(self): 

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

1587 makeFiles(self.dincludes) 

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

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

1590 

1591 def testUselessIncludePath(self): 

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

1593 makeFiles(self.dincludes) 

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

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

1596 

1597 def testSysPathIsUnchanged(self): 

1598 d = { 

1599 'bad.cog': """\ 

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

1601 //[[[end]]] 

1602 """, 

1603 'good.cog': """\ 

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

1605 //[[[end]]] 

1606 """, 

1607 } 

1608 

1609 makeFiles(d) 

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

1611 oldsyspath = sys.path[:] 

1612 self.newCog() 

1613 self.assertEqual(oldsyspath, sys.path) 

1614 # Is it unchanged for a successful run? 

1615 self.newCog() 

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

1617 self.assertEqual(oldsyspath, sys.path) 

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

1619 self.newCog() 

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

1621 self.assertEqual(oldsyspath, sys.path) 

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

1623 self.newCog() 

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

1625 self.assertEqual(oldsyspath, sys.path) 

1626 # Is it unchanged for a failed run? 

1627 self.newCog() 

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

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

1630 self.assertEqual(oldsyspath, sys.path) 

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

1632 self.newCog() 

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

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

1635 self.assertEqual(oldsyspath, sys.path) 

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

1637 self.newCog() 

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

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

1640 self.assertEqual(oldsyspath, sys.path) 

1641 

1642 def testSubDirectories(self): 

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

1644 

1645 d = { 

1646 'code': { 

1647 'test.cog': """\ 

1648 //[[[cog 

1649 import mysubmodule 

1650 //]]] 

1651 //[[[end]]] 

1652 """, 

1653 

1654 'test.out': """\ 

1655 //[[[cog 

1656 import mysubmodule 

1657 //]]] 

1658 Hello from mysubmodule 

1659 //[[[end]]] 

1660 """, 

1661 

1662 'mysubmodule.py': """\ 

1663 import cog 

1664 cog.outl("Hello from mysubmodule") 

1665 """ 

1666 } 

1667 } 

1668 

1669 makeFiles(d) 

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

1671 # auto-include the current directory 

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

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

1674 

1675 

1676class CogTestsInFiles(TestCaseWithTempDir): 

1677 

1678 def testWarnIfNoCogCode(self): 

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

1680 d = { 

1681 'with.cog': """\ 

1682 //[[[cog 

1683 cog.outl("hello world") 

1684 //]]] 

1685 hello world 

1686 //[[[end]]] 

1687 """, 

1688 

1689 'without.cog': """\ 

1690 There's no cog 

1691 code in this file. 

1692 """, 

1693 } 

1694 

1695 makeFiles(d) 

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

1697 output = self.output.getvalue() 

1698 self.assertNotIn("Warning", output) 

1699 self.newCog() 

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

1701 output = self.output.getvalue() 

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

1703 self.newCog() 

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

1705 output = self.output.getvalue() 

1706 self.assertNotIn("Warning", output) 

1707 

1708 def testFileNameProps(self): 

1709 d = { 

1710 'cog1.txt': """\ 

1711 //[[[cog 

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

1713 //]]] 

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

1715 [[[end]]] 

1716 """, 

1717 

1718 'cog1.out': """\ 

1719 //[[[cog 

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

1721 //]]] 

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

1723 [[[end]]] 

1724 """, 

1725 

1726 'cog1out.out': """\ 

1727 //[[[cog 

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

1729 //]]] 

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

1731 [[[end]]] 

1732 """, 

1733 } 

1734 

1735 makeFiles(d) 

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

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

1738 self.newCog() 

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

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

1741 

1742 def testGlobalsDontCrossFiles(self): 

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

1744 d = { 

1745 'one.cog': """\ 

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

1747 //[[[end]]] 

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

1749 //[[[end]]] 

1750 """, 

1751 

1752 'one.out': """\ 

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

1754 //[[[end]]] 

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

1756 This was set in one.cog 

1757 //[[[end]]] 

1758 """, 

1759 

1760 'two.cog': """\ 

1761 //[[[cog 

1762 try: 

1763 cog.outl(s) 

1764 except NameError: 

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

1766 //]]] 

1767 //[[[end]]] 

1768 """, 

1769 

1770 'two.out': """\ 

1771 //[[[cog 

1772 try: 

1773 cog.outl(s) 

1774 except NameError: 

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

1776 //]]] 

1777 s isn't set! 

1778 //[[[end]]] 

1779 """, 

1780 

1781 'cogfiles.txt': """\ 

1782 # Please run cog 

1783 one.cog 

1784 

1785 two.cog 

1786 """ 

1787 } 

1788 

1789 makeFiles(d) 

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

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

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

1793 output = self.output.getvalue() 

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

1795 

1796 def testRemoveGeneratedOutput(self): 

1797 d = { 

1798 'cog1.txt': """\ 

1799 //[[[cog 

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

1801 //]]] 

1802 This line was generated. 

1803 //[[[end]]] 

1804 This line was not. 

1805 """, 

1806 

1807 'cog1.out': """\ 

1808 //[[[cog 

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

1810 //]]] 

1811 //[[[end]]] 

1812 This line was not. 

1813 """, 

1814 

1815 'cog1.out2': """\ 

1816 //[[[cog 

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

1818 //]]] 

1819 This line was generated. 

1820 //[[[end]]] 

1821 This line was not. 

1822 """, 

1823 } 

1824 

1825 makeFiles(d) 

1826 # Remove generated output. 

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

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

1829 self.newCog() 

1830 # Regenerate the generated output. 

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

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

1833 self.newCog() 

1834 # Remove the generated output again. 

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

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

1837 

1838 def testMsgCall(self): 

1839 infile = """\ 

1840 #[[[cog 

1841 cog.msg("Hello there!") 

1842 #]]] 

1843 #[[[end]]] 

1844 """ 

1845 infile = reindentBlock(infile) 

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

1847 output = self.output.getvalue() 

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

1849 

1850 def testErrorMessageHasNoTraceback(self): 

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

1852 

1853 d = { 

1854 'cog1.txt': """\ 

1855 //[[[cog 

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

1857 cog.outl("generated by cog") 

1858 cog.outl("blah blah.") 

1859 //]]] 

1860 Xhis line was newly 

1861 generated by cog 

1862 blah blah. 

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

1864 """, 

1865 } 

1866 

1867 makeFiles(d) 

1868 stderr = StringIO() 

1869 self.cog.setOutput(stderr=stderr) 

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

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

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

1873 

1874 def testDashD(self): 

1875 d = { 

1876 'test.cog': """\ 

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

1878 --[[[end]]] 

1879 """, 

1880 

1881 'test.kablooey': """\ 

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

1883 Defined fooey as kablooey 

1884 --[[[end]]] 

1885 """, 

1886 

1887 'test.einstein': """\ 

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

1889 Defined fooey as e=mc2 

1890 --[[[end]]] 

1891 """, 

1892 } 

1893 

1894 makeFiles(d) 

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

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

1897 makeFiles(d) 

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

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

1900 makeFiles(d) 

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

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

1903 makeFiles(d) 

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

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

1906 makeFiles(d) 

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

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

1909 makeFiles(d) 

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

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

1912 

1913 def testOutputToStdout(self): 

1914 d = { 

1915 'test.cog': """\ 

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

1917 --[[[end]]] 

1918 """ 

1919 } 

1920 

1921 makeFiles(d) 

1922 stderr = StringIO() 

1923 self.cog.setOutput(stderr=stderr) 

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

1925 output = self.output.getvalue() 

1926 outerr = stderr.getvalue() 

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

1928 self.assertEqual(outerr, "") 

1929 

1930 def testReadFromStdin(self): 

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

1932 def restore_stdin(old_stdin): 

1933 sys.stdin = old_stdin 

1934 self.addCleanup(restore_stdin, sys.stdin) 

1935 sys.stdin = stdin 

1936 

1937 stderr = StringIO() 

1938 self.cog.setOutput(stderr=stderr) 

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

1940 output = self.output.getvalue() 

1941 outerr = stderr.getvalue() 

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

1943 self.assertEqual(outerr, "") 

1944 

1945 def testSuffixOutputLines(self): 

1946 d = { 

1947 'test.cog': """\ 

1948 Hey there. 

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

1950 ;[[[end]]] 

1951 Good bye. 

1952 """, 

1953 

1954 'test.out': """\ 

1955 Hey there. 

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

1957 a (foo) 

1958 b (foo) 

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

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

1961 """ 

1962 c (foo) 

1963 ;[[[end]]] 

1964 Good bye. 

1965 """, 

1966 } 

1967 

1968 makeFiles(d) 

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

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

1971 

1972 def testEmptySuffix(self): 

1973 d = { 

1974 'test.cog': """\ 

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

1976 ;[[[end]]] 

1977 """, 

1978 

1979 'test.out': """\ 

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

1981 a 

1982 b 

1983 c 

1984 ;[[[end]]] 

1985 """, 

1986 } 

1987 

1988 makeFiles(d) 

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

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

1991 

1992 def testHellishSuffix(self): 

1993 d = { 

1994 'test.cog': """\ 

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

1996 """, 

1997 

1998 'test.out': """\ 

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

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

2001 

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

2003 """, 

2004 } 

2005 

2006 makeFiles(d) 

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

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

2009 

2010 def testPrologue(self): 

2011 d = { 

2012 'test.cog': """\ 

2013 Some text. 

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

2015 //[[[end]]] 

2016 epilogue. 

2017 """, 

2018 

2019 'test.out': """\ 

2020 Some text. 

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

2022 1.4142135623 

2023 //[[[end]]] 

2024 epilogue. 

2025 """, 

2026 } 

2027 

2028 makeFiles(d) 

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

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

2031 

2032 def testThreads(self): 

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

2034 # different threads. 

2035 numthreads = 20 

2036 

2037 d = {} 

2038 for i in range(numthreads): 

2039 d['f{}.cog'.format(i)] = ( 

2040 "x\n" * i + 

2041 "[[[cog\n" + 

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

2043 "]]]\n" + 

2044 "[[[end]]]\n" 

2045 ) 

2046 makeFiles(d) 

2047 

2048 results = [] 

2049 

2050 def thread_main(num): 

2051 try: 

2052 ret = Cog().main( 

2053 ['cog.py', '-r', '-D', 'FIRST={}'.format(num+1), 'f{}.cog'.format(num)] 

2054 ) 

2055 assert ret == 0 

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

2057 results.append(exc) 

2058 else: 

2059 results.append(None) 

2060 

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

2062 for t in ts: 

2063 t.start() 

2064 for t in ts: 

2065 t.join() 

2066 assert results == [None] * numthreads 

2067 

2068 

2069class CheckTests(TestCaseWithTempDir): 

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

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

2072 print(self.output.getvalue()) 

2073 self.assertEqual(status, actual_status) 

2074 

2075 def assert_made_files_unchanged(self, d): 

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

2077 content = reindentBlock(content) 

2078 if os.name == 'nt': 

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

2080 self.assertFileContent(name, content) 

2081 

2082 def test_check_no_cog(self): 

2083 d = { 

2084 'hello.txt': """\ 

2085 Hello. 

2086 """, 

2087 } 

2088 makeFiles(d) 

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

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

2091 self.assert_made_files_unchanged(d) 

2092 

2093 def test_check_good(self): 

2094 d = { 

2095 'unchanged.cog': """\ 

2096 //[[[cog 

2097 cog.outl("hello world") 

2098 //]]] 

2099 hello world 

2100 //[[[end]]] 

2101 """, 

2102 } 

2103 makeFiles(d) 

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

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

2106 self.assert_made_files_unchanged(d) 

2107 

2108 def test_check_bad(self): 

2109 d = { 

2110 'changed.cog': """\ 

2111 //[[[cog 

2112 cog.outl("goodbye world") 

2113 //]]] 

2114 hello world 

2115 //[[[end]]] 

2116 """, 

2117 } 

2118 makeFiles(d) 

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

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

2121 self.assert_made_files_unchanged(d) 

2122 

2123 def test_check_mixed(self): 

2124 d = { 

2125 'unchanged.cog': """\ 

2126 //[[[cog 

2127 cog.outl("hello world") 

2128 //]]] 

2129 hello world 

2130 //[[[end]]] 

2131 """, 

2132 'changed.cog': """\ 

2133 //[[[cog 

2134 cog.outl("goodbye world") 

2135 //]]] 

2136 hello world 

2137 //[[[end]]] 

2138 """, 

2139 } 

2140 makeFiles(d) 

2141 for verbosity, output in [ 

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

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

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

2145 ]: 

2146 self.newCog() 

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

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

2149 self.assert_made_files_unchanged(d) 

2150 

2151 def test_check_with_good_checksum(self): 

2152 d = { 

2153 'good.txt': """\ 

2154 //[[[cog 

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

2156 cog.outl("generated by cog") 

2157 cog.outl("blah blah.") 

2158 //]]] 

2159 This line was newly 

2160 generated by cog 

2161 blah blah. 

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

2163 """, 

2164 } 

2165 makeFiles(d) 

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

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

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

2169 self.assert_made_files_unchanged(d) 

2170 

2171 def test_check_with_bad_checksum(self): 

2172 d = { 

2173 'bad.txt': """\ 

2174 //[[[cog 

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

2176 cog.outl("generated by cog") 

2177 cog.outl("blah blah.") 

2178 //]]] 

2179 This line was newly 

2180 generated by cog 

2181 blah blah. 

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

2183 """, 

2184 } 

2185 makeFiles(d) 

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

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

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

2189 self.assert_made_files_unchanged(d) 

2190 

2191 

2192class WritabilityTests(TestCaseWithTempDir): 

2193 

2194 d = { 

2195 'test.cog': """\ 

2196 //[[[cog 

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

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

2199 //]]] 

2200 //[[[end]]] 

2201 """, 

2202 

2203 'test.out': """\ 

2204 //[[[cog 

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

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

2207 //]]] 

2208 void DoSomething(); 

2209 void DoAnotherThing(); 

2210 void DoLastThing(); 

2211 //[[[end]]] 

2212 """, 

2213 } 

2214 

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

2216 # for Windows 

2217 cmd_w_args = 'attrib -R %s' 

2218 cmd_w_asterisk = 'attrib -R *' 

2219 else: 

2220 # for unix-like 

2221 cmd_w_args = 'chmod +w %s' 

2222 cmd_w_asterisk = 'chmod +w *' 

2223 

2224 def setUp(self): 

2225 super(WritabilityTests, self).setUp() 

2226 makeFiles(self.d) 

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

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

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

2230 

2231 def tearDown(self): 

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

2233 super(WritabilityTests, self).tearDown() 

2234 

2235 def testReadonlyNoCommand(self): 

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

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

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

2239 

2240 def testReadonlyWithCommand(self): 

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

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

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

2244 

2245 def testReadonlyWithCommandWithNoSlot(self): 

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

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

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

2249 

2250 def testReadonlyWithIneffectualCommand(self): 

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

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

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

2254 

2255 

2256class ChecksumTests(TestCaseWithTempDir): 

2257 

2258 def testCreateChecksumOutput(self): 

2259 d = { 

2260 'cog1.txt': """\ 

2261 //[[[cog 

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

2263 //]]] 

2264 This line was generated. 

2265 //[[[end]]] 

2266 This line was not. 

2267 """, 

2268 

2269 'cog1.out': """\ 

2270 //[[[cog 

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

2272 //]]] 

2273 This line was generated. 

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

2275 This line was not. 

2276 """, 

2277 } 

2278 

2279 makeFiles(d) 

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

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

2282 

2283 def testCheckChecksumOutput(self): 

2284 d = { 

2285 'cog1.txt': """\ 

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 generated. 

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

2293 """, 

2294 

2295 'cog1.out': """\ 

2296 //[[[cog 

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

2298 cog.outl("generated by cog") 

2299 cog.outl("blah blah.") 

2300 //]]] 

2301 This line was newly 

2302 generated by cog 

2303 blah blah. 

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

2305 """, 

2306 } 

2307 

2308 makeFiles(d) 

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

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

2311 

2312 def testRemoveChecksumOutput(self): 

2313 d = { 

2314 'cog1.txt': """\ 

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 generated. 

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

2322 """, 

2323 

2324 'cog1.out': """\ 

2325 //[[[cog 

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

2327 cog.outl("generated by cog") 

2328 cog.outl("blah blah.") 

2329 //]]] 

2330 This line was newly 

2331 generated by cog 

2332 blah blah. 

2333 //[[[end]]] fooey 

2334 """, 

2335 } 

2336 

2337 makeFiles(d) 

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

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

2340 

2341 def testTamperedChecksumOutput(self): 

2342 d = { 

2343 'cog1.txt': """\ 

2344 //[[[cog 

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

2346 cog.outl("generated by cog") 

2347 cog.outl("blah blah.") 

2348 //]]] 

2349 Xhis line was newly 

2350 generated by cog 

2351 blah blah. 

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

2353 """, 

2354 

2355 'cog2.txt': """\ 

2356 //[[[cog 

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

2358 cog.outl("generated by cog") 

2359 cog.outl("blah blah.") 

2360 //]]] 

2361 This line was newly 

2362 generated by cog 

2363 blah blah! 

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

2365 """, 

2366 

2367 'cog3.txt': """\ 

2368 //[[[cog 

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

2370 cog.outl("generated by cog") 

2371 cog.outl("blah blah.") 

2372 //]]] 

2373 

2374 This line was newly 

2375 generated by cog 

2376 blah blah. 

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

2378 """, 

2379 

2380 'cog4.txt': """\ 

2381 //[[[cog 

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

2383 cog.outl("generated by cog") 

2384 cog.outl("blah blah.") 

2385 //]]] 

2386 This line was newly 

2387 generated by cog 

2388 blah blah.. 

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

2390 """, 

2391 

2392 'cog5.txt': """\ 

2393 //[[[cog 

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

2395 cog.outl("generated by cog") 

2396 cog.outl("blah blah.") 

2397 //]]] 

2398 This line was newly 

2399 generated by cog 

2400 blah blah. 

2401 extra 

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

2403 """, 

2404 

2405 'cog6.txt': """\ 

2406 //[[[cog 

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

2408 cog.outl("generated by cog") 

2409 cog.outl("blah blah.") 

2410 //]]] 

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

2412 """, 

2413 } 

2414 

2415 makeFiles(d) 

2416 with self.assertRaisesRegex(CogError, 

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

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

2419 with self.assertRaisesRegex(CogError, 

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

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

2422 with self.assertRaisesRegex(CogError, 

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

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

2425 with self.assertRaisesRegex(CogError, 

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

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

2428 with self.assertRaisesRegex(CogError, 

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

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

2431 with self.assertRaisesRegex(CogError, 

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

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

2434 

2435 def testArgvIsntModified(self): 

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

2437 orig_argv = argv[:] 

2438 self.cog.callableMain(argv) 

2439 self.assertEqual(argv, orig_argv) 

2440 

2441 

2442class CustomMarkerTests(TestCaseWithTempDir): 

2443 

2444 def testCustomerMarkers(self): 

2445 d = { 

2446 'test.cog': """\ 

2447 //{{ 

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

2449 //}} 

2450 //{{end}} 

2451 """, 

2452 

2453 'test.out': """\ 

2454 //{{ 

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

2456 //}} 

2457 void MyFunction(); 

2458 //{{end}} 

2459 """, 

2460 } 

2461 

2462 makeFiles(d) 

2463 self.cog.callableMain([ 

2464 'argv0', '-r', 

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

2466 'test.cog' 

2467 ]) 

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

2469 

2470 def testTrulyWackyMarkers(self): 

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

2472 d = { 

2473 'test.cog': """\ 

2474 //**( 

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

2476 //**) 

2477 //**(end)** 

2478 """, 

2479 

2480 'test.out': """\ 

2481 //**( 

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

2483 //**) 

2484 void MyFunction(); 

2485 //**(end)** 

2486 """, 

2487 } 

2488 

2489 makeFiles(d) 

2490 self.cog.callableMain([ 

2491 'argv0', '-r', 

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

2493 'test.cog' 

2494 ]) 

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

2496 

2497 def testChangeJustOneMarker(self): 

2498 d = { 

2499 'test.cog': """\ 

2500 //**( 

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

2502 //]]] 

2503 //[[[end]]] 

2504 """, 

2505 

2506 'test.out': """\ 

2507 //**( 

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

2509 //]]] 

2510 void MyFunction(); 

2511 //[[[end]]] 

2512 """, 

2513 } 

2514 

2515 makeFiles(d) 

2516 self.cog.callableMain([ 

2517 'argv0', '-r', 

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

2519 'test.cog' 

2520 ]) 

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

2522 

2523 

2524class BlakeTests(TestCaseWithTempDir): 

2525 

2526 # Blake Winton's contributions. 

2527 def testDeleteCode(self): 

2528 # -o sets the output file. 

2529 d = { 

2530 'test.cog': """\ 

2531 // This is my C++ file. 

2532 //[[[cog 

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

2534 for fn in fnames: 

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

2536 //]]] 

2537 Some Sample Code Here 

2538 //[[[end]]]Data Data 

2539 And Some More 

2540 """, 

2541 

2542 'test.out': """\ 

2543 // This is my C++ file. 

2544 void DoSomething(); 

2545 void DoAnotherThing(); 

2546 void DoLastThing(); 

2547 And Some More 

2548 """, 

2549 } 

2550 

2551 makeFiles(d) 

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

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

2554 

2555 def testDeleteCodeWithDashRFails(self): 

2556 d = { 

2557 'test.cog': """\ 

2558 // This is my C++ file. 

2559 """ 

2560 } 

2561 

2562 makeFiles(d) 

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

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

2565 

2566 def testSettingGlobals(self): 

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

2568 # processFile(). 

2569 d = { 

2570 'test.cog': """\ 

2571 // This is my C++ file. 

2572 //[[[cog 

2573 for fn in fnames: 

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

2575 //]]] 

2576 Some Sample Code Here 

2577 //[[[end]]]""", 

2578 

2579 'test.out': """\ 

2580 // This is my C++ file. 

2581 void DoBlake(); 

2582 void DoWinton(); 

2583 void DoContribution(); 

2584 """, 

2585 } 

2586 

2587 makeFiles(d) 

2588 globals = {} 

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

2590 self.cog.options.bDeleteCode = True 

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

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

2593 

2594 

2595class ErrorCallTests(TestCaseWithTempDir): 

2596 

2597 def testErrorCallHasNoTraceback(self): 

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

2599 d = { 

2600 'error.cog': """\ 

2601 //[[[cog 

2602 cog.error("Something Bad!") 

2603 //]]] 

2604 //[[[end]]] 

2605 """, 

2606 } 

2607 

2608 makeFiles(d) 

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

2610 output = self.output.getvalue() 

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

2612 

2613 def testRealErrorHasTraceback(self): 

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

2615 d = { 

2616 'error.cog': """\ 

2617 //[[[cog 

2618 raise RuntimeError("Hey!") 

2619 //]]] 

2620 //[[[end]]] 

2621 """, 

2622 } 

2623 

2624 makeFiles(d) 

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

2626 output = self.output.getvalue() 

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

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

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

2630 

2631 

2632# Things not yet tested: 

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