Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" Test cogapp. 

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

3 

4 Copyright 2004-2019, 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 -?)\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 testWildcards(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 cog.outl("void %s();" % fn) 

1029 //]]] 

1030 //[[[end]]] 

1031 """, 

1032 

1033 'test2.cog': """\ 

1034 // This is my C++ file. 

1035 //[[[cog 

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

1037 for fn in fnames: 

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

1039 //]]] 

1040 //[[[end]]] 

1041 """, 

1042 

1043 'test.out': """\ 

1044 // This is my C++ file. 

1045 //[[[cog 

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

1047 for fn in fnames: 

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

1049 //]]] 

1050 void DoSomething(); 

1051 void DoAnotherThing(); 

1052 void DoLastThing(); 

1053 //[[[end]]] 

1054 """, 

1055 

1056 'not_this_one.cog': """\ 

1057 // This is my C++ file. 

1058 //[[[cog 

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

1060 for fn in fnames: 

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

1062 //]]] 

1063 //[[[end]]] 

1064 """, 

1065 

1066 'not_this_one.out': """\ 

1067 // This is my C++ file. 

1068 //[[[cog 

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

1070 for fn in fnames: 

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

1072 //]]] 

1073 //[[[end]]] 

1074 """, 

1075 } 

1076 

1077 makeFiles(d) 

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

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

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

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

1082 output = self.output.getvalue() 

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

1084 

1085 def testOutputFile(self): 

1086 # -o sets the output file. 

1087 d = { 

1088 'test.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 'test.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 void DoSomething(); 

1106 void DoAnotherThing(); 

1107 void DoLastThing(); 

1108 //[[[end]]] 

1109 """, 

1110 } 

1111 

1112 makeFiles(d) 

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

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

1115 

1116 def testAtFile(self): 

1117 d = { 

1118 'one.cog': """\ 

1119 //[[[cog 

1120 cog.outl("hello world") 

1121 //]]] 

1122 //[[[end]]] 

1123 """, 

1124 

1125 'one.out': """\ 

1126 //[[[cog 

1127 cog.outl("hello world") 

1128 //]]] 

1129 hello world 

1130 //[[[end]]] 

1131 """, 

1132 

1133 'two.cog': """\ 

1134 //[[[cog 

1135 cog.outl("goodbye cruel world") 

1136 //]]] 

1137 //[[[end]]] 

1138 """, 

1139 

1140 'two.out': """\ 

1141 //[[[cog 

1142 cog.outl("goodbye cruel world") 

1143 //]]] 

1144 goodbye cruel world 

1145 //[[[end]]] 

1146 """, 

1147 

1148 'cogfiles.txt': """\ 

1149 # Please run cog 

1150 one.cog 

1151 

1152 two.cog 

1153 """ 

1154 } 

1155 

1156 makeFiles(d) 

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

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

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

1160 output = self.output.getvalue() 

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

1162 

1163 def testNestedAtFile(self): 

1164 d = { 

1165 'one.cog': """\ 

1166 //[[[cog 

1167 cog.outl("hello world") 

1168 //]]] 

1169 //[[[end]]] 

1170 """, 

1171 

1172 'one.out': """\ 

1173 //[[[cog 

1174 cog.outl("hello world") 

1175 //]]] 

1176 hello world 

1177 //[[[end]]] 

1178 """, 

1179 

1180 'two.cog': """\ 

1181 //[[[cog 

1182 cog.outl("goodbye cruel world") 

1183 //]]] 

1184 //[[[end]]] 

1185 """, 

1186 

1187 'two.out': """\ 

1188 //[[[cog 

1189 cog.outl("goodbye cruel world") 

1190 //]]] 

1191 goodbye cruel world 

1192 //[[[end]]] 

1193 """, 

1194 

1195 'cogfiles.txt': """\ 

1196 # Please run cog 

1197 one.cog 

1198 @cogfiles2.txt 

1199 """, 

1200 

1201 'cogfiles2.txt': """\ 

1202 # This one too, please. 

1203 two.cog 

1204 """, 

1205 } 

1206 

1207 makeFiles(d) 

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

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

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

1211 output = self.output.getvalue() 

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

1213 

1214 def testAtFileWithArgs(self): 

1215 d = { 

1216 'both.cog': """\ 

1217 //[[[cog 

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

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

1220 //]]] 

1221 //[[[end]]] 

1222 """, 

1223 

1224 'one.out': """\ 

1225 //[[[cog 

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

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

1228 //]]] 

1229 one: True // ONE 

1230 two: False // ONE 

1231 //[[[end]]] 

1232 """, 

1233 

1234 'two.out': """\ 

1235 //[[[cog 

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

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

1238 //]]] 

1239 one: False // TWO 

1240 two: True // TWO 

1241 //[[[end]]] 

1242 """, 

1243 

1244 'cogfiles.txt': """\ 

1245 # Please run cog 

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

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

1248 """ 

1249 } 

1250 

1251 makeFiles(d) 

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

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

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

1255 

1256 def testAtFileWithBadArgCombo(self): 

1257 d = { 

1258 'both.cog': """\ 

1259 //[[[cog 

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

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

1262 //]]] 

1263 //[[[end]]] 

1264 """, 

1265 

1266 'cogfiles.txt': """\ 

1267 # Please run cog 

1268 both.cog 

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

1270 """ 

1271 } 

1272 

1273 makeFiles(d) 

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

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

1276 

1277 def testAtFileWithTrickyFilenames(self): 

1278 def fix_backslashes(files_txt): 

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

1280 if sys.platform != "win32": 

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

1282 return files_txt 

1283 

1284 d = { 

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

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

1287 """, 

1288 

1289 'one.out': """\ 

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

1291 hello world //xxx 

1292 """, 

1293 

1294 'subdir': { 

1295 'subback.cog': """\ 

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

1297 """, 

1298 

1299 'subfwd.cog': """\ 

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

1301 """, 

1302 }, 

1303 

1304 'subback.out': """\ 

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

1306 down deep with backslashes //yyy 

1307 """, 

1308 

1309 'subfwd.out': """\ 

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

1311 down deep with slashes //zzz 

1312 """, 

1313 

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

1315 # Please run cog 

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

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

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

1319 """) 

1320 } 

1321 

1322 makeFiles(d) 

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

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

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

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

1327 

1328 def run_with_verbosity(self, verbosity): 

1329 d = { 

1330 'unchanged.cog': """\ 

1331 //[[[cog 

1332 cog.outl("hello world") 

1333 //]]] 

1334 hello world 

1335 //[[[end]]] 

1336 """, 

1337 

1338 'changed.cog': """\ 

1339 //[[[cog 

1340 cog.outl("goodbye cruel world") 

1341 //]]] 

1342 //[[[end]]] 

1343 """, 

1344 

1345 'cogfiles.txt': """\ 

1346 unchanged.cog 

1347 changed.cog 

1348 """ 

1349 } 

1350 

1351 makeFiles(d) 

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

1353 output = self.output.getvalue() 

1354 return output 

1355 

1356 def test_verbosity0(self): 

1357 output = self.run_with_verbosity("0") 

1358 self.assertEqual(output, "") 

1359 

1360 def test_verbosity1(self): 

1361 output = self.run_with_verbosity("1") 

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

1363 

1364 def test_verbosity2(self): 

1365 output = self.run_with_verbosity("2") 

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

1367 

1368 

1369class CogTestLineEndings(TestCaseWithTempDir): 

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

1371 

1372 lines_in = ['Some text.', 

1373 '//[[[cog', 

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

1375 '//]]]', 

1376 'gobbledegook.', 

1377 '//[[[end]]]', 

1378 'epilogue.', 

1379 ''] 

1380 

1381 lines_out = ['Some text.', 

1382 '//[[[cog', 

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

1384 '//]]]', 

1385 'Cog text', 

1386 '//[[[end]]]', 

1387 'epilogue.', 

1388 ''] 

1389 

1390 def testOutputNativeEol(self): 

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

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

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

1394 

1395 def testOutputLfEol(self): 

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

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

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

1399 

1400 def testReplaceNativeEol(self): 

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

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

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

1404 

1405 def testReplaceLfEol(self): 

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

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

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

1409 

1410 

1411class CogTestCharacterEncoding(TestCaseWithTempDir): 

1412 

1413 def testSimple(self): 

1414 d = { 

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

1416 // This is my C++ file. 

1417 //[[[cog 

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

1419 //]]] 

1420 //[[[end]]] 

1421 """, 

1422 

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

1424 // This is my C++ file. 

1425 //[[[cog 

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

1427 //]]] 

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

1429 //[[[end]]] 

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

1431 } 

1432 

1433 makeFiles(d, bytes=True) 

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

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

1436 output = self.output.getvalue() 

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

1438 

1439 def testFileEncodingOption(self): 

1440 d = { 

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

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

1443 //[[[cog 

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

1445 //]]] 

1446 //[[[end]]] 

1447 """, 

1448 

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

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

1451 //[[[cog 

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

1453 //]]] 

1454 \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 

1455 //[[[end]]] 

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

1457 } 

1458 

1459 makeFiles(d, bytes=True) 

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

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

1462 output = self.output.getvalue() 

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

1464 

1465 

1466class TestCaseWithImports(TestCaseWithTempDir): 

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

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

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

1470 """ 

1471 

1472 def setUp(self): 

1473 super(TestCaseWithImports, self).setUp() 

1474 self.sysmodulekeys = list(sys.modules) 

1475 

1476 def tearDown(self): 

1477 modstoscrub = [ 

1478 modname 

1479 for modname in sys.modules 

1480 if modname not in self.sysmodulekeys 

1481 ] 

1482 for modname in modstoscrub: 

1483 del sys.modules[modname] 

1484 super(TestCaseWithImports, self).tearDown() 

1485 

1486 

1487class CogIncludeTests(TestCaseWithImports): 

1488 dincludes = { 

1489 'test.cog': """\ 

1490 //[[[cog 

1491 import mymodule 

1492 //]]] 

1493 //[[[end]]] 

1494 """, 

1495 

1496 'test.out': """\ 

1497 //[[[cog 

1498 import mymodule 

1499 //]]] 

1500 Hello from mymodule 

1501 //[[[end]]] 

1502 """, 

1503 

1504 'test2.out': """\ 

1505 //[[[cog 

1506 import mymodule 

1507 //]]] 

1508 Hello from mymodule in inc2 

1509 //[[[end]]] 

1510 """, 

1511 

1512 'include': { 

1513 'mymodule.py': """\ 

1514 import cog 

1515 cog.outl("Hello from mymodule") 

1516 """ 

1517 }, 

1518 

1519 'inc2': { 

1520 'mymodule.py': """\ 

1521 import cog 

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

1523 """ 

1524 }, 

1525 

1526 'inc3': { 

1527 'someothermodule.py': """\ 

1528 import cog 

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

1530 """ 

1531 }, 

1532 } 

1533 

1534 def testNeedIncludePath(self): 

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

1536 makeFiles(self.dincludes) 

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

1538 with self.assertRaisesRegex(CogUserException, msg): 

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

1540 

1541 def testIncludePath(self): 

1542 # Test that -I adds include directories properly. 

1543 makeFiles(self.dincludes) 

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

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

1546 

1547 def testTwoIncludePaths(self): 

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

1549 makeFiles(self.dincludes) 

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

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

1552 

1553 def testTwoIncludePaths2(self): 

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

1555 makeFiles(self.dincludes) 

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

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

1558 

1559 def testUselessIncludePath(self): 

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

1561 makeFiles(self.dincludes) 

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

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

1564 

1565 def testSysPathIsUnchanged(self): 

1566 d = { 

1567 'bad.cog': """\ 

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

1569 //[[[end]]] 

1570 """, 

1571 'good.cog': """\ 

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

1573 //[[[end]]] 

1574 """, 

1575 } 

1576 

1577 makeFiles(d) 

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

1579 oldsyspath = sys.path[:] 

1580 self.newCog() 

1581 self.assertEqual(oldsyspath, sys.path) 

1582 # Is it unchanged for a successful run? 

1583 self.newCog() 

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

1585 self.assertEqual(oldsyspath, sys.path) 

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

1587 self.newCog() 

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

1589 self.assertEqual(oldsyspath, sys.path) 

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

1591 self.newCog() 

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

1593 self.assertEqual(oldsyspath, sys.path) 

1594 # Is it unchanged for a failed run? 

1595 self.newCog() 

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

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

1598 self.assertEqual(oldsyspath, sys.path) 

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

1600 self.newCog() 

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

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

1603 self.assertEqual(oldsyspath, sys.path) 

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

1605 self.newCog() 

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

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

1608 self.assertEqual(oldsyspath, sys.path) 

1609 

1610 def testSubDirectories(self): 

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

1612 

1613 d = { 

1614 'code': { 

1615 'test.cog': """\ 

1616 //[[[cog 

1617 import mysubmodule 

1618 //]]] 

1619 //[[[end]]] 

1620 """, 

1621 

1622 'test.out': """\ 

1623 //[[[cog 

1624 import mysubmodule 

1625 //]]] 

1626 Hello from mysubmodule 

1627 //[[[end]]] 

1628 """, 

1629 

1630 'mysubmodule.py': """\ 

1631 import cog 

1632 cog.outl("Hello from mysubmodule") 

1633 """ 

1634 } 

1635 } 

1636 

1637 makeFiles(d) 

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

1639 # auto-include the current directory 

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

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

1642 

1643 

1644class CogTestsInFiles(TestCaseWithTempDir): 

1645 

1646 def testWarnIfNoCogCode(self): 

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

1648 d = { 

1649 'with.cog': """\ 

1650 //[[[cog 

1651 cog.outl("hello world") 

1652 //]]] 

1653 hello world 

1654 //[[[end]]] 

1655 """, 

1656 

1657 'without.cog': """\ 

1658 There's no cog 

1659 code in this file. 

1660 """, 

1661 } 

1662 

1663 makeFiles(d) 

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

1665 output = self.output.getvalue() 

1666 self.assertNotIn("Warning", output) 

1667 self.newCog() 

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

1669 output = self.output.getvalue() 

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

1671 self.newCog() 

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

1673 output = self.output.getvalue() 

1674 self.assertNotIn("Warning", output) 

1675 

1676 def testFileNameProps(self): 

1677 d = { 

1678 'cog1.txt': """\ 

1679 //[[[cog 

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

1681 //]]] 

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

1683 [[[end]]] 

1684 """, 

1685 

1686 'cog1.out': """\ 

1687 //[[[cog 

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

1689 //]]] 

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

1691 [[[end]]] 

1692 """, 

1693 

1694 'cog1out.out': """\ 

1695 //[[[cog 

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

1697 //]]] 

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

1699 [[[end]]] 

1700 """, 

1701 } 

1702 

1703 makeFiles(d) 

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

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

1706 self.newCog() 

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

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

1709 

1710 def testGlobalsDontCrossFiles(self): 

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

1712 d = { 

1713 'one.cog': """\ 

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

1715 //[[[end]]] 

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

1717 //[[[end]]] 

1718 """, 

1719 

1720 'one.out': """\ 

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

1722 //[[[end]]] 

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

1724 This was set in one.cog 

1725 //[[[end]]] 

1726 """, 

1727 

1728 'two.cog': """\ 

1729 //[[[cog 

1730 try: 

1731 cog.outl(s) 

1732 except NameError: 

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

1734 //]]] 

1735 //[[[end]]] 

1736 """, 

1737 

1738 'two.out': """\ 

1739 //[[[cog 

1740 try: 

1741 cog.outl(s) 

1742 except NameError: 

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

1744 //]]] 

1745 s isn't set! 

1746 //[[[end]]] 

1747 """, 

1748 

1749 'cogfiles.txt': """\ 

1750 # Please run cog 

1751 one.cog 

1752 

1753 two.cog 

1754 """ 

1755 } 

1756 

1757 makeFiles(d) 

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

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

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

1761 output = self.output.getvalue() 

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

1763 

1764 def testRemoveGeneratedOutput(self): 

1765 d = { 

1766 'cog1.txt': """\ 

1767 //[[[cog 

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

1769 //]]] 

1770 This line was generated. 

1771 //[[[end]]] 

1772 This line was not. 

1773 """, 

1774 

1775 'cog1.out': """\ 

1776 //[[[cog 

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

1778 //]]] 

1779 //[[[end]]] 

1780 This line was not. 

1781 """, 

1782 

1783 'cog1.out2': """\ 

1784 //[[[cog 

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

1786 //]]] 

1787 This line was generated. 

1788 //[[[end]]] 

1789 This line was not. 

1790 """, 

1791 } 

1792 

1793 makeFiles(d) 

1794 # Remove generated output. 

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

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

1797 self.newCog() 

1798 # Regenerate the generated output. 

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

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

1801 self.newCog() 

1802 # Remove the generated output again. 

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

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

1805 

1806 def testMsgCall(self): 

1807 infile = """\ 

1808 #[[[cog 

1809 cog.msg("Hello there!") 

1810 #]]] 

1811 #[[[end]]] 

1812 """ 

1813 infile = reindentBlock(infile) 

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

1815 output = self.output.getvalue() 

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

1817 

1818 def testErrorMessageHasNoTraceback(self): 

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

1820 

1821 d = { 

1822 'cog1.txt': """\ 

1823 //[[[cog 

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

1825 cog.outl("generated by cog") 

1826 cog.outl("blah blah.") 

1827 //]]] 

1828 Xhis line was newly 

1829 generated by cog 

1830 blah blah. 

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

1832 """, 

1833 } 

1834 

1835 makeFiles(d) 

1836 stderr = StringIO() 

1837 self.cog.setOutput(stderr=stderr) 

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

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

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

1841 

1842 def testDashD(self): 

1843 d = { 

1844 'test.cog': """\ 

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

1846 --[[[end]]] 

1847 """, 

1848 

1849 'test.kablooey': """\ 

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

1851 Defined fooey as kablooey 

1852 --[[[end]]] 

1853 """, 

1854 

1855 'test.einstein': """\ 

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

1857 Defined fooey as e=mc2 

1858 --[[[end]]] 

1859 """, 

1860 } 

1861 

1862 makeFiles(d) 

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

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

1865 makeFiles(d) 

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

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

1868 makeFiles(d) 

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

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

1871 makeFiles(d) 

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

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

1874 makeFiles(d) 

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

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

1877 makeFiles(d) 

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

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

1880 

1881 def testOutputToStdout(self): 

1882 d = { 

1883 'test.cog': """\ 

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

1885 --[[[end]]] 

1886 """ 

1887 } 

1888 

1889 makeFiles(d) 

1890 stderr = StringIO() 

1891 self.cog.setOutput(stderr=stderr) 

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

1893 output = self.output.getvalue() 

1894 outerr = stderr.getvalue() 

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

1896 self.assertEqual(outerr, "") 

1897 

1898 def testReadFromStdin(self): 

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

1900 def restore_stdin(old_stdin): 

1901 sys.stdin = old_stdin 

1902 self.addCleanup(restore_stdin, sys.stdin) 

1903 sys.stdin = stdin 

1904 

1905 stderr = StringIO() 

1906 self.cog.setOutput(stderr=stderr) 

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

1908 output = self.output.getvalue() 

1909 outerr = stderr.getvalue() 

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

1911 self.assertEqual(outerr, "") 

1912 

1913 def testSuffixOutputLines(self): 

1914 d = { 

1915 'test.cog': """\ 

1916 Hey there. 

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

1918 ;[[[end]]] 

1919 Good bye. 

1920 """, 

1921 

1922 'test.out': """\ 

1923 Hey there. 

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

1925 a (foo) 

1926 b (foo) 

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

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

1929 """ 

1930 c (foo) 

1931 ;[[[end]]] 

1932 Good bye. 

1933 """, 

1934 } 

1935 

1936 makeFiles(d) 

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

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

1939 

1940 def testEmptySuffix(self): 

1941 d = { 

1942 'test.cog': """\ 

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

1944 ;[[[end]]] 

1945 """, 

1946 

1947 'test.out': """\ 

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

1949 a 

1950 b 

1951 c 

1952 ;[[[end]]] 

1953 """, 

1954 } 

1955 

1956 makeFiles(d) 

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

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

1959 

1960 def testHellishSuffix(self): 

1961 d = { 

1962 'test.cog': """\ 

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

1964 """, 

1965 

1966 'test.out': """\ 

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

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

1969 

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

1971 """, 

1972 } 

1973 

1974 makeFiles(d) 

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

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

1977 

1978 def testPrologue(self): 

1979 d = { 

1980 'test.cog': """\ 

1981 Some text. 

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

1983 //[[[end]]] 

1984 epilogue. 

1985 """, 

1986 

1987 'test.out': """\ 

1988 Some text. 

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

1990 1.4142135623 

1991 //[[[end]]] 

1992 epilogue. 

1993 """, 

1994 } 

1995 

1996 makeFiles(d) 

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

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

1999 

2000 def testThreads(self): 

2001 # Test that the implictly imported cog module is actually different for 

2002 # different threads. 

2003 numthreads = 20 

2004 

2005 d = {} 

2006 for i in range(numthreads): 

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

2008 "x\n" * i + 

2009 "[[[cog\n" + 

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

2011 "]]]\n" + 

2012 "[[[end]]]\n" 

2013 ) 

2014 makeFiles(d) 

2015 

2016 results = [] 

2017 

2018 def thread_main(num): 

2019 try: 

2020 ret = Cog().main( 

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

2022 ) 

2023 assert ret == 0 

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

2025 results.append(exc) 

2026 else: 

2027 results.append(None) 

2028 

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

2030 for t in ts: 

2031 t.start() 

2032 for t in ts: 

2033 t.join() 

2034 assert results == [None] * numthreads 

2035 

2036 

2037class WritabilityTests(TestCaseWithTempDir): 

2038 

2039 d = { 

2040 'test.cog': """\ 

2041 //[[[cog 

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

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

2044 //]]] 

2045 //[[[end]]] 

2046 """, 

2047 

2048 'test.out': """\ 

2049 //[[[cog 

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

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

2052 //]]] 

2053 void DoSomething(); 

2054 void DoAnotherThing(); 

2055 void DoLastThing(); 

2056 //[[[end]]] 

2057 """, 

2058 } 

2059 

2060 if os.name == 'nt': #pragma: no cover 

2061 # for Windows 

2062 cmd_w_args = 'attrib -R %s' 

2063 cmd_w_asterisk = 'attrib -R *' 

2064 else: #pragma: no cover 

2065 # for unix-like 

2066 cmd_w_args = 'chmod +w %s' 

2067 cmd_w_asterisk = 'chmod +w *' 

2068 

2069 def setUp(self): 

2070 super(WritabilityTests, self).setUp() 

2071 makeFiles(self.d) 

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

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

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

2075 

2076 def tearDown(self): 

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

2078 super(WritabilityTests, self).tearDown() 

2079 

2080 def testReadonlyNoCommand(self): 

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

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

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

2084 

2085 def testReadonlyWithCommand(self): 

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

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

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

2089 

2090 def testReadonlyWithCommandWithNoSlot(self): 

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

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

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

2094 

2095 def testReadonlyWithIneffectualCommand(self): 

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

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

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

2099 

2100 

2101class ChecksumTests(TestCaseWithTempDir): 

2102 

2103 def testCreateChecksumOutput(self): 

2104 d = { 

2105 'cog1.txt': """\ 

2106 //[[[cog 

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

2108 //]]] 

2109 This line was generated. 

2110 //[[[end]]] 

2111 This line was not. 

2112 """, 

2113 

2114 'cog1.out': """\ 

2115 //[[[cog 

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

2117 //]]] 

2118 This line was generated. 

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

2120 This line was not. 

2121 """, 

2122 } 

2123 

2124 makeFiles(d) 

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

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

2127 

2128 def testCheckChecksumOutput(self): 

2129 d = { 

2130 'cog1.txt': """\ 

2131 //[[[cog 

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

2133 cog.outl("generated by cog") 

2134 cog.outl("blah blah.") 

2135 //]]] 

2136 This line was generated. 

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

2138 """, 

2139 

2140 'cog1.out': """\ 

2141 //[[[cog 

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

2143 cog.outl("generated by cog") 

2144 cog.outl("blah blah.") 

2145 //]]] 

2146 This line was newly 

2147 generated by cog 

2148 blah blah. 

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

2150 """, 

2151 } 

2152 

2153 makeFiles(d) 

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

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

2156 

2157 def testRemoveChecksumOutput(self): 

2158 d = { 

2159 'cog1.txt': """\ 

2160 //[[[cog 

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

2162 cog.outl("generated by cog") 

2163 cog.outl("blah blah.") 

2164 //]]] 

2165 This line was generated. 

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

2167 """, 

2168 

2169 'cog1.out': """\ 

2170 //[[[cog 

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

2172 cog.outl("generated by cog") 

2173 cog.outl("blah blah.") 

2174 //]]] 

2175 This line was newly 

2176 generated by cog 

2177 blah blah. 

2178 //[[[end]]] fooey 

2179 """, 

2180 } 

2181 

2182 makeFiles(d) 

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

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

2185 

2186 def testTamperedChecksumOutput(self): 

2187 d = { 

2188 'cog1.txt': """\ 

2189 //[[[cog 

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

2191 cog.outl("generated by cog") 

2192 cog.outl("blah blah.") 

2193 //]]] 

2194 Xhis line was newly 

2195 generated by cog 

2196 blah blah. 

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

2198 """, 

2199 

2200 'cog2.txt': """\ 

2201 //[[[cog 

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

2203 cog.outl("generated by cog") 

2204 cog.outl("blah blah.") 

2205 //]]] 

2206 This line was newly 

2207 generated by cog 

2208 blah blah! 

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

2210 """, 

2211 

2212 'cog3.txt': """\ 

2213 //[[[cog 

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

2215 cog.outl("generated by cog") 

2216 cog.outl("blah blah.") 

2217 //]]] 

2218 

2219 This line was newly 

2220 generated by cog 

2221 blah blah. 

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

2223 """, 

2224 

2225 'cog4.txt': """\ 

2226 //[[[cog 

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

2228 cog.outl("generated by cog") 

2229 cog.outl("blah blah.") 

2230 //]]] 

2231 This line was newly 

2232 generated by cog 

2233 blah blah.. 

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

2235 """, 

2236 

2237 'cog5.txt': """\ 

2238 //[[[cog 

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

2240 cog.outl("generated by cog") 

2241 cog.outl("blah blah.") 

2242 //]]] 

2243 This line was newly 

2244 generated by cog 

2245 blah blah. 

2246 extra 

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

2248 """, 

2249 

2250 'cog6.txt': """\ 

2251 //[[[cog 

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

2253 cog.outl("generated by cog") 

2254 cog.outl("blah blah.") 

2255 //]]] 

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

2257 """, 

2258 } 

2259 

2260 makeFiles(d) 

2261 with self.assertRaisesRegex(CogError, 

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

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

2264 with self.assertRaisesRegex(CogError, 

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

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

2267 with self.assertRaisesRegex(CogError, 

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

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

2270 with self.assertRaisesRegex(CogError, 

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

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

2273 with self.assertRaisesRegex(CogError, 

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

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

2276 with self.assertRaisesRegex(CogError, 

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

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

2279 

2280 def testArgvIsntModified(self): 

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

2282 orig_argv = argv[:] 

2283 self.cog.callableMain(argv) 

2284 self.assertEqual(argv, orig_argv) 

2285 

2286 

2287class CustomMarkerTests(TestCaseWithTempDir): 

2288 

2289 def testCustomerMarkers(self): 

2290 d = { 

2291 'test.cog': """\ 

2292 //{{ 

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

2294 //}} 

2295 //{{end}} 

2296 """, 

2297 

2298 'test.out': """\ 

2299 //{{ 

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

2301 //}} 

2302 void MyFunction(); 

2303 //{{end}} 

2304 """, 

2305 } 

2306 

2307 makeFiles(d) 

2308 self.cog.callableMain([ 

2309 'argv0', '-r', 

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

2311 'test.cog' 

2312 ]) 

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

2314 

2315 def testTrulyWackyMarkers(self): 

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

2317 d = { 

2318 'test.cog': """\ 

2319 //**( 

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

2321 //**) 

2322 //**(end)** 

2323 """, 

2324 

2325 'test.out': """\ 

2326 //**( 

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

2328 //**) 

2329 void MyFunction(); 

2330 //**(end)** 

2331 """, 

2332 } 

2333 

2334 makeFiles(d) 

2335 self.cog.callableMain([ 

2336 'argv0', '-r', 

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

2338 'test.cog' 

2339 ]) 

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

2341 

2342 def testChangeJustOneMarker(self): 

2343 d = { 

2344 'test.cog': """\ 

2345 //**( 

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

2347 //]]] 

2348 //[[[end]]] 

2349 """, 

2350 

2351 'test.out': """\ 

2352 //**( 

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

2354 //]]] 

2355 void MyFunction(); 

2356 //[[[end]]] 

2357 """, 

2358 } 

2359 

2360 makeFiles(d) 

2361 self.cog.callableMain([ 

2362 'argv0', '-r', 

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

2364 'test.cog' 

2365 ]) 

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

2367 

2368 

2369class BlakeTests(TestCaseWithTempDir): 

2370 

2371 # Blake Winton's contributions. 

2372 def testDeleteCode(self): 

2373 # -o sets the output file. 

2374 d = { 

2375 'test.cog': """\ 

2376 // This is my C++ file. 

2377 //[[[cog 

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

2379 for fn in fnames: 

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

2381 //]]] 

2382 Some Sample Code Here 

2383 //[[[end]]]Data Data 

2384 And Some More 

2385 """, 

2386 

2387 'test.out': """\ 

2388 // This is my C++ file. 

2389 void DoSomething(); 

2390 void DoAnotherThing(); 

2391 void DoLastThing(); 

2392 And Some More 

2393 """, 

2394 } 

2395 

2396 makeFiles(d) 

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

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

2399 

2400 def testDeleteCodeWithDashRFails(self): 

2401 d = { 

2402 'test.cog': """\ 

2403 // This is my C++ file. 

2404 """ 

2405 } 

2406 

2407 makeFiles(d) 

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

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

2410 

2411 def testSettingGlobals(self): 

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

2413 # processFile(). 

2414 d = { 

2415 'test.cog': """\ 

2416 // This is my C++ file. 

2417 //[[[cog 

2418 for fn in fnames: 

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

2420 //]]] 

2421 Some Sample Code Here 

2422 //[[[end]]]""", 

2423 

2424 'test.out': """\ 

2425 // This is my C++ file. 

2426 void DoBlake(); 

2427 void DoWinton(); 

2428 void DoContribution(); 

2429 """, 

2430 } 

2431 

2432 makeFiles(d) 

2433 globals = {} 

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

2435 self.cog.options.bDeleteCode = True 

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

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

2438 

2439 

2440class ErrorCallTests(TestCaseWithTempDir): 

2441 

2442 def testErrorCallHasNoTraceback(self): 

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

2444 d = { 

2445 'error.cog': """\ 

2446 //[[[cog 

2447 cog.error("Something Bad!") 

2448 //]]] 

2449 //[[[end]]] 

2450 """, 

2451 } 

2452 

2453 makeFiles(d) 

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

2455 output = self.output.getvalue() 

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

2457 

2458 def testRealErrorHasTraceback(self): 

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

2460 d = { 

2461 'error.cog': """\ 

2462 //[[[cog 

2463 raise RuntimeError("Hey!") 

2464 //]]] 

2465 //[[[end]]] 

2466 """, 

2467 } 

2468 

2469 makeFiles(d) 

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

2471 output = self.output.getvalue() 

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

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

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

2475 

2476 

2477# Things not yet tested: 

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