#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for titlecase""" import os import sys import tempfile import unittest sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) from titlecase import titlecase, set_small_word_list, create_wordlist_filter_from_file # (executed by `test_input_output` below) TEST_DATA = ( ( "", "" ), ( "word/word", "Word/Word" ), ( "a title and/or string", "A Title and/or String" ), ( "dance with me/let’s face the music and dance", "Dance With Me/Let’s Face the Music and Dance" ), ( "a-b end-to-end two-not-three/three-by-four/five-and", "A-B End-to-End Two-Not-Three/Three-by-Four/Five-And" ), ( "34th 3rd 2nd", "34th 3rd 2nd" ), ( "Q&A with steve jobs: 'that's what happens in technology'", "Q&A With Steve Jobs: 'That's What Happens in Technology'" ), ( "What is AT&T's problem?", "What Is AT&T's Problem?" ), ( "Apple deal with AT&T falls through", "Apple Deal With AT&T Falls Through" ), ( "Words with all consonants like cnn are acronyms", "Words With All Consonants Like CNN Are Acronyms" ), ( "this v that", "This v That" ), ( "this v. that", "This v. That" ), ( "this vs that", "This vs That" ), ( "this vs. that", "This vs. That" ), ( "The SEC's Apple probe: what you need to know", "The SEC's Apple Probe: What You Need to Know" ), ( "'by the Way, small word at the start but within quotes.'", "'By the Way, Small Word at the Start but Within Quotes.'" ), ( "Small word at end is nothing to be afraid of", "Small Word at End Is Nothing to Be Afraid Of" ), ( "Starting Sub-Phrase With a Small Word: a Trick, Perhaps?", "Starting Sub-Phrase With a Small Word: A Trick, Perhaps?" ), ( "Sub-Phrase With a Small Word in Quotes: 'a Trick, Perhaps?'", "Sub-Phrase With a Small Word in Quotes: 'A Trick, Perhaps?'" ), ( 'sub-phrase with a small word in quotes: "a trick, perhaps?"', 'Sub-Phrase With a Small Word in Quotes: "A Trick, Perhaps?"' ), ( "Starting a Hyphen Delimited Sub-Phrase With a Small Word - a Trick, Perhaps?", "Starting a Hyphen Delimited Sub-Phrase With a Small Word - A Trick, Perhaps?" ), ( "Hyphen Delimited Sub-Phrase With a Small Word in Quotes - 'a Trick, Perhaps?'", "Hyphen Delimited Sub-Phrase With a Small Word in Quotes - 'A Trick, Perhaps?'" ), ( 'Hyphen Delimited sub-phrase with a small word in quotes - "a trick, perhaps?"', 'Hyphen Delimited Sub-Phrase With a Small Word in Quotes - "A Trick, Perhaps?"' ), ( "Snakes on a Plane - The TV Edit - The Famous Line", "Snakes on a Plane - The TV Edit - The Famous Line" ), ( "Starting an Em Dash Delimited Sub-Phrase With a Small Word — a Trick, Perhaps?", "Starting an Em Dash Delimited Sub-Phrase With a Small Word — A Trick, Perhaps?" ), ( "Em Dash Delimited Sub-Phrase With a Small Word in Quotes — 'a Trick, Perhaps?'", "Em Dash Delimited Sub-Phrase With a Small Word in Quotes — 'A Trick, Perhaps?'" ), ( 'Em Dash Delimited sub-phrase with a small word in quotes — "a trick, perhaps?"', 'Em Dash Delimited Sub-Phrase With a Small Word in Quotes — "A Trick, Perhaps?"' ), ( "Snakes on a Plane — The TV Edit — The Famous Line", "Snakes on a Plane — The TV Edit — The Famous Line" ), ( "EPISODE 7 — THE FORCE AWAKENS", "Episode 7 — The Force Awakens" ), ( "episode 7 – The force awakens", "Episode 7 – The Force Awakens" ), ( "THE CASE OF X ≤ 7", "The Case of X ≤ 7" ), ( "the case of X ≤ 7", "The Case of X ≤ 7" ), ( '"Nothing to Be Afraid of?"', '"Nothing to Be Afraid Of?"' ), ( '"Nothing to be Afraid Of?"', '"Nothing to Be Afraid Of?"' ), ( 'a thing', 'A Thing' ), ( "2lmc Spool: 'gruber on OmniFocus and vapo(u)rware'", "2lmc Spool: 'Gruber on OmniFocus and Vapo(u)rware'" ), ( 'this is just an example.com', 'This Is Just an example.com' ), ( 'this is something listed on del.icio.us', 'This Is Something Listed on del.icio.us' ), ( 'iTunes should be unmolested', 'iTunes Should Be Unmolested' ), ( 'reading between the lines of steve jobs’s ‘thoughts on music’', 'Reading Between the Lines of Steve Jobs’s ‘Thoughts on Music’' ), ( 'seriously, ‘repair permissions’ is voodoo', 'Seriously, ‘Repair Permissions’ Is Voodoo' ), ( 'generalissimo francisco franco: still dead; kieren McCarthy: still a jackass', 'Generalissimo Francisco Franco: Still Dead; Kieren McCarthy: Still a Jackass' ), ( "O'Reilly should be untouched", "O'Reilly Should Be Untouched" ), ( "my name is o'reilly", "My Name Is O'Reilly" ), ( "WASHINGTON, D.C. SHOULD BE FIXED BUT MIGHT BE A PROBLEM", "Washington, D.C. Should Be Fixed but Might Be a Problem" ), ( "THIS IS ALL CAPS AND SHOULD BE ADDRESSED", "This Is All Caps and Should Be Addressed" ), ( "Mr McTavish went to MacDonalds", "Mr McTavish Went to MacDonalds" ), ( "this shouldn't\nget mangled", "This Shouldn't\nGet Mangled" ), ( "this is http://foo.com", "This Is http://foo.com" ), ( "mac mc MAC MC machine", "Mac Mc MAC MC Machine", ), ( "FOO BAR 5TH ST", "Foo Bar 5th St", ), ( "foo bar 5th st", "Foo Bar 5th St", ), ( "l'grange l'grange l'Grange l'Grange", "l'Grange l'Grange l'Grange l'Grange", ), ( "L'grange L'grange L'Grange L'Grange", "l'Grange l'Grange l'Grange l'Grange", ), ( "l'GranGe", "l'GranGe", ), ( "o'grange O'grange o'Grange O'Grange", "O'Grange O'Grange O'Grange O'Grange", ), ( "o'grange's O'grange's o'Grange's O'Grange's", "O'Grange's O'Grange's O'Grange's O'Grange's", ), ( "O'GranGe", "O'GranGe", ), ( "o'melveny/o'doyle o'Melveny/o'doyle O'melveny/o'doyle o'melveny/o'Doyle o'melveny/O'doyle", "O'Melveny/O'Doyle O'Melveny/O'Doyle O'Melveny/O'Doyle O'Melveny/O'Doyle O'Melveny/O'Doyle", ), # These 'Mc' cases aim to ensure more consistent/predictable behavior. # The examples here are somewhat contrived, and are subject to change # if there is a compelling argument for updating their behavior. # See https://github.com/ppannuto/python-titlecase/issues/64 ( "mccay-mcbut-mcdo mcdonalds/mcby", "McCay-McBut-McDo McDonalds/McBy" ), ( "oblon, spivak, mcclelland, maier & neustadt", "Oblon, Spivak, McClelland, Maier & Neustadt", ), ( "Mcoblon, spivak, mcclelland, mcmaier, & mcneustadt", "McOblon, Spivak, McClelland, McMaier, & McNeustadt", ), ( "mcfoo-bar, MCFOO-BAR, McFoo-bar, McFoo-Bar, mcfoo-mcbar, foo-mcbar", "McFoo-Bar, McFoo-Bar, McFoo-Bar, McFoo-Bar, McFoo-McBar, Foo-McBar", ), ( "'QUOTE' A GREAT", "'Quote' a Great", ), ( "‘QUOTE’ A GREAT", "‘Quote’ a Great", ), ( "“YOUNG AND RESTLESS”", "“Young and Restless”", ), ( "EL NIÑO A ARRIVÉ HIER", "El Niño a Arrivé Hier", ), ( "YEA NO", "Yea No", ), ( "ÝÆ ÑØ", "Ýæ Ñø", ), ( "yea no", "Yea No", ), ( "ýæ ñø", "Ýæ Ñø", ), # https://github.com/ppannuto/python-titlecase/pull/67 ( "Mr mr Mrs Ms Mss Dr dr , Mr. and Mrs. Person", "Mr Mr Mrs Ms MSS Dr Dr , Mr. And Mrs. Person", ), ) class TestStringSuite(unittest.TestCase): """Generated tests from strings""" def test_specific_string(self): for data in TEST_DATA: with self.subTest(): self.assertEqual(titlecase(data[0]), data[1]) class TestInitialsRegex(unittest.TestCase): def test_initials_regex(self): """Test - uppercase initials regex with A.B""" from titlecase import UC_INITIALS #assert bool(UC_INITIALS.match('A.B')) is True self.assertRegex('A.B', UC_INITIALS) def test_initials_regex_2(self): """Test - uppercase initials regex with A.B.""" from titlecase import UC_INITIALS #assert bool(UC_INITIALS.match('A.B.')) is True self.assertRegex('A.B.', UC_INITIALS) def test_initials_regex_3(self): """Test - uppercase initials regex with ABCD""" from titlecase import UC_INITIALS #assert bool(UC_INITIALS.match('ABCD')) is False self.assertNotRegex('ABCD', UC_INITIALS) class TestSymbols(unittest.TestCase): @staticmethod def at_n_t(word, **kwargs): if word.upper() == "AT&T": return word.upper() def test_at_n_t(self): self.assertEqual(titlecase("at&t", callback=TestSymbols.at_n_t), "AT&T") class TestCallback(unittest.TestCase): @staticmethod def abbreviation(word, **kwargs): if word.upper() in ('TCP', 'UDP'): return word.upper() def test_callback(self): s = 'a simple tcp and udp wrapper' # Note: this library is able to guess that all-consonant words are acronyms, so TCP # works naturally, but others will require the custom list self.assertEqual(titlecase(s), 'A Simple TCP and Udp Wrapper') self.assertEqual(titlecase(s, callback=TestCallback.abbreviation), 'A Simple TCP and UDP Wrapper') self.assertEqual(titlecase(s.upper(), callback=TestCallback.abbreviation), 'A Simple TCP and UDP Wrapper') self.assertEqual(titlecase(u'crème brûlée', callback=lambda x, **kw: x.upper()), u'CRÈME BRÛLÉE') # It looks like set_small_word_list uses different regexs that the original # setup code path :/. It really should be the case that one could call # titlecase.set_small_word_list() and reset to the original behavior (it # _really_ should be the case that there aren't all these ugly globals around). # # It seems that `nose` ran every test in isolation, or just in a different # order, so the global state bug wasn't caught before. This should be fixed, # but one thingg at a time. @unittest.skip("FIXME: Converting to unittest exposed a bug") class TestSmallWordList(unittest.TestCase): def test_set_small_word_list(self): self.assertEqual(titlecase('playing the game "words with friends"'), 'Playing the Game "Words With Friends"') set_small_word_list('a|an|the|with') self.assertEqual(titlecase('playing the game "words with friends"'), 'Playing the Game "Words with Friends"') class TestCustomAbbreviations(unittest.TestCase): def setUp(self): # Do not delete on close, instead do manually for Windows (see #86). self.f = tempfile.NamedTemporaryFile(mode='w', delete=False) self.f.write('UDP\nPPPoE\n') self.f.flush() def tearDown(self): self.f.close() # manually close os.unlink(self.f.name) # manually delete def test_technical_acronyms(self): # This works without a wordlist, because it begins mixed case self.assertEqual(titlecase('sending UDP packets over PPPoE works great'), 'Sending UDP Packets Over PPPoE Works Great') # Without a wordlist, this will do the "wrong" thing for the context self.assertEqual(titlecase('SENDING UDP PACKETS OVER PPPOE WORKS GREAT'), 'Sending Udp Packets Over Pppoe Works Great') # A wordlist can provide custom acronyms self.assertEqual(titlecase( 'sending UDP packets over PPPoE works great', callback=create_wordlist_filter_from_file(self.f.name)), 'Sending UDP Packets Over PPPoE Works Great') class TestBlankLines(unittest.TestCase): # Really, it's a bit odd that the default behavior is to delete blank lines, # but that's what it was from day one, so we're kind of stuck with that. # This ensures folks can opt-out of that behavior if they want. def test_one_blank(self): s = 'Line number one\n\nand Line three\n' self.assertEqual(titlecase(s), 'Line Number One\nAnd Line Three\n') self.assertEqual(titlecase(s, preserve_blank_lines=True), 'Line Number One\n\nAnd Line Three\n') def test_complex_blanks(self): s = '\n\nLeading blank\n\n\nMulti-blank\n\n\n\n\nTrailing Blank\n\n' self.assertEqual(titlecase(s), '\nLeading Blank\nMulti-Blank\nTrailing Blank\n') self.assertEqual(titlecase(s, preserve_blank_lines=True), '\n\nLeading Blank\n\n\nMulti-Blank\n\n\n\n\nTrailing Blank\n\n') if __name__ == '__main__': unittest.main()