cctool

A tool for managing contacts and calendars.
git clone https://git.ce9e.org/cctool.git

commit
8379fca760e9d3a1b53976b59541580f2df27b5d
parent
5e1ad6dd5e2c5fd9e76c4ee8fb5b90e618d14cc0
Author
Tobias Bengfort <tobias.bengfort@gmx.net>
Date
2015-05-08 21:47
add more tests

Diffstat

M cctool.py 42 +++++++++++++++++++++---------------------
M tests.py 266 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-

2 files changed, 283 insertions, 25 deletions


diff --git a/cctool.py b/cctool.py

@@ -49,19 +49,19 @@ import pickle
   49    49 import re
   50    50 import sys
   51    51 
   52    -1 try:
   -1    52 try:  # pragma: nocover
   53    53 	from ConfigParser import RawConfigParser as ConfigParser
   54    -1 except ImportError:
   -1    54 except ImportError:  # pragma: nocover
   55    55 	from configparser import RawConfigParser as ConfigParser
   56    56 
   57    -1 try:
   -1    57 try:  # pragma: nocover
   58    58 	import icalendar
   59    -1 except ImportError as err:
   -1    59 except ImportError as err:  # pragma: nocover
   60    60 	icalendar = err
   61    61 
   62    -1 try:
   -1    62 try:  # pragma: nocover
   63    63 	import yaml
   64    -1 except ImportError as err:
   -1    64 except ImportError as err:  # pragma: nocover
   65    65 	yaml = err
   66    66 
   67    67 
@@ -70,14 +70,14 @@ PERSON = ['abook', 'ldif']
   70    70 EVENT = ['bsdcal', 'ics']
   71    71 
   72    72 
   73    -1 def _str(x):
   -1    73 def _str(x):  # pragma: nocover
   74    74 	try:
   75    75 		return unicode(x)
   76    76 	except NameError:
   77    77 		return str(x)
   78    78 
   79    79 
   80    -1 def formats():
   -1    80 def formats():  # pragma: nocover
   81    81 	informats = {
   82    82 		'bsdcal': BSDCal,
   83    83 		'abook': ABook,
@@ -91,10 +91,10 @@ def formats():
   91    91 		'json': JSON,
   92    92 		'pickle': Pickle,
   93    93 	}
   94    -1 	if not isinstance(icalendar, Exception):
   -1    94 	if not isinstance(icalendar, Exception):  # pragma: nocover
   95    95 		informats['ics'] = ICal
   96    96 		outformats['ics'] = ICal
   97    -1 	if not isinstance(yaml, Exception):
   -1    97 	if not isinstance(yaml, Exception):  # pragma: nocover
   98    98 		informats['yml'] = YAML
   99    99 		outformats['yml'] = YAML
  100   100 	return informats, outformats
@@ -194,7 +194,7 @@ class Format(object):
  194   194 	"""
  195   195 
  196   196 	@classmethod
  197    -1 	def load(cls, fh):
   -1   197 	def load(cls, fh):  # pragma: nocover
  198   198 		raise NotImplementedError
  199   199 
  200   200 	@classmethod
@@ -202,7 +202,7 @@ class Format(object):
  202   202 		return cls.load(BytesIO(s))
  203   203 
  204   204 	@classmethod
  205    -1 	def dump(cls, data, fh):
   -1   205 	def dump(cls, data, fh):  # pragma: nocover
  206   206 		raise NotImplementedError
  207   207 
  208   208 	@classmethod
@@ -281,15 +281,13 @@ class ICal(Format):
  281   281 
  282   282 	@classmethod
  283   283 	def load(cls, fh):
  284    -1 		if isinstance(icalendar, Exception):
   -1   284 		if isinstance(icalendar, Exception):  # pragma: nocover
  285   285 			raise icalendar
  286   286 
  287   287 		calendar = icalendar.Calendar.from_ical(fh.read())
  288   288 
  289   289 		for event in cls._iter_events(calendar):
  290   290 			d = MultiDict()
  291    -1 			if 'RRULE' in event:
  292    -1 				d['freq'] = [s.lower() for s in event['RRULE']['FREQ']]
  293   291 			for key, value in event.items():
  294   292 				if key.lower() in cls.fields:
  295   293 					try:
@@ -299,11 +297,13 @@ class ICal(Format):
  299   297 					except ValueError:
  300   298 						break
  301   299 			else:
   -1   300 				if 'RRULE' in event:
   -1   301 					d['freq'] = [s.lower() for s in event['RRULE']['FREQ']]
  302   302 				yield map_keys(d, cls.fields)
  303   303 
  304   304 	@classmethod
  305   305 	def dump(cls, data, fh):
  306    -1 		if isinstance(icalendar, Exception):
   -1   306 		if isinstance(icalendar, Exception):  # pragma: nocover
  307   307 			raise icalendar
  308   308 
  309   309 		calendar = icalendar.Calendar()
@@ -353,10 +353,10 @@ class ABook(Format):
  353   353 	def dump(cls, data, fh):
  354   354 		cp = ConfigParser()
  355   355 
  356    -1 		if isinstance(cp.__module__, bytes):
   -1   356 		if isinstance(cp.__module__, bytes):  # pragma: nocover
  357   357 			_fh = fh
  358   358 			encode = lambda x: x.encode('utf8')
  359    -1 		else:
   -1   359 		else:  # pragma: nocover
  360   360 			_fh = codecs.getwriter('utf8')(fh)
  361   361 			encode = lambda x: x
  362   362 
@@ -441,14 +441,14 @@ class JSON(Format):
  441   441 class YAML(Format):
  442   442 	@classmethod
  443   443 	def load(cls, fh):
  444    -1 		if isinstance(yaml, Exception):
   -1   444 		if isinstance(yaml, Exception):  # pragma: nocover
  445   445 			raise yaml
  446   446 
  447   447 		return [MultiDict(d) for d in yaml.load(fh.read())]
  448   448 
  449   449 	@classmethod
  450   450 	def dump(cls, data, fh):
  451    -1 		if isinstance(yaml, Exception):
   -1   451 		if isinstance(yaml, Exception):  # pragma: nocover
  452   452 			raise yaml
  453   453 
  454   454 		_fh = codecs.getwriter('utf8')(fh)
@@ -511,7 +511,7 @@ def get_informat(filename):
  511   511 	sys.exit(1)
  512   512 
  513   513 
  514    -1 def main():
   -1   514 def main():  # pragma: nocover
  515   515 	informats, outformats = formats()
  516   516 	args = parse_args()
  517   517 

diff --git a/tests.py b/tests.py

@@ -2,6 +2,7 @@ from __future__ import unicode_literals
    2     2 
    3     3 import unittest
    4     4 from datetime import datetime
   -1     5 from io import BytesIO
    5     6 
    6     7 import cctool
    7     8 
@@ -12,6 +13,26 @@ class TestMultiDict(unittest.TestCase):
   12    13 	def setUp(self):
   13    14 		self.d = cctool.MultiDict()
   14    15 
   -1    16 	def test_construct_list(self):
   -1    17 		d = cctool.MultiDict([
   -1    18 			('foo', [1, 2]),
   -1    19 			('bar', [3, 4]),
   -1    20 		])
   -1    21 
   -1    22 		self.assertEqual(list(d.keys()), ['foo', 'bar'])
   -1    23 		self.assertEqual(d['foo'], [1, 2])
   -1    24 		self.assertEqual(d['bar'], [3, 4])
   -1    25 
   -1    26 	def test_construct_dict(self):
   -1    27 		d = cctool.MultiDict({
   -1    28 			'foo': [1, 2],
   -1    29 			'bar': [3, 4],
   -1    30 		})
   -1    31 
   -1    32 		self.assertEqual(set(d.keys()), set(['foo', 'bar']))
   -1    33 		self.assertEqual(d['foo'], [1, 2])
   -1    34 		self.assertEqual(d['bar'], [3, 4])
   -1    35 
   15    36 	def test_containes(self):
   16    37 		self.assertFalse('foo' in self.d)
   17    38 		self.d['foo'] = [1, 2, 3]
@@ -35,6 +56,10 @@ class TestMultiDict(unittest.TestCase):
   35    56 		self.assertEqual(self.d.join('foo'), '1,2,3')
   36    57 		self.assertEqual(self.d.join('bar', default='baz'), 'baz')
   37    58 
   -1    59 	def test_join_missing_key(self):
   -1    60 		with self.assertRaises(KeyError):
   -1    61 			self.d.join('foo', default=None)
   -1    62 
   38    63 	def test_update(self):
   39    64 		self.d['foo'] = [1]
   40    65 		self.d['bar'] = [1, 2]
@@ -69,6 +94,169 @@ class TestMerged(unittest.TestCase):
   69    94 			self.assertIn(item, expected)
   70    95 
   71    96 
   -1    97 class TestMapKeys(unittest.TestCase):
   -1    98 	def test_simple(self):
   -1    99 		d = cctool.MultiDict([
   -1   100 			('foo', [1, 2]),
   -1   101 			('bar', [3, 4]),
   -1   102 		])
   -1   103 
   -1   104 		d2 = cctool.map_keys(d, {
   -1   105 			'foo': 'baz'
   -1   106 		})
   -1   107 
   -1   108 		self.assertEqual(list(d2.keys()), ['baz'])
   -1   109 		self.assertEqual(d2['baz'], [1, 2])
   -1   110 
   -1   111 	def test_keep_order(self):
   -1   112 		d = cctool.MultiDict([
   -1   113 			('foo', [1, 2]),
   -1   114 			('bar', [3, 4]),
   -1   115 			('baz', [4, 5]),
   -1   116 		])
   -1   117 
   -1   118 		d2 = cctool.map_keys(d, {
   -1   119 			'foo': 'bar',
   -1   120 			'bar': 'baz',
   -1   121 			'baz': 'foo',
   -1   122 		})
   -1   123 
   -1   124 		self.assertEqual(list(d2.keys()), ['bar', 'baz', 'foo'])
   -1   125 		self.assertEqual(d2['bar'], [1, 2])
   -1   126 		self.assertEqual(d2['baz'], [3, 4])
   -1   127 		self.assertEqual(d2['foo'], [4, 5])
   -1   128 
   -1   129 	def test_reverse(self):
   -1   130 		d = cctool.MultiDict([
   -1   131 			('foo', [1, 2]),
   -1   132 			('bar', [3, 4]),
   -1   133 			('baz', [4, 5]),
   -1   134 		])
   -1   135 
   -1   136 		d2 = cctool.map_keys(d, {
   -1   137 			'foo': 'bar',
   -1   138 			'bar': 'baz',
   -1   139 			'baz': 'foo',
   -1   140 		}, reverse=True)
   -1   141 
   -1   142 		self.assertEqual(list(d2.keys()), ['baz', 'foo', 'bar'])
   -1   143 		self.assertEqual(d2['baz'], [1, 2])
   -1   144 		self.assertEqual(d2['foo'], [3, 4])
   -1   145 		self.assertEqual(d2['bar'], [4, 5])
   -1   146 
   -1   147 	def test_non_exclusive(self):
   -1   148 		d = cctool.MultiDict([
   -1   149 			('foo', [1, 2]),
   -1   150 			('bar', [3, 4]),
   -1   151 		])
   -1   152 
   -1   153 		d2 = cctool.map_keys(d, {
   -1   154 			'foo': 'baz'
   -1   155 		}, exclusive=False)
   -1   156 
   -1   157 		self.assertEqual(list(d2.keys()), ['baz', 'bar'])
   -1   158 		self.assertEqual(d2['baz'], [1, 2])
   -1   159 		self.assertEqual(d2['bar'], [3, 4])
   -1   160 
   -1   161 	def test_join(self):
   -1   162 		d = cctool.MultiDict([
   -1   163 			('foo', [1, 2]),
   -1   164 			('bar', [3, 4]),
   -1   165 			('baz', [4, 5]),
   -1   166 		])
   -1   167 
   -1   168 		d2 = cctool.map_keys(d, {
   -1   169 			'foo': 'foo',
   -1   170 			'bar': 'foo',
   -1   171 			'baz': 'baz',
   -1   172 		})
   -1   173 
   -1   174 		self.assertEqual(list(d2.keys()), ['foo', 'baz'])
   -1   175 		self.assertEqual(d2['foo'], [1, 2, 3, 4])
   -1   176 		self.assertEqual(d2['baz'], [4, 5])
   -1   177 
   -1   178 	@unittest.skip  # non deterministic
   -1   179 	def test_join_reverse(self):
   -1   180 		d = cctool.MultiDict([
   -1   181 			('foo', [1, 2]),
   -1   182 			('bar', [3, 4]),
   -1   183 			('baz', [4, 5]),
   -1   184 		])
   -1   185 
   -1   186 		d2 = cctool.map_keys(d, {
   -1   187 			'foo': 'foo',
   -1   188 			'bar': 'foo',
   -1   189 			'baz': 'baz',
   -1   190 		}, reverse=True)
   -1   191 
   -1   192 		self.assertEqual(list(d2.keys()), ['bar', 'baz'])
   -1   193 		self.assertEqual(d2['bar'], [1, 2])
   -1   194 		self.assertEqual(d2['baz'], [4, 5])
   -1   195 
   -1   196 	def test_non_destructive(self):
   -1   197 		d = cctool.MultiDict([
   -1   198 			('foo', [1, 2]),
   -1   199 			('bar', [3, 4]),
   -1   200 			('baz', [4, 5]),
   -1   201 		])
   -1   202 
   -1   203 		d2 = cctool.map_keys(d, {
   -1   204 			'foo': 'bar',
   -1   205 			'bar': 'baz',
   -1   206 			'baz': 'foo',
   -1   207 		})
   -1   208 
   -1   209 		self.assertEqual(list(d.keys()), ['foo', 'bar', 'baz'])
   -1   210 		self.assertEqual(d['foo'], [1, 2])
   -1   211 		self.assertEqual(d['bar'], [3, 4])
   -1   212 		self.assertEqual(d['baz'], [4, 5])
   -1   213 
   -1   214 
   -1   215 class TestEvent2Person(unittest.TestCase):
   -1   216 	def test_event2person(self):
   -1   217 		items = list(cctool.event2person([cctool.MultiDict([
   -1   218 			('summary', ['some summary']),
   -1   219 			('dtstart', [dt]),
   -1   220 			('tag', ['tag1', 'tag2']),
   -1   221 		])]))
   -1   222 
   -1   223 		self.assertEqual(list(items[0].keys()), ['name', 'bday', 'tag'])
   -1   224 		self.assertEqual(items[0]['name'], ['some summary'])
   -1   225 		self.assertEqual(items[0]['bday'], [dt])
   -1   226 		self.assertEqual(items[0]['tag'], ['tag1', 'tag2'])
   -1   227 
   -1   228 	def test_person2event(self):
   -1   229 		items = list(cctool.event2person([cctool.MultiDict([
   -1   230 			('name', ['some name']),
   -1   231 			('bday', [dt]),
   -1   232 			('tag', ['tag1', 'tag2']),
   -1   233 		])], reverse=True))
   -1   234 
   -1   235 		self.assertEqual(list(items[0].keys()), ['summary', 'dtstart', 'tag', 'freq'])
   -1   236 		self.assertEqual(items[0]['summary'], ['some name'])
   -1   237 		self.assertEqual(items[0]['dtstart'], [dt])
   -1   238 		self.assertEqual(items[0]['tag'], ['tag1', 'tag2'])
   -1   239 		self.assertEqual(items[0]['freq'], ['yearly'])
   -1   240 
   -1   241 	def test_event2person_wo_dtstart(self):
   -1   242 		items = list(cctool.event2person([cctool.MultiDict([
   -1   243 			('summary', ['some summary']),
   -1   244 			('tag', ['tag1', 'tag2']),
   -1   245 		])]))
   -1   246 
   -1   247 		self.assertEqual(list(items[0].keys()), ['name', 'tag'])
   -1   248 		self.assertEqual(items[0]['name'], ['some summary'])
   -1   249 		self.assertEqual(items[0]['tag'], ['tag1', 'tag2'])
   -1   250 
   -1   251 	def test_person2event_wo_dtstart(self):
   -1   252 		items = list(cctool.event2person([cctool.MultiDict([
   -1   253 			('name', ['some name']),
   -1   254 			('tag', ['tag1', 'tag2']),
   -1   255 		])], reverse=True))
   -1   256 
   -1   257 		self.assertEqual(len(items), 0)
   -1   258 
   -1   259 
   72   260 class _TestFormat(unittest.TestCase):
   73   261 	data = [cctool.MultiDict({'name': ['foo']})]
   74   262 
@@ -85,11 +273,11 @@ class TestBSDCal(_TestFormat):
   85   273 		self.data = [
   86   274 			cctool.MultiDict([
   87   275 				('dtstart', [dt]),
   88    -1 				('summary', [u'foo']),
   -1   276 				('summary', ['foo']),
   89   277 			]),
   90   278 			cctool.MultiDict([
   91   279 				('dtstart', [dt]),
   92    -1 				('summary', [u'bar']),
   -1   280 				('summary', ['bar']),
   93   281 				('freq', ['yearly']),
   94   282 			]),
   95   283 		]
@@ -100,8 +288,18 @@ class TestBSDCal(_TestFormat):
  100   288 class TestICal(_TestFormat):
  101   289 	def setUp(self):
  102   290 		self.format = cctool.ICal()
  103    -1 		self.data = [cctool.MultiDict({u'summary': [u'lorem ipsum']})]
  104    -1 		self.text = b'BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//XI//NONSGML CCTOOL//\r\nBEGIN:VEVENT\r\nSUMMARY:lorem ipsum\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n'
   -1   291 		self.data = [
   -1   292 			cctool.MultiDict([
   -1   293 				('summary', ['lorem ipsum']),
   -1   294 				('dtstart', [dt]),
   -1   295 				('freq', ['daily']),
   -1   296 			]),
   -1   297 			cctool.MultiDict([
   -1   298 				('summary', ['lorem ipsum2', 'lorem ipsum3']),
   -1   299 				('dtstart', [dt.date()]),
   -1   300 			]),
   -1   301 		]
   -1   302 		self.text = b'BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//XI//NONSGML CCTOOL//\r\nBEGIN:VEVENT\r\nSUMMARY:lorem ipsum\r\nDTSTART;VALUE=DATE-TIME:20150101T000000\r\nRRULE:FREQ=DAILY\r\nEND:VEVENT\r\nBEGIN:VEVENT\r\nSUMMARY:lorem ipsum2\r\nSUMMARY:lorem ipsum3\r\nDTSTART;VALUE=DATE:20150101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n'
  105   303 
  106   304 
  107   305 class TestABook(_TestFormat):
@@ -126,6 +324,24 @@ class TestLDIF(_TestFormat):
  126   324 	def test_dump(self):
  127   325 		pass
  128   326 
   -1   327 	def test_get_blocks(self):
   -1   328 		fh = BytesIO(b'foo: bar\n  baz\n zz\n# second block\nencoded:: aHVodQ==\n\nfoo: bar')
   -1   329 		blocks = list(self.format.get_blocks(fh))
   -1   330 
   -1   331 		self.assertEqual(blocks, [
   -1   332 			[b'foo: bar bazzz', b'encoded:: aHVodQ=='],
   -1   333 			[b'foo: bar'],
   -1   334 		])
   -1   335 
   -1   336 	def test_get_blocks_w_trailing(self):
   -1   337 		fh = BytesIO(b'foo: bar\n  baz\n zz\n# second block\nencoded:: aHVodQ==\n\nfoo: bar\n\n')
   -1   338 		blocks = list(self.format.get_blocks(fh))
   -1   339 
   -1   340 		self.assertEqual(blocks, [
   -1   341 			[b'foo: bar bazzz', b'encoded:: aHVodQ=='],
   -1   342 			[b'foo: bar'],
   -1   343 		])
   -1   344 
  129   345 
  130   346 class TestJSON(_TestFormat):
  131   347 	def setUp(self):
@@ -165,5 +381,47 @@ class TestArgs(unittest.TestCase):
  165   381 		self.assertEqual(args.outformat, 'bsdcal')
  166   382 
  167   383 
   -1   384 class ArgsMock(object):
   -1   385 	outformat = None
   -1   386 	output = None
   -1   387 
   -1   388 
   -1   389 class TestGetOutformat(unittest.TestCase):
   -1   390 	def test_arg(self):
   -1   391 		args = ArgsMock()
   -1   392 		args.outformat = 'json'
   -1   393 
   -1   394 		self.assertEqual(cctool.get_outformat(args), 'json')
   -1   395 
   -1   396 	def test_extension(self):
   -1   397 		args = ArgsMock()
   -1   398 		args.output = 'foo.json'
   -1   399 
   -1   400 		self.assertEqual(cctool.get_outformat(args), 'json')
   -1   401 
   -1   402 
   -1   403 class TestGetInformat(unittest.TestCase):
   -1   404 	def test_extension(self):
   -1   405 		filename = 'foo.json'
   -1   406 		e, fn = cctool.get_informat(filename)
   -1   407 
   -1   408 		self.assertEqual(e, 'json')
   -1   409 		self.assertEqual(fn, filename)
   -1   410 
   -1   411 	def test_colon(self):
   -1   412 		filename = 'foo:json'
   -1   413 		e, fn = cctool.get_informat(filename)
   -1   414 
   -1   415 		self.assertEqual(e, 'json')
   -1   416 		self.assertEqual(fn, 'foo')
   -1   417 
   -1   418 	def test_both(self):
   -1   419 		filename = 'foo.json:json'
   -1   420 		e, fn = cctool.get_informat(filename)
   -1   421 
   -1   422 		self.assertEqual(e, 'json')
   -1   423 		self.assertEqual(fn, 'foo.json')
   -1   424 
   -1   425 
  168   426 if __name__ == '__main__':
  169   427     unittest.main()