Blob Blame History Raw
import re
import json
import unittest
from collections import namedtuple
from io import StringIO
from typing import Optional, Union, re as re_t, Iterator
from unittest.result import TestResult

from ndjson_testrunner import JSONTestRunner


StructuredResult = namedtuple('StructuredResult', 'type id desc msg')


class TestsToBeTested(unittest.TestCase):
	"""Those tests are not there to pass, but to create all kinds of output"""
	def test_success(self):
		pass

	def test_failure(self):
		self.assertFalse(True)

	def test_error(self):
		raise Exception('Something went wrong')

	def test_subtest_success(self):
		with self.subTest('a succeeding subtest'):
			pass

	def test_subtest_failure(self):
		with self.subTest('a failing subtest'):
			self.test_failure()

		with self.subTest('an erroring subtest'):
			self.test_error()


class TestRunner(unittest.TestCase):
	def setUp(self):
		self.capture = StringIO()
		self.runner = JSONTestRunner(self.capture)

	def run_test(self, name: str, subtests=False) -> Union[dict, Iterator[dict]]:
		self.runner.run(unittest.defaultTestLoader.loadTestsFromName('tests.TestsToBeTested.{}'.format(name)))
		v = self.capture.getvalue()
		self.assertTrue(v, 'no JSON received')
		if subtests:
			return map(json.loads, v.strip().split('\n'))
		else:
			self.assertNotIn('\n', v.strip(), 'expected 1 ndjson record')
			return json.loads(v)

	def check_test(self, test: Union[str, dict], typ: str, id_: str, desc: Optional[str], msg_re: Union[str, re_t.Pattern]):
		if isinstance(test, str):
			test = self.run_test(test)
		self.assertSetEqual(set(test.keys()), {'type', 'id', 'desc', 'msg'})
		result = StructuredResult(**test)
		self.assertEqual(result.type, typ)
		self.assertEqual(result.id, id_)
		self.assertEqual(result.desc, desc)
		if result.msg is not None:
			self.assertRegex(result.msg, re.compile(msg_re, re.DOTALL))
		return result

	def test_success(self):
		"""Test a normal succeeding tests"""
		self.check_test(
			'test_success', 'success',
			'tests.TestsToBeTested.test_success',
			None, None)

	def test_failure(self):
		"""Test a normal failing tests"""
		self.check_test(
			'test_failure', 'failure',
			'tests.TestsToBeTested.test_failure',
			None,
			r'^Traceback.*in test_failure.*AssertionError: True is not false\n$')

	def test_error(self):
		self.check_test(
			'test_error', 'error',
			'tests.TestsToBeTested.test_error',
			None,
			r'^Traceback.*in test_error.*Exception: Something went wrong\n$')

	def test_subtest_success(self):
		"""Test if subtests result in the whole thing to pass"""
		succ_sub, succ_total = self.run_test('test_subtest_success', subtests=True)
		self.check_test(
			succ_sub, 'success',
			'tests.TestsToBeTested.test_subtest_success [a succeeding subtest]',
			None, None)
		self.check_test(
			succ_total, 'success',
			'tests.TestsToBeTested.test_subtest_success',
			None, None)

	def test_subtest_failure(self):
		"""Test if all subtests run"""
		fail, error = self.run_test('test_subtest_failure', subtests=True)
		self.check_test(
			fail, 'failure',
			'tests.TestsToBeTested.test_subtest_failure [a failing subtest]',
			None,
			r'^Traceback.*in test_subtest_failure.*AssertionError: True is not false\n$')
		self.check_test(
			error, 'error',
			'tests.TestsToBeTested.test_subtest_failure [an erroring subtest]',
			None,
			r'^Traceback.*in test_error.*Exception: Something went wrong\n$')


def load_tests(loader: unittest.TestLoader, standard_tests: unittest.TestSuite, pattern: Optional[str]):
	return unittest.TestSuite(loader.loadTestsFromTestCase(TestRunner))

if __name__ == '__main__':
	unittest.main()