1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
|
"""
This module tests the code listings used in the documentation.
"""
import sys
from twisted.python.filepath import FilePath
from twisted.trial.unittest import SkipTest, TestCase
from nevow._widget_plugin import ElementRenderingLivePage
from nevow.testutil import renderLivePage, JavaScriptTestCase
from nevow.athena import jsDeps, expose
from nevow import plugins
class ExampleTestBase(object):
"""
This is a mixin which adds an example to the path, tests it, and then
removes it from the path and unimports the modules which the test loaded.
Test cases which test example code and documentation listings should use
this.
This is done this way so that examples can live in isolated path entries,
next to the documentation, replete with their own plugin packages and
whatever other metadata they need. Also, example code is a rare instance
of it being valid to have multiple versions of the same code in the
repository at once, rather than relying on version control, because
documentation will often show the progression of a single piece of code as
features are added to it, and we want to test each one.
"""
examplePath = None
def setUp(self):
"""
Add our example directory to the path and record which modules are
currently loaded.
"""
self.originalPath = sys.path[:]
self.originalModules = sys.modules.copy()
here = FilePath(__file__).parent().parent().parent()
for childName in self.examplePath:
here = here.child(childName)
if not here.exists():
raise SkipTest("Examples (%s) not found - cannot test" % (here.path,))
sys.path.append(here.path)
# This stuff is pretty horrible; we need to turn off JS caching for the
# duration of this test (this flag will be set back to True by the
# plugin-loading itself, so no need to clean it up) because the
# previously-loaded list of plugins is going to be invalid.
jsDeps._loadPlugins = True
# Even more horrible! nevow.plugins.__path__ needs to be recomputed
# each time for the new value of sys.path.
reload(plugins)
def tearDown(self):
"""
Remove the example directory from the path and remove all modules loaded by
the test from sys.modules.
"""
sys.modules.clear()
sys.modules.update(self.originalModules)
sys.path[:] = self.originalPath
reload(plugins)
class ExampleJavaScriptTestCase(JavaScriptTestCase):
"""
Since JavaScriptTestCase does not use setUp and tearDown, we can't use
simple inheritance to invoke the functionality in ExampleTestBase; so,
instead, this class composes JavaScriptTestCase with a ExampleTestBase.
"""
def run(self, result):
"""
Wrap L{JavaScriptTestCase.run} to change and restore the plugin environment
on each test run.
"""
base = ExampleTestBase()
base.examplePath = self.examplePath
try:
base.setUp()
except SkipTest, e:
result.startTest(self)
result.addSkip(self, str(e))
result.stopTest(self)
else:
try:
return JavaScriptTestCase.run(self, result)
finally:
base.tearDown()
def chatListing(partname):
"""
Return a list of strings that represents a path from the root of the Nevow
source package which contains a potential PYTHONPATH entry with some
listing code in it, based on which part of the chat tutorial it came from.
"""
return ['doc', 'howto', 'chattutorial', 'part'+partname, 'listings']
class Part00Tests(ExampleJavaScriptTestCase):
"""
Refer to JavaScript unit tests for section 01 of the tutorial.
"""
examplePath = chatListing('00')
def test_part00(self):
"""
Test method pointing to part 00 of the tutorial.
"""
return 'Nevow.Test.TestHowtoListing00'
class Echo00(ExampleTestBase, TestCase):
"""
These tests will make sure that the very basic 'echobox' portion of the
tutorial works.
"""
examplePath = chatListing('00')
def test_renderEcho(self):
"""
Rendering the echo example element should produce a very simple text area.
"""
from echothing.echobox import EchoElement
TEXT = 'Echo Element'
eb = EchoElement()
erlp = ElementRenderingLivePage(eb)
def checkContent(result):
# The "liveElement" renderer inserts this, let's look for it to
# make sure it rendered live:
self.assertIn('id="athena:'+str(eb._athenaID)+'"', result)
self.assertIn('athena:class="EchoThing.EchoWidget"', result)
self.assertIn(TEXT, result)
return renderLivePage(erlp).addCallback(checkContent)
def test_echoTellsClient(self):
"""
When 'hear' is called on a ChatterElement, it should inform its client of what
was said and by whom.
"""
from echothing.echobox import EchoElement
eb = EchoElement()
echoed = []
eb.callRemote = lambda method, message: echoed.append((method, message))
eb.say(u'HELLO... Hello... hello...')
self.assertEquals(echoed, [('addText', u'HELLO... Hello... hello...')])
class Part01Tests(ExampleJavaScriptTestCase):
"""
Refer to JavaScript unit tests for section 01 of the tutorial.
"""
examplePath = chatListing('01')
def test_part01(self):
"""
Test method pointing to part 01 of the tutorial.
"""
return 'Nevow.Test.TestHowtoListing01'
class RenderAndChat01(ExampleTestBase, TestCase):
"""
These tests make sure that the listing for part 01 of the chatterbox
tutorial will import, render, and allow users to chat with each other.
"""
examplePath = chatListing('01')
def test_basicRendering(self):
"""
Rendering the example element should produce a chat area.
"""
from chatthing.chatterbox import ChatterElement, ChatRoom
PROMPT = 'Choose your username: '
cb = ChatterElement(ChatRoom())
erlp = ElementRenderingLivePage(cb)
def checkContent(result):
# The "liveElement" renderer inserts this, let's look for it to
# make sure it rendered live:
self.assertIn('id="athena:'+str(cb._athenaID)+'"', result)
self.assertIn('athena:class="ChatThing.ChatterWidget"', result)
self.assertIn(PROMPT, result)
return renderLivePage(erlp).addCallback(checkContent)
def test_username(self):
"""
When a user sets their username in the chat room, it should set an
attribute on their ChatterElement instance.
"""
from chatthing.chatterbox import ChatterElement, ChatRoom
cb = ChatterElement(ChatRoom())
setUsername = expose.get(cb, 'setUsername')
setUsername(u'jethro')
self.assertIdentical(u'jethro', cb.username)
def test_loginThenWall(self):
"""
When a user logs in the 'wall' method on their ChatterElement gets called,
notifiying everyone in the room that they have entered.
"""
from chatthing.chatterbox import ChatRoom
jethroHeard = []
cletusHeard = []
cr = ChatRoom()
user1 = cr.makeChatter()
user1.wall = lambda msg: jethroHeard.append(msg)
user1.setUsername(u'jethro')
user2 = cr.makeChatter()
user2.wall = lambda msg: cletusHeard.append(msg)
user2.setUsername(u'cletus')
self.assertEquals(jethroHeard,
[u' * user jethro has joined the room',
u' * user cletus has joined the room'])
self.assertEquals(cletusHeard, [u' * user cletus has joined the room'])
def test_sayThenHear(self):
"""
When a user calls the 'say' method on their ChatterElement, everyone (including
them) should 'hear' it.
"""
from chatthing.chatterbox import ChatRoom
cr = ChatRoom()
user1 = cr.makeChatter()
user1.wall = lambda msg: msg
user1.setUsername(u'jethro')
user2 = cr.makeChatter()
user2.wall = lambda msg: msg
user2.setUsername(u'cletus')
jethroHeard = []
cletusHeard = []
user1.hear = lambda who, what: jethroHeard.append((who,what))
user2.hear = lambda who, what: cletusHeard.append((who,what))
say = expose.get(user1, 'say')
say(u'Hey, Cletus!')
self.assertEquals(jethroHeard, cletusHeard)
self.assertEquals(cletusHeard, [(u'jethro', u'Hey, Cletus!')])
def test_wallTellsClient(self):
"""
When 'wall' is called on a ChatterElement, it should inform its client of
the message.
"""
from chatthing.chatterbox import ChatRoom
cb = ChatRoom().makeChatter()
heard = []
cb.callRemote = lambda method, msg: heard.append((method, msg))
cb.wall(u'Message for everyone...')
self.assertEquals(heard, [('displayMessage', u'Message for everyone...')])
def test_hearTellsClient(self):
"""
When 'hear' is called on a ChatterElement, it should inform its client of what
was said and by whom.
"""
from chatthing.chatterbox import ChatRoom
cb = ChatRoom().makeChatter()
heard = []
cb.callRemote = lambda method, who, what: heard.append((method, who, what))
cb.hear(u'Hello', u'Chat')
self.assertEquals(heard, [('displayUserMessage', u'Hello', u'Chat')])
|