Skip to content

Commit be2efd1

Browse files
committed
New tests using the unittest framework
1 parent 6677f62 commit be2efd1

9 files changed

Lines changed: 274 additions & 234 deletions

File tree

‎test/base.py‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from unittest import TestCase, main
2+
import tempfile
3+
from csp.csp import process
4+
5+
class BaseCspTest(TestCase):
6+
"""Provides an output mechanism for asserting the output of
7+
parallel running processes or threads. Python's builtin datatypes
8+
will be copied for each process. Instead we use a file here."""
9+
10+
def setUp(self):
11+
self.outpipe = tempfile.TemporaryFile(mode='wt+')
12+
13+
def proc(self, x):
14+
def tester():
15+
self.outpipe.write(str(x))
16+
self.outpipe.flush()
17+
return process(tester)()
18+
19+
def source(self, data, channel_out):
20+
def tester(cout):
21+
for x in data:
22+
cout.write(x)
23+
return process(tester)(channel_out)
24+
25+
def sink(self, length, channel_in):
26+
def tester(length, cin):
27+
for ii in range(length):
28+
x = cin.read()
29+
self.outpipe.write(str(x))
30+
self.outpipe.flush()
31+
return process(tester)(length, channel_in)
32+
33+
def output(self):
34+
self.outpipe.seek(0)
35+
return self.outpipe.read()
36+

‎test/csptest_channel.py‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from base import BaseCspTest, main
2+
from csp.csp import Channel, Par
3+
4+
class TestChannels(BaseCspTest):
5+
6+
def test_single_source_sink_channel(self):
7+
chan = Channel()
8+
self.source('012345', chan) // (self.sink(6, chan), )
9+
self.assertEquals(self.output(), '012345')
10+
11+
def test_multiple_source_sink_channel_triples(self):
12+
chans = [Channel() for x in range(5)]
13+
srces = Par(*[self.source(str(ii), ch) for ii, ch in enumerate(chans)])
14+
sinks = Par(*[self.sink(1, ch) for ch in chans])
15+
Par(srces, sinks).start()
16+
self.assertEquals(''.join(sorted(self.output())), '01234')
17+
18+
def test_multiple_source_sink_tuples_one_channel(self):
19+
channel = Channel()
20+
srces = Par(*[self.source(str(ii), channel) for ii in range(5)])
21+
sinks = Par(*[self.sink(1, channel) for ii in range(5)])
22+
Par(srces, sinks).start()
23+
self.assertEquals(''.join(sorted(self.output())), '01234')
24+
25+
26+
class TestPoison(BaseCspTest):
27+
28+
def test_poison_aborts_channel(self):
29+
channel = Channel()
30+
channel.poison()
31+
Par(self.source('012345', channel),
32+
self.sink(6, channel)).start()
33+
self.assertEquals(self.output(), '')
34+
35+
def test_poison_does_not_abort_other_channels(self):
36+
poisoned_channel = Channel()
37+
clean_channel = Channel()
38+
poisoned_channel.poison()
39+
Par(self.source('012345', clean_channel),
40+
self.sink(6, clean_channel)).start()
41+
self.assertEquals(self.output(), '012345')
42+
43+
44+
class TestOverread(BaseCspTest):
45+
46+
def do_not_test_overread(self): # as it will hang
47+
channel = Channel()
48+
self.source('012345', channel) // (self.sink(12, channel), )
49+
self.assertEquals(self.output(), '012345')
50+
51+
52+
if __name__ == '__main__':
53+
main()

‎test/csptest_par.py‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from base import BaseCspTest, main
2+
from csp.csp import Par
3+
4+
class TestPar(BaseCspTest):
5+
6+
def test_process_object(self):
7+
Par(*[self.proc(x) for x in range(5)]).start()
8+
self.assertEqual(''.join(sorted(self.output())), '01234')
9+
10+
def test_supports_nesting(self):
11+
_ = self.proc
12+
Par(_(0), Par(_(1), _(2)), Par(_(3), Par(_(4), _(5)))).start()
13+
self.assertEqual(''.join(sorted(self.output())), '012345')
14+
15+
16+
class TestMixinFloordivOperator(BaseCspTest):
17+
18+
def test_floordiv_runs_parallel(self):
19+
_ = self.proc
20+
_(0) // (_(1), _(2), _(3), _(4))
21+
self.assertEqual(''.join(sorted(self.output())), '01234')
22+
23+
def does_not_work_with_non_tuple(self):
24+
# Why not?
25+
_ = self.proc
26+
_(0) // _(1)
27+
self.assertEqual(''.join(sorted(self.output())), '01')
28+
29+
30+
class TestMixParObjectAndFloordivOperator(BaseCspTest):
31+
32+
def test_mixed_syntax(self):
33+
_ = self.proc
34+
_(0) // (Par(_(1), _(2)), _(3))
35+
self.assertEqual(''.join(sorted(self.output())), '0123')
36+
37+
38+
if __name__ == '__main__':
39+
main()

‎test/csptest_process_decorator.py‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from unittest import TestCase, main
2+
from csp.csp import process, Channel, Par
3+
import tempfile
4+
5+
@process
6+
def sender(message, channel):
7+
channel.write(message)
8+
9+
@process
10+
def receiver(channel, temp_file):
11+
temp_file.write(channel.read())
12+
temp_file.flush()
13+
14+
15+
class TestUnbound(TestCase):
16+
17+
def test_works(self):
18+
channel = Channel()
19+
temp_file = tempfile.TemporaryFile()
20+
Par(sender('123', channel), receiver(channel, temp_file)).start()
21+
temp_file.seek(0)
22+
self.assertEqual(temp_file.read(), '123')
23+
24+
25+
class TestBound(TestCase):
26+
27+
def setUp(self):
28+
self.channel = Channel()
29+
30+
@process
31+
def sender(self, message):
32+
self.channel.write(message)
33+
34+
@process
35+
def receiver(self):
36+
self.result = self.channel.read()
37+
38+
def test_workd(self):
39+
Par(self.sender('123'), self.receiver()).start()
40+
self.assertEqual(self.result, '123')
41+
42+
43+
if __name__ == '__main__':
44+
main()

‎test/csptest_seq.py‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from base import BaseCspTest, main
2+
from csp.csp import Seq
3+
4+
class TestSeq(BaseCspTest):
5+
6+
def test_process_object(self):
7+
Seq(*[self.proc(x) for x in range(5)]).start()
8+
self.assertEqual(self.output(), '01234')
9+
10+
def test_supports_nesting(self):
11+
_ = self.proc
12+
Seq(_(0), Seq(_(1), _(2)), Seq(_(3), Seq(_(4), _(5)))).start()
13+
self.assertEqual(self.output(), '012345')
14+
15+
16+
class TestMixinGtOperator(BaseCspTest):
17+
18+
def test_greater_operator_starts_sequence(self):
19+
_ = self.proc
20+
_(0) > _(1) > _(2) > _(3) > _(4)
21+
self.assertEqual(self.output(), '01234')
22+
23+
24+
def does_not_supports_nesting(self):
25+
# As the resulting Seq object is immediately started, the syntax
26+
# does not make sense, when nested
27+
_ = self.proc
28+
_(0) > (_(1) > _(2)) > (_(3) > (_(4) > _(5)))
29+
self.assertEqual(self.output(), '012345')
30+
31+
32+
class TestMixSeqObjectAndGtOperator(BaseCspTest):
33+
34+
def test_mixed_syntax(self):
35+
_ = self.proc
36+
_(0) > Seq(_(1), _(2)) > _(3)
37+
self.assertEqual(self.output(), '0123')
38+
39+
40+
if __name__ == '__main__':
41+
main()

‎test/test.py‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python
2+
"""
3+
This is mainly only a test finder, but adds the feature of running
4+
tests that start with "csptest_" twice for the os_thread and os_process
5+
backend.
6+
"""
7+
8+
import sys
9+
import os
10+
import unittest
11+
import re
12+
13+
os.chdir(os.path.dirname(__file__))
14+
sys.path.insert(0, '..')
15+
test_suite_re = re.compile('((csp)?test_(.*))\.py$')
16+
17+
BACKENDS = []
18+
try:
19+
import csp.os_thread
20+
BACKENDS.append(sys.modules['csp.os_thread'])
21+
except: pass
22+
#try:
23+
# import csp.os_process
24+
# BACKENDS.append(sys.modules['csp.os_process'])
25+
#except: pass
26+
import csp.csp
27+
28+
if len(BACKENDS) < 2:
29+
from warnings import warn
30+
warn('This platform supports only backend {0}, '
31+
'the other one is thus UNTESTED!'.format(csp.csp.CSP_IMPLEMENTATION))
32+
33+
34+
runner = unittest.TextTestRunner(verbosity=1)
35+
def load_and_run(test_module):
36+
sys.stdout.flush()
37+
suite = unittest.defaultTestLoader.loadTestsFromName(test_module)
38+
return runner.run(suite).wasSuccessful()
39+
40+
for testcase in [os.path.basename(x) for x in sys.argv[1:]] or os.listdir('.'):
41+
assert os.path.exists(testcase)
42+
testname = test_suite_re.match(os.path.basename(testcase))
43+
if testname is None:
44+
continue
45+
test_module, csptest, titlename = testname.groups()
46+
title = 'Testing ' + titlename
47+
if csptest:
48+
for backend in BACKENDS:
49+
if test_module in sys.modules:
50+
del sys.modules[test_module]
51+
sys.modules['csp.csp'] = backend
52+
sys.stdout.write('{0} ({1}): '.format(title,
53+
backend.CSP_IMPLEMENTATION))
54+
if not load_and_run(test_module):
55+
sys.exit(2)
56+
else:
57+
sys.stdout.write(title + ': ')
58+
if not load_and_run(test_module):
59+
sys.exit(2)
60+
print

‎test/test_forever.py‎

Lines changed: 0 additions & 62 deletions
This file was deleted.

0 commit comments

Comments
 (0)