#!/usr/bin/python # -*- coding: utf-8 -*- import snudown import unittest import itertools import cStringIO as StringIO cases = { '': '', 'http://www.reddit.com': '

http://www.reddit.com

\n', 'http://www.reddit.com/a\x00b': '

http://www.reddit.com/ab

\n', 'foo@example.com': '

foo@example.com

\n', '[foo](http://en.wikipedia.org/wiki/Link_(film\))': '

foo

\n', '(http://tsfr.org)': '

(http://tsfr.org)

\n', '[A link with a /r/subreddit in it](/lol)': '

A link with a /r/subreddit in it

\n', '[A link with a http://www.url.com in it](/lol)': '

A link with a http://www.url.com in it

\n', '[Empty Link]()': '

[Empty Link]()

\n', 'http://en.wikipedia.org/wiki/café_racer': '

http://en.wikipedia.org/wiki/café_racer

\n', '#####################################################hi': '
###############################################hi
\n', '[foo](http://bar\nbar)': '

foo

\n', '/r/test': '

/r/test

\n', 'Words words /r/test words': '

Words words /r/test words

\n', '/r/': '

/r/

\n', r'escaped \/r/test': '

escaped /r/test

\n', 'ampersands http://www.google.com?test&blah': '

ampersands http://www.google.com?test&blah

\n', '[_regular_ link with nesting](/test)': '

regular link with nesting

\n', ' www.a.co?with&test': '

www.a.co?with&test

\n', r'Normal^superscript': '

Normalsuperscript

\n', r'Escape\^superscript': '

Escape^superscript

\n', r'~~normal strikethrough~~': '

normal strikethrough

\n', r'\~~escaped strikethrough~~': '

~~escaped strikethrough~~

\n', 'anywhere\x03, you': '

anywhere, you

\n', '[Test](//test)': '

Test

\n', '[Test](//#test)': '

Test

\n', '[Test](#test)': '

Test

\n', '[Test](git://github.com)': '

Test

\n', '[Speculation](//?)': '

Speculation

\n', '/r/sr_with_underscores': '

/r/sr_with_underscores

\n', '[Test](///#test)': '

Test

\n', '/r/multireddit+test+yay': '

/r/multireddit+test+yay

\n', '': '

<test>

\n', 'words_with_underscores': '

words_with_underscores

\n', 'words*with*asterisks': '

wordswithasterisks

\n', '~test': '

~test

\n', '/u/test': '

/u/test

\n', '/u/test/m/test test': '

/u/test/m/test test

\n', '/U/nope': '

/U/nope

\n', '/r/test/m/test test': '

/r/test/m/test test

\n', '/r/test/w/test test': '

/r/test/w/test test

\n', '/r/test/comments/test test': '

/r/test/comments/test test

\n', '/u/test/commentscommentscommentscommentscommentscommentscomments/test test': '

/u/test/commentscommentscommentscommentscommentscommentscomments/test test

\n', 'a /u/reddit': '

a /u/reddit

\n', 'u/reddit': '

u/reddit

\n', 'a u/reddit': '

a u/reddit

\n', 'a u/reddit/foobaz': '

a u/reddit/foobaz

\n', 'foo:u/reddit': '

foo:u/reddit

\n', 'fuu/reddit': '

fuu/reddit

\n', # Don't treat unicode punctuation as a word boundary for now u'a。u/reddit'.encode('utf8'): u'

a。u/reddit

\n'.encode('utf8'), '\\/u/me': '

/u/me

\n', '\\\\/u/me': '

\\/u/me

\n', '\\u/me': '

\\u/me

\n', '\\\\u/me': '

\\u/me

\n', 'u\\/me': '

u/me

\n', '*u/me*': '

u/me

\n', 'foo^u/me': '

foou/me

\n', '*foo*u/me': '

foou/me

\n', 'u/me': '

u/me

\n', '/u/me': '

/u/me

\n', 'u/m': '

u/m

\n', '/u/m': '

/u/m

\n', '/f/oobar': '

/f/oobar

\n', 'f/oobar': '

f/oobar

\n', '/r/test/commentscommentscommentscommentscommentscommentscomments/test test': '

/r/test/commentscommentscommentscommentscommentscommentscomments/test test

\n', 'blah \\': '

blah \\

\n', '/r/whatever: fork': '

/r/whatever: fork

\n', '/r/t:timereddit': '

/r/t:timereddit

\n', '/r/reddit.com': '

/r/reddit.com

\n', '/r/not.cool': '

/r/not.cool

\n', '/r/very+clever+multireddit+reddit.com+t:fork+yay': '

/r/very+clever+multireddit+reddit.com+t:fork+yay

\n', '/r/t:heatdeathoftheuniverse': '

/r/t:heatdeathoftheuniverse

\n', '/r/all-minus-something': '

/r/all-minus-something

\n', '/r/notall-minus': '

/r/notall-minus

\n', 'a /r/reddit.com': '

a /r/reddit.com

\n', 'a r/reddit.com': '

a r/reddit.com

\n', 'foo:r/reddit.com': '

foo:r/reddit.com

\n', 'foobar/reddit.com': '

foobar/reddit.com

\n', u'a。r/reddit.com'.encode('utf8'): u'

a。r/reddit.com

\n'.encode('utf8'), '/R/reddit.com': '

/R/reddit.com

\n', '/r/irc://foo.bar/': '

/r/irc://foo.bar/

\n', '/r/t:irc//foo.bar/': '

/r/t:irc//foo.bar/

\n', '/r/all-irc://foo.bar/': '

/r/all-irc://foo.bar/

\n', '/r/foo+irc://foo.bar/': '

/r/foo+irc://foo.bar/

\n', '/r/www.example.com': '

/r/www.example.com

\n', '.http://reddit.com': '

.http://reddit.com

\n', '[r://](/aa)': '

r://http://reddit.com/

\n', '/u/http://www.reddit.com/user/reddit': '

/u/http://www.reddit.com/user/reddit

\n', 'www.http://example.com/': '

www.http://example.com/

\n', ('|' * 5) + '\n' + ('-|' * 5) + '\n|\n': '\n\n' + ('\n' * 4) + '\n\n\n\n\n
\n', ('|' * 2) + '\n' + ('-|' * 2) + '\n|\n': '\n\n' + ('\n' * 1) + '\n\n\n\n\n
\n', ('|' * 65) + '\n' + ('-|' * 65) + '\n|\n': '\n\n' + ('\n' * 64) + '\n\n\n\n\n
\n', ('|' * 66) + '\n' + ('-|' * 66) + '\n|\n': '

' + ('|' * 66) + '\n' + ('-|' * 66) + '\n|' + '

\n', 'ϑ': '

ϑ

\n', '&foobar;': '

&foobar;

\n', ' ': '

&nbsp

\n', '&#foobar;': '

&#foobar;

\n', 'oobar;': '

&#xfoobar;

\n', '�': '

&#9999999999;

\n', 'c': '

c

\n', '~': '

~

\n', '~': '

~

\n', '½': '

½

\n', 'aaa½aaa': '

aaa½aaa

\n', '&': '

&

\n', '&;': '

&;

\n', '&#;': '

&#;

\n', '&#;': '

&#;

\n', '&#x;': '

&#x;

\n', } # Test that every numeric entity is encoded as # it should be. ILLEGAL_NUMERIC_ENTS = frozenset(itertools.chain( xrange(0, 9), xrange(11, 13), xrange(14, 32), xrange(55296, 57344), xrange(65534, 65536), )) ent_test_key = '' ent_test_val = '' for i in xrange(65550): ent_testcase = '&#%d;&#x%x;' % (i, i) ent_test_key += ent_testcase if i in ILLEGAL_NUMERIC_ENTS: ent_test_val += ent_testcase.replace('&', '&') else: ent_test_val += ent_testcase cases[ent_test_key] = '

%s

\n' % ent_test_val wiki_cases = { '': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', '
': '

\n', } class SnudownTestCase(unittest.TestCase): def __init__(self, renderer=snudown.RENDERER_USERTEXT): self.renderer = renderer unittest.TestCase.__init__(self) def runTest(self): output = snudown.markdown(self.input, renderer=self.renderer) for i, (a, b) in enumerate(zip(repr(self.expected_output), repr(output))): if a != b: io = StringIO.StringIO() print >> io, "TEST FAILED:" print >> io, " input: %s" % repr(self.input) print >> io, " expected: %s" % repr(self.expected_output) print >> io, " actual: %s" % repr(output) print >> io, " %s" % (' ' * i + '^') self.fail(io.getvalue()) def test_snudown(): suite = unittest.TestSuite() for input, expected_output in wiki_cases.iteritems(): case = SnudownTestCase(renderer=snudown.RENDERER_WIKI) case.input = input case.expected_output = expected_output suite.addTest(case) for input, expected_output in cases.iteritems(): case = SnudownTestCase() case.input = input case.expected_output = expected_output suite.addTest(case) return suite