# Copyright 2017 The Abseil Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for absltest.""" import collections import contextlib import dataclasses import io import os import pathlib import re import stat import string import subprocess import sys import tempfile import textwrap from typing import Optional import unittest from absl.testing import _bazelize_command from absl.testing import absltest from absl.testing import parameterized from absl.testing.tests import absltest_env class BaseTestCase(absltest.TestCase): def _get_helper_exec_path(self, helper_name): helper = 'absl/testing/tests/' + helper_name return _bazelize_command.get_executable_path(helper) def run_helper( self, test_id, args, env_overrides, expect_success, helper_name=None, ): env = absltest_env.inherited_env() for key, value in env_overrides.items(): if value is None: if key in env: del env[key] else: env[key] = value if helper_name is None: helper_name = 'absltest_test_helper' command = [self._get_helper_exec_path(helper_name)] if test_id is not None: command.append('--test_id={}'.format(test_id)) command.extend(args) process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, universal_newlines=True) stdout, stderr = process.communicate() if expect_success: self.assertEqual( 0, process.returncode, 'Expected success, but failed with exit code {},' ' stdout:\n{}\nstderr:\n{}\n'.format( process.returncode, stdout, stderr ), ) else: self.assertGreater( process.returncode, 0, 'Expected failure, but succeeded with ' 'stdout:\n{}\nstderr:\n{}\n'.format(stdout, stderr), ) return stdout, stderr, process.returncode class TestCaseTest(BaseTestCase): longMessage = True def run_helper( self, test_id, args, env_overrides, expect_success, helper_name=None ): return super(TestCaseTest, self).run_helper( test_id, args + ['HelperTest'], env_overrides, expect_success, helper_name, ) def test_flags_no_env_var_no_flags(self): self.run_helper( 1, [], {'TEST_RANDOM_SEED': None, 'TEST_SRCDIR': None, 'TEST_TMPDIR': None, }, expect_success=True) def test_flags_env_var_no_flags(self): tmpdir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) srcdir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) self.run_helper( 2, [], {'TEST_RANDOM_SEED': '321', 'TEST_SRCDIR': srcdir, 'TEST_TMPDIR': tmpdir, 'ABSLTEST_TEST_HELPER_EXPECTED_TEST_SRCDIR': srcdir, 'ABSLTEST_TEST_HELPER_EXPECTED_TEST_TMPDIR': tmpdir, }, expect_success=True) def test_flags_no_env_var_flags(self): tmpdir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) srcdir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) self.run_helper( 3, ['--test_random_seed=123', '--test_srcdir={}'.format(srcdir), '--test_tmpdir={}'.format(tmpdir)], {'TEST_RANDOM_SEED': None, 'TEST_SRCDIR': None, 'TEST_TMPDIR': None, 'ABSLTEST_TEST_HELPER_EXPECTED_TEST_SRCDIR': srcdir, 'ABSLTEST_TEST_HELPER_EXPECTED_TEST_TMPDIR': tmpdir, }, expect_success=True) def test_flags_env_var_flags(self): tmpdir_from_flag = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) srcdir_from_flag = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) tmpdir_from_env_var = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) srcdir_from_env_var = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) self.run_helper( 4, ['--test_random_seed=221', '--test_srcdir={}'.format(srcdir_from_flag), '--test_tmpdir={}'.format(tmpdir_from_flag)], {'TEST_RANDOM_SEED': '123', 'TEST_SRCDIR': srcdir_from_env_var, 'TEST_TMPDIR': tmpdir_from_env_var, 'ABSLTEST_TEST_HELPER_EXPECTED_TEST_SRCDIR': srcdir_from_flag, 'ABSLTEST_TEST_HELPER_EXPECTED_TEST_TMPDIR': tmpdir_from_flag, }, expect_success=True) def test_xml_output_file_from_xml_output_file_env(self): xml_dir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) xml_output_file_env = os.path.join(xml_dir, 'xml_output_file.xml') random_dir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) self.run_helper( 6, [], {'XML_OUTPUT_FILE': xml_output_file_env, 'RUNNING_UNDER_TEST_DAEMON': '1', 'TEST_XMLOUTPUTDIR': random_dir, 'ABSLTEST_TEST_HELPER_EXPECTED_XML_OUTPUT_FILE': xml_output_file_env, }, expect_success=True) def test_xml_output_file_from_daemon(self): tmpdir = os.path.join(tempfile.mkdtemp( dir=absltest.TEST_TMPDIR.value), 'sub_dir') random_dir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) self.run_helper( 6, ['--test_tmpdir', tmpdir], {'XML_OUTPUT_FILE': None, 'RUNNING_UNDER_TEST_DAEMON': '1', 'TEST_XMLOUTPUTDIR': random_dir, 'ABSLTEST_TEST_HELPER_EXPECTED_XML_OUTPUT_FILE': os.path.join( os.path.dirname(tmpdir), 'test_detail.xml'), }, expect_success=True) def test_xml_output_file_from_test_xmloutputdir_env(self): xml_output_dir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) expected_xml_file = 'absltest_test_helper.xml' self.run_helper( 6, [], {'XML_OUTPUT_FILE': None, 'RUNNING_UNDER_TEST_DAEMON': None, 'TEST_XMLOUTPUTDIR': xml_output_dir, 'ABSLTEST_TEST_HELPER_EXPECTED_XML_OUTPUT_FILE': os.path.join( xml_output_dir, expected_xml_file), }, expect_success=True) def test_xml_output_file_from_flag(self): random_dir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) flag_file = os.path.join( tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value), 'output.xml') self.run_helper( 6, ['--xml_output_file', flag_file], {'XML_OUTPUT_FILE': os.path.join(random_dir, 'output.xml'), 'RUNNING_UNDER_TEST_DAEMON': '1', 'TEST_XMLOUTPUTDIR': random_dir, 'ABSLTEST_TEST_HELPER_EXPECTED_XML_OUTPUT_FILE': flag_file, }, expect_success=True) def test_app_run(self): stdout, _, _ = self.run_helper( 7, ['--name=cat', '--name=dog'], {'ABSLTEST_TEST_HELPER_USE_APP_RUN': '1'}, expect_success=True, ) self.assertIn('Names in main() are: cat dog', stdout) self.assertIn('Names in test_name_flag() are: cat dog', stdout) def test_assert_in(self): animals = {'monkey': 'banana', 'cow': 'grass', 'seal': 'fish'} self.assertIn('a', 'abc') self.assertIn(2, [1, 2, 3]) self.assertIn('monkey', animals) self.assertNotIn('d', 'abc') self.assertNotIn(0, [1, 2, 3]) self.assertNotIn('otter', animals) self.assertRaises(AssertionError, self.assertIn, 'x', 'abc') self.assertRaises(AssertionError, self.assertIn, 4, [1, 2, 3]) self.assertRaises(AssertionError, self.assertIn, 'elephant', animals) self.assertRaises(AssertionError, self.assertNotIn, 'c', 'abc') self.assertRaises(AssertionError, self.assertNotIn, 1, [1, 2, 3]) self.assertRaises(AssertionError, self.assertNotIn, 'cow', animals) @absltest.expectedFailure def test_expected_failure(self): self.assertEqual(1, 2) # the expected failure @absltest.expectedFailureIf(True, 'always true') def test_expected_failure_if(self): self.assertEqual(1, 2) # the expected failure def test_expected_failure_success(self): _, stderr, _ = self.run_helper(5, ['--', '-v'], {}, expect_success=False) self.assertRegex(stderr, r'FAILED \(.*unexpected successes=1\)') def test_assert_equal(self): self.assertListEqual([], []) self.assertTupleEqual((), ()) self.assertSequenceEqual([], ()) a = [0, 'a', []] b = [] self.assertRaises(absltest.TestCase.failureException, self.assertListEqual, a, b) self.assertRaises(absltest.TestCase.failureException, self.assertListEqual, tuple(a), tuple(b)) self.assertRaises(absltest.TestCase.failureException, self.assertSequenceEqual, a, tuple(b)) b.extend(a) self.assertListEqual(a, b) self.assertTupleEqual(tuple(a), tuple(b)) self.assertSequenceEqual(a, tuple(b)) self.assertSequenceEqual(tuple(a), b) self.assertRaises(AssertionError, self.assertListEqual, a, tuple(b)) self.assertRaises(AssertionError, self.assertTupleEqual, tuple(a), b) self.assertRaises(AssertionError, self.assertListEqual, None, b) self.assertRaises(AssertionError, self.assertTupleEqual, None, tuple(b)) self.assertRaises(AssertionError, self.assertSequenceEqual, None, tuple(b)) self.assertRaises(AssertionError, self.assertListEqual, 1, 1) self.assertRaises(AssertionError, self.assertTupleEqual, 1, 1) self.assertRaises(AssertionError, self.assertSequenceEqual, 1, 1) self.assertSameElements([1, 2, 3], [3, 2, 1]) self.assertSameElements([1, 2] + [3] * 100, [1] * 100 + [2, 3]) self.assertSameElements(['foo', 'bar', 'baz'], ['bar', 'baz', 'foo']) self.assertRaises(AssertionError, self.assertSameElements, [10], [10, 11]) self.assertRaises(AssertionError, self.assertSameElements, [10, 11], [10]) # Test that sequences of unhashable objects can be tested for sameness: self.assertSameElements([[1, 2], [3, 4]], [[3, 4], [1, 2]]) self.assertRaises(AssertionError, self.assertSameElements, [[1]], [[2]]) def test_assert_items_equal_hotfix(self): """Confirm that http://bugs.python.org/issue14832 - b/10038517 is gone.""" for assert_items_method in (self.assertItemsEqual, self.assertCountEqual): with self.assertRaises(self.failureException) as error_context: assert_items_method([4], [2]) error_message = str(error_context.exception) # Confirm that the bug is either no longer present in Python or that our # assertItemsEqual patching version of the method in absltest.TestCase # doesn't get used. self.assertIn('First has 1, Second has 0: 4', error_message) self.assertIn('First has 0, Second has 1: 2', error_message) def test_assert_dict_equal(self): self.assertDictEqual({}, {}) c = {'x': 1} d = {} self.assertRaises(absltest.TestCase.failureException, self.assertDictEqual, c, d) d.update(c) self.assertDictEqual(c, d) d['x'] = 0 self.assertRaises(absltest.TestCase.failureException, self.assertDictEqual, c, d, 'These are unequal') self.assertRaises(AssertionError, self.assertDictEqual, None, d) self.assertRaises(AssertionError, self.assertDictEqual, [], d) self.assertRaises(AssertionError, self.assertDictEqual, 1, 1) try: # Ensure we use equality as the sole measure of elements, not type, since # that is consistent with dict equality. self.assertDictEqual({1: 1.0, 2: 2}, {1: 1, 2: 3}) except AssertionError as e: self.assertMultiLineEqual('{1: 1.0, 2: 2} != {1: 1, 2: 3}\n' 'repr() of differing entries:\n2: 2 != 3\n', str(e)) try: self.assertDictEqual({}, {'x': 1}) except AssertionError as e: self.assertMultiLineEqual("{} != {'x': 1}\n" "Unexpected, but present entries:\n'x': 1\n", str(e)) else: self.fail('Expecting AssertionError') try: self.assertDictEqual({}, {'x': 1}, 'a message') except AssertionError as e: self.assertIn('a message', str(e)) else: self.fail('Expecting AssertionError') expected = {'a': 1, 'b': 2, 'c': 3} seen = {'a': 2, 'c': 3, 'd': 4} try: self.assertDictEqual(expected, seen) except AssertionError as e: self.assertMultiLineEqual("""\ {'a': 1, 'b': 2, 'c': 3} != {'a': 2, 'c': 3, 'd': 4} Unexpected, but present entries: 'd': 4 repr() of differing entries: 'a': 1 != 2 Missing entries: 'b': 2 """, str(e)) else: self.fail('Expecting AssertionError') self.assertRaises(AssertionError, self.assertDictEqual, (1, 2), {}) self.assertRaises(AssertionError, self.assertDictEqual, {}, (1, 2)) # Ensure deterministic output of keys in dictionaries whose sort order # doesn't match the lexical ordering of repr -- this is most Python objects, # which are keyed by memory address. class Obj(object): def __init__(self, name): self.name = name def __repr__(self): return self.name try: self.assertDictEqual( {'a': Obj('A'), Obj('b'): Obj('B'), Obj('c'): Obj('C')}, {'a': Obj('A'), Obj('d'): Obj('D'), Obj('e'): Obj('E')}) except AssertionError as e: # Do as best we can not to be misleading when objects have the same repr # but aren't equal. err_str = str(e) self.assertStartsWith(err_str, "{'a': A, b: B, c: C} != {'a': A, d: D, e: E}\n") self.assertRegex( err_str, r'(?ms).*^Unexpected, but present entries:\s+' r'^(d: D$\s+^e: E|e: E$\s+^d: D)$') self.assertRegex( err_str, r'(?ms).*^repr\(\) of differing entries:\s+' r'^.a.: A != A$', err_str) self.assertRegex( err_str, r'(?ms).*^Missing entries:\s+' r'^(b: B$\s+^c: C|c: C$\s+^b: B)$') else: self.fail('Expecting AssertionError') # Confirm that safe_repr, not repr, is being used. class RaisesOnRepr(object): def __repr__(self): return 1/0 # Intentionally broken __repr__ implementation. try: self.assertDictEqual( {RaisesOnRepr(): RaisesOnRepr()}, {RaisesOnRepr(): RaisesOnRepr()} ) self.fail('Expected dicts not to match') except AssertionError as e: # Depending on the testing environment, the object may get a __main__ # prefix or a absltest_test prefix, so strip that for comparison. error_msg = re.sub( r'( at 0x[^>]+)|__main__\.|absltest_test\.', '', str(e)) self.assertRegex(error_msg, """(?m)\ {<.*RaisesOnRepr object.*>: <.*RaisesOnRepr object.*>} != \ {<.*RaisesOnRepr object.*>: <.*RaisesOnRepr object.*>} Unexpected, but present entries: <.*RaisesOnRepr object.*>: <.*RaisesOnRepr object.*> Missing entries: <.*RaisesOnRepr object.*>: <.*RaisesOnRepr object.*> """) # Confirm that safe_repr, not repr, is being used. class RaisesOnLt(object): def __lt__(self, unused_other): raise TypeError('Object is unordered.') def __repr__(self): return '' try: self.assertDictEqual( {RaisesOnLt(): RaisesOnLt()}, {RaisesOnLt(): RaisesOnLt()}) except AssertionError as e: self.assertIn('Unexpected, but present entries:\n other.x except AttributeError: return NotImplemented def __ge__(self, other): try: return self.x >= other.x except AttributeError: return NotImplemented class B(A): """Like A, but not hashable.""" __hash__ = None self.assertTotallyOrdered( [A(1, 'a')], [A(2, 'b')], # 2 is after 1. [ A(3, 'c'), B(3, 'd'), B(3, 'e') # The second argument is irrelevant. ], [A(4, 'z')]) # Invalid. msg = 'This is a useful message' whole_msg = '2 not less than 1 : This is a useful message' self.assertRaisesWithLiteralMatch(AssertionError, whole_msg, self.assertTotallyOrdered, [2], [1], msg=msg) self.assertRaises(AssertionError, self.assertTotallyOrdered, [2], [1]) self.assertRaises(AssertionError, self.assertTotallyOrdered, [2], [1], [3]) self.assertRaises(AssertionError, self.assertTotallyOrdered, [1, 2]) def test_short_description_without_docstring(self): self.assertEqual( self.shortDescription(), 'TestCaseTest.test_short_description_without_docstring', ) def test_short_description_with_one_line_docstring(self): """Tests shortDescription() for a method with a docstring.""" self.assertEqual( self.shortDescription(), 'TestCaseTest.test_short_description_with_one_line_docstring\n' 'Tests shortDescription() for a method with a docstring.', ) def test_short_description_with_multi_line_docstring(self): """Tests shortDescription() for a method with a longer docstring. This method ensures that only the first line of a docstring is returned used in the short description, no matter how long the whole thing is. """ self.assertEqual( self.shortDescription(), 'TestCaseTest.test_short_description_with_multi_line_docstring\n' 'Tests shortDescription() for a method with a longer docstring.', ) def test_assert_url_equal_same(self): self.assertUrlEqual('http://a', 'http://a') self.assertUrlEqual('http://a/path/test', 'http://a/path/test') self.assertUrlEqual('#fragment', '#fragment') self.assertUrlEqual('http://a/?q=1', 'http://a/?q=1') self.assertUrlEqual('http://a/?q=1&v=5', 'http://a/?v=5&q=1') self.assertUrlEqual('/logs?v=1&a=2&t=labels&f=path%3A%22foo%22', '/logs?a=2&f=path%3A%22foo%22&v=1&t=labels') self.assertUrlEqual('http://a/path;p1', 'http://a/path;p1') self.assertUrlEqual('http://a/path;p2;p3;p1', 'http://a/path;p1;p2;p3') self.assertUrlEqual('sip:alice@atlanta.com;maddr=239.255.255.1;ttl=15', 'sip:alice@atlanta.com;ttl=15;maddr=239.255.255.1') self.assertUrlEqual('http://nyan/cat?p=1&b=', 'http://nyan/cat?b=&p=1') def test_assert_url_equal_different(self): msg = 'This is a useful message' whole_msg = 'This is a useful message:\n- a\n+ b\n' self.assertRaisesWithLiteralMatch(AssertionError, whole_msg, self.assertUrlEqual, 'http://a', 'http://b', msg=msg) self.assertRaises(AssertionError, self.assertUrlEqual, 'http://a/x', 'http://a:8080/x') self.assertRaises(AssertionError, self.assertUrlEqual, 'http://a/x', 'http://a/y') self.assertRaises(AssertionError, self.assertUrlEqual, 'http://a/?q=2', 'http://a/?q=1') self.assertRaises(AssertionError, self.assertUrlEqual, 'http://a/?q=1&v=5', 'http://a/?v=2&q=1') self.assertRaises(AssertionError, self.assertUrlEqual, 'http://a', 'sip://b') self.assertRaises(AssertionError, self.assertUrlEqual, 'http://a#g', 'sip://a#f') self.assertRaises(AssertionError, self.assertUrlEqual, 'http://a/path;p1;p3;p1', 'http://a/path;p1;p2;p3') self.assertRaises(AssertionError, self.assertUrlEqual, 'http://nyan/cat?p=1&b=', 'http://nyan/cat?p=1') def test_same_structure_same(self): self.assertSameStructure(0, 0) self.assertSameStructure(1, 1) self.assertSameStructure('', '') self.assertSameStructure('hello', 'hello', msg='This Should not fail') self.assertSameStructure(set(), set()) self.assertSameStructure(set([1, 2]), set([1, 2])) self.assertSameStructure(set(), frozenset()) self.assertSameStructure(set([1, 2]), frozenset([1, 2])) self.assertSameStructure([], []) self.assertSameStructure(['a'], ['a']) self.assertSameStructure([], ()) self.assertSameStructure(['a'], ('a',)) self.assertSameStructure({}, {}) self.assertSameStructure({'one': 1}, {'one': 1}) self.assertSameStructure(collections.defaultdict(None, {'one': 1}), {'one': 1}) self.assertSameStructure(collections.OrderedDict({'one': 1}), collections.defaultdict(None, {'one': 1})) def test_same_structure_different(self): # Different type with self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'int'> but b is a <(type|class) 'str'>"): self.assertSameStructure(0, 'hello') with self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'int'> but b is a <(type|class) 'list'>"): self.assertSameStructure(0, []) with self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'int'> but b is a <(type|class) 'float'>"): self.assertSameStructure(2, 2.0) with self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'list'> but b is a <(type|class) 'dict'>"): self.assertSameStructure([], {}) with self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'list'> but b is a <(type|class) 'set'>"): self.assertSameStructure([], set()) with self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'dict'> but b is a <(type|class) 'set'>"): self.assertSameStructure({}, set()) # Different scalar values self.assertRaisesWithLiteralMatch( AssertionError, 'a is 0 but b is 1', self.assertSameStructure, 0, 1) self.assertRaisesWithLiteralMatch( AssertionError, "a is 'hello' but b is 'goodbye' : This was expected", self.assertSameStructure, 'hello', 'goodbye', msg='This was expected') # Different sets self.assertRaisesWithLiteralMatch( AssertionError, r'AA has 2 but BB does not', self.assertSameStructure, set([1, 2]), set([1]), aname='AA', bname='BB') self.assertRaisesWithLiteralMatch( AssertionError, r'AA lacks 2 but BB has it', self.assertSameStructure, set([1]), set([1, 2]), aname='AA', bname='BB') # Different lists self.assertRaisesWithLiteralMatch( AssertionError, "a has [2] with value 'z' but b does not", self.assertSameStructure, ['x', 'y', 'z'], ['x', 'y']) self.assertRaisesWithLiteralMatch( AssertionError, "a lacks [2] but b has it with value 'z'", self.assertSameStructure, ['x', 'y'], ['x', 'y', 'z']) self.assertRaisesWithLiteralMatch( AssertionError, "a[2] is 'z' but b[2] is 'Z'", self.assertSameStructure, ['x', 'y', 'z'], ['x', 'y', 'Z']) # Different dicts self.assertRaisesWithLiteralMatch( AssertionError, "a has ['two'] with value 2 but it's missing in b", self.assertSameStructure, {'one': 1, 'two': 2}, {'one': 1}) self.assertRaisesWithLiteralMatch( AssertionError, "a lacks ['two'] but b has it with value 2", self.assertSameStructure, {'one': 1}, {'one': 1, 'two': 2}) self.assertRaisesWithLiteralMatch( AssertionError, "a['two'] is 2 but b['two'] is 3", self.assertSameStructure, {'one': 1, 'two': 2}, {'one': 1, 'two': 3}) # String and byte types should not be considered equivalent to other # sequences self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'list'> but b is a <(type|class) 'str'>", self.assertSameStructure, [], '') self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'str'> but b is a <(type|class) 'tuple'>", self.assertSameStructure, '', ()) self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'list'> but b is a <(type|class) 'str'>", self.assertSameStructure, ['a', 'b', 'c'], 'abc') self.assertRaisesRegex( AssertionError, r"a is a <(type|class) 'str'> but b is a <(type|class) 'tuple'>", self.assertSameStructure, 'abc', ('a', 'b', 'c')) # Deep key generation self.assertRaisesWithLiteralMatch( AssertionError, "a[0][0]['x']['y']['z'][0] is 1 but b[0][0]['x']['y']['z'][0] is 2", self.assertSameStructure, [[{'x': {'y': {'z': [1]}}}]], [[{'x': {'y': {'z': [2]}}}]]) # Multiple problems self.assertRaisesWithLiteralMatch( AssertionError, 'a[0] is 1 but b[0] is 3; a[1] is 2 but b[1] is 4', self.assertSameStructure, [1, 2], [3, 4]) with self.assertRaisesRegex( AssertionError, re.compile(r"^a\[0] is 'a' but b\[0] is 'A'; .*" r"a\[18] is 's' but b\[18] is 'S'; \.\.\.$")): self.assertSameStructure( list(string.ascii_lowercase), list(string.ascii_uppercase)) # Verify same behavior with self.maxDiff = None self.maxDiff = None self.assertRaisesWithLiteralMatch( AssertionError, 'a[0] is 1 but b[0] is 3; a[1] is 2 but b[1] is 4', self.assertSameStructure, [1, 2], [3, 4]) def test_same_structure_mapping_unchanged(self): default_a = collections.defaultdict(lambda: 'BAD MODIFICATION', {}) dict_b = {'one': 'z'} self.assertRaisesWithLiteralMatch( AssertionError, r"a lacks ['one'] but b has it with value 'z'", self.assertSameStructure, default_a, dict_b) self.assertEmpty(default_a) dict_a = {'one': 'z'} default_b = collections.defaultdict(lambda: 'BAD MODIFICATION', {}) self.assertRaisesWithLiteralMatch( AssertionError, r"a has ['one'] with value 'z' but it's missing in b", self.assertSameStructure, dict_a, default_b) self.assertEmpty(default_b) def test_same_structure_uses_type_equality_func_for_leaves(self): class CustomLeaf(object): def __init__(self, n): self.n = n def __repr__(self): return f'CustomLeaf({self.n})' def assert_custom_leaf_equal(a, b, msg): del msg assert a.n % 5 == b.n % 5 self.addTypeEqualityFunc(CustomLeaf, assert_custom_leaf_equal) self.assertSameStructure(CustomLeaf(4), CustomLeaf(9)) self.assertRaisesWithLiteralMatch( AssertionError, r'a is CustomLeaf(4) but b is CustomLeaf(8)', self.assertSameStructure, CustomLeaf(4), CustomLeaf(8), ) def test_assert_json_equal_same(self): self.assertJsonEqual('{"success": true}', '{"success": true}') self.assertJsonEqual('{"success": true}', '{"success":true}') self.assertJsonEqual('true', 'true') self.assertJsonEqual('null', 'null') self.assertJsonEqual('false', 'false') self.assertJsonEqual('34', '34') self.assertJsonEqual('[1, 2, 3]', '[1,2,3]', msg='please PASS') self.assertJsonEqual('{"sequence": [1, 2, 3], "float": 23.42}', '{"float": 23.42, "sequence": [1,2,3]}') self.assertJsonEqual('{"nest": {"spam": "eggs"}, "float": 23.42}', '{"float": 23.42, "nest": {"spam":"eggs"}}') def test_assert_json_equal_different(self): with self.assertRaises(AssertionError): self.assertJsonEqual('{"success": true}', '{"success": false}') with self.assertRaises(AssertionError): self.assertJsonEqual('{"success": false}', '{"Success": false}') with self.assertRaises(AssertionError): self.assertJsonEqual('false', 'true') with self.assertRaises(AssertionError) as error_context: self.assertJsonEqual('null', '0', msg='I demand FAILURE') self.assertIn('I demand FAILURE', error_context.exception.args[0]) self.assertIn('None', error_context.exception.args[0]) with self.assertRaises(AssertionError): self.assertJsonEqual('[1, 0, 3]', '[1,2,3]') with self.assertRaises(AssertionError): self.assertJsonEqual('{"sequence": [1, 2, 3], "float": 23.42}', '{"float": 23.42, "sequence": [1,0,3]}') with self.assertRaises(AssertionError): self.assertJsonEqual('{"nest": {"spam": "eggs"}, "float": 23.42}', '{"float": 23.42, "nest": {"Spam":"beans"}}') def test_assert_json_equal_bad_json(self): with self.assertRaises(ValueError) as error_context: self.assertJsonEqual("alhg'2;#", '{"a": true}') self.assertIn('first', error_context.exception.args[0]) self.assertIn('alhg', error_context.exception.args[0]) with self.assertRaises(ValueError) as error_context: self.assertJsonEqual('{"a": true}', "alhg'2;#") self.assertIn('second', error_context.exception.args[0]) self.assertIn('alhg', error_context.exception.args[0]) with self.assertRaises(ValueError) as error_context: self.assertJsonEqual('', '') class GetCommandStderrTestCase(absltest.TestCase): def test_return_status(self): tmpdir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) returncode = ( absltest.get_command_stderr( ['cat', os.path.join(tmpdir, 'file.txt')], env=_env_for_command_tests())[0]) self.assertEqual(1, returncode) def test_stderr(self): tmpdir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) stderr = ( absltest.get_command_stderr( ['cat', os.path.join(tmpdir, 'file.txt')], env=_env_for_command_tests())[1]) stderr = stderr.decode('utf-8') self.assertRegex(stderr, 'No such file or directory') @contextlib.contextmanager def cm_for_test(obj): try: obj.cm_state = 'yielded' yield 'value' finally: obj.cm_state = 'exited' class EnterContextTest(absltest.TestCase): def setUp(self): self.cm_state = 'unset' self.cm_value = 'unset' def assert_cm_exited(): self.assertEqual(self.cm_state, 'exited') # Because cleanup functions are run in reverse order, we have to add # our assert-cleanup before the exit stack registers its own cleanup. # This ensures we see state after the stack cleanup runs. self.addCleanup(assert_cm_exited) super(EnterContextTest, self).setUp() self.cm_value = self.enter_context(cm_for_test(self)) def test_enter_context(self): self.assertEqual(self.cm_value, 'value') self.assertEqual(self.cm_state, 'yielded') @absltest.skipIf(not hasattr(absltest.TestCase, 'addClassCleanup'), 'Python 3.8 required for class-level enter_context') class EnterContextClassmethodTest(absltest.TestCase): cm_state = 'unset' cm_value = 'unset' @classmethod def setUpClass(cls): def assert_cm_exited(): assert cls.cm_state == 'exited' # Because cleanup functions are run in reverse order, we have to add # our assert-cleanup before the exit stack registers its own cleanup. # This ensures we see state after the stack cleanup runs. cls.addClassCleanup(assert_cm_exited) super(EnterContextClassmethodTest, cls).setUpClass() cls.cm_value = cls.enter_context(cm_for_test(cls)) def test_enter_context(self): self.assertEqual(self.cm_value, 'value') self.assertEqual(self.cm_state, 'yielded') class EqualityAssertionTest(absltest.TestCase): """This test verifies that absltest.failIfEqual actually tests __ne__. If a user class implements __eq__, unittest.assertEqual will call it via first == second. However, failIfEqual also calls first == second. This means that while the caller may believe their __ne__ method is being tested, it is not. """ class NeverEqual(object): """Objects of this class behave like NaNs.""" def __eq__(self, unused_other): return False def __ne__(self, unused_other): return False class AllSame(object): """All objects of this class compare as equal.""" def __eq__(self, unused_other): return True def __ne__(self, unused_other): return False class EqualityTestsWithEq(object): """Performs all equality and inequality tests with __eq__.""" def __init__(self, value): self._value = value def __eq__(self, other): return self._value == other._value def __ne__(self, other): return not self.__eq__(other) class EqualityTestsWithNe(object): """Performs all equality and inequality tests with __ne__.""" def __init__(self, value): self._value = value def __eq__(self, other): return not self.__ne__(other) def __ne__(self, other): return self._value != other._value class EqualityTestsWithCmp(object): def __init__(self, value): self._value = value def __cmp__(self, other): return cmp(self._value, other._value) class EqualityTestsWithLtEq(object): def __init__(self, value): self._value = value def __eq__(self, other): return self._value == other._value def __lt__(self, other): return self._value < other._value def test_all_comparisons_fail(self): i1 = self.NeverEqual() i2 = self.NeverEqual() self.assertFalse(i1 == i2) self.assertFalse(i1 != i2) # Compare two distinct objects self.assertFalse(i1 is i2) self.assertRaises(AssertionError, self.assertEqual, i1, i2) self.assertRaises(AssertionError, self.assertNotEqual, i1, i2) # A NeverEqual object should not compare equal to itself either. i2 = i1 self.assertTrue(i1 is i2) self.assertFalse(i1 == i2) self.assertFalse(i1 != i2) self.assertRaises(AssertionError, self.assertEqual, i1, i2) self.assertRaises(AssertionError, self.assertNotEqual, i1, i2) def test_all_comparisons_succeed(self): a = self.AllSame() b = self.AllSame() self.assertFalse(a is b) self.assertTrue(a == b) self.assertFalse(a != b) self.assertEqual(a, b) self.assertRaises(AssertionError, self.assertNotEqual, a, b) def _perform_apple_apple_orange_checks(self, same_a, same_b, different): """Perform consistency checks with two apples and an orange. The two apples should always compare as being the same (and inequality checks should fail). The orange should always compare as being different to each of the apples. Args: same_a: the first apple same_b: the second apple different: the orange """ self.assertTrue(same_a == same_b) self.assertFalse(same_a != same_b) self.assertEqual(same_a, same_b) self.assertFalse(same_a == different) self.assertTrue(same_a != different) self.assertNotEqual(same_a, different) self.assertFalse(same_b == different) self.assertTrue(same_b != different) self.assertNotEqual(same_b, different) def test_comparison_with_eq(self): same_a = self.EqualityTestsWithEq(42) same_b = self.EqualityTestsWithEq(42) different = self.EqualityTestsWithEq(1769) self._perform_apple_apple_orange_checks(same_a, same_b, different) def test_comparison_with_ne(self): same_a = self.EqualityTestsWithNe(42) same_b = self.EqualityTestsWithNe(42) different = self.EqualityTestsWithNe(1769) self._perform_apple_apple_orange_checks(same_a, same_b, different) def test_comparison_with_cmp_or_lt_eq(self): same_a = self.EqualityTestsWithLtEq(42) same_b = self.EqualityTestsWithLtEq(42) different = self.EqualityTestsWithLtEq(1769) self._perform_apple_apple_orange_checks(same_a, same_b, different) class AssertSequenceStartsWithTest(parameterized.TestCase): def setUp(self): super().setUp() self.a = [5, 'foo', {'c': 'd'}, None] def test_empty_sequence_starts_with_empty_prefix(self): self.assertSequenceStartsWith([], ()) def test_sequence_prefix_is_an_empty_list(self): self.assertSequenceStartsWith([[]], ([], 'foo')) def test_raise_if_empty_prefix_with_non_empty_whole(self): with self.assertRaisesRegex( AssertionError, 'Prefix length is 0 but whole length is %d: %s' % (len( self.a), r"\[5, 'foo', \{'c': 'd'\}, None\]")): self.assertSequenceStartsWith([], self.a) def test_single_element_prefix(self): self.assertSequenceStartsWith([5], self.a) def test_two_element_prefix(self): self.assertSequenceStartsWith((5, 'foo'), self.a) def test_prefix_is_full_sequence(self): self.assertSequenceStartsWith([5, 'foo', {'c': 'd'}, None], self.a) def test_string_prefix(self): self.assertSequenceStartsWith('abc', 'abc123') def test_convert_non_sequence_prefix_to_sequence_and_try_again(self): self.assertSequenceStartsWith(5, self.a) def test_whole_not_asequence(self): msg = (r'For whole: len\(5\) is not supported, it appears to be type: ' '<(type|class) \'int\'>') with self.assertRaisesRegex(AssertionError, msg): self.assertSequenceStartsWith(self.a, 5) def test_raise_if_sequence_does_not_start_with_prefix(self): msg = (r"prefix: \['foo', \{'c': 'd'\}\] not found at start of whole: " r"\[5, 'foo', \{'c': 'd'\}, None\].") with self.assertRaisesRegex(AssertionError, msg): self.assertSequenceStartsWith(['foo', {'c': 'd'}], self.a) @parameterized.named_parameters( ('dict', {'a': 1, 2: 'b'}, {'a': 1, 2: 'b', 'c': '3'}), ('set', {1, 2}, {1, 2, 3}), ) def test_raise_if_set_or_dict(self, prefix, whole): with self.assertRaisesRegex( AssertionError, 'For whole: Mapping or Set objects are not supported' ): self.assertSequenceStartsWith(prefix, whole) class TestAssertEmpty(absltest.TestCase): longMessage = True def test_raises_if_not_asized_object(self): msg = "Expected a Sized object, got: 'int'" with self.assertRaisesRegex(AssertionError, msg): self.assertEmpty(1) def test_calls_len_not_bool(self): class BadList(list): def __bool__(self): return False __nonzero__ = __bool__ bad_list = BadList() self.assertEmpty(bad_list) self.assertFalse(bad_list) def test_passes_when_empty(self): empty_containers = [ list(), tuple(), dict(), set(), frozenset(), b'', u'', bytearray(), ] for container in empty_containers: self.assertEmpty(container) def test_raises_with_not_empty_containers(self): not_empty_containers = [ [1], (1,), {'foo': 'bar'}, {1}, frozenset([1]), b'a', u'a', bytearray(b'a'), ] regexp = r'.* has length of 1\.$' for container in not_empty_containers: with self.assertRaisesRegex(AssertionError, regexp): self.assertEmpty(container) def test_user_message_added_to_default(self): msg = 'This is a useful message' whole_msg = re.escape('[1] has length of 1. : This is a useful message') with self.assertRaisesRegex(AssertionError, whole_msg): self.assertEmpty([1], msg=msg) class TestAssertNotEmpty(absltest.TestCase): longMessage = True def test_raises_if_not_asized_object(self): msg = "Expected a Sized object, got: 'int'" with self.assertRaisesRegex(AssertionError, msg): self.assertNotEmpty(1) def test_calls_len_not_bool(self): class BadList(list): def __bool__(self): return False __nonzero__ = __bool__ bad_list = BadList([1]) self.assertNotEmpty(bad_list) self.assertFalse(bad_list) def test_passes_when_not_empty(self): not_empty_containers = [ [1], (1,), {'foo': 'bar'}, {1}, frozenset([1]), b'a', u'a', bytearray(b'a'), ] for container in not_empty_containers: self.assertNotEmpty(container) def test_raises_with_empty_containers(self): empty_containers = [ list(), tuple(), dict(), set(), frozenset(), b'', u'', bytearray(), ] regexp = r'.* has length of 0\.$' for container in empty_containers: with self.assertRaisesRegex(AssertionError, regexp): self.assertNotEmpty(container) def test_user_message_added_to_default(self): msg = 'This is a useful message' whole_msg = re.escape('[] has length of 0. : This is a useful message') with self.assertRaisesRegex(AssertionError, whole_msg): self.assertNotEmpty([], msg=msg) class TestAssertLen(absltest.TestCase): longMessage = True def test_raises_if_not_asized_object(self): msg = "Expected a Sized object, got: 'int'" with self.assertRaisesRegex(AssertionError, msg): self.assertLen(1, 1) def test_passes_when_expected_len(self): containers = [ [[1], 1], [(1, 2), 2], [{'a': 1, 'b': 2, 'c': 3}, 3], [{1, 2, 3, 4}, 4], [frozenset([1]), 1], [b'abc', 3], [u'def', 3], [bytearray(b'ghij'), 4], ] for container, expected_len in containers: self.assertLen(container, expected_len) def test_raises_when_unexpected_len(self): containers = [ [1], (1, 2), {'a': 1, 'b': 2, 'c': 3}, {1, 2, 3, 4}, frozenset([1]), b'abc', u'def', bytearray(b'ghij'), ] for container in containers: regexp = r'.* has length of %d, expected 100\.$' % len(container) with self.assertRaisesRegex(AssertionError, regexp): self.assertLen(container, 100) def test_user_message_added_to_default(self): msg = 'This is a useful message' whole_msg = ( r'\[1\] has length of 1, expected 100. : This is a useful message') with self.assertRaisesRegex(AssertionError, whole_msg): self.assertLen([1], 100, msg) class TestLoaderTest(absltest.TestCase): """Tests that the TestLoader bans methods named TestFoo.""" # pylint: disable=invalid-name class Valid(absltest.TestCase): """Test case containing a variety of valid names.""" test_property = 1 TestProperty = 2 @staticmethod def TestStaticMethod(): pass @staticmethod def TestStaticMethodWithArg(foo): pass @classmethod def TestClassMethod(cls): pass def Test(self): pass def TestingHelper(self): pass def testMethod(self): pass def TestHelperWithParams(self, a, b): pass def TestHelperWithVarargs(self, *args, **kwargs): pass def TestHelperWithDefaults(self, a=5): pass def TestHelperWithKeywordOnly(self, *, arg): pass class Invalid(absltest.TestCase): """Test case containing a suspicious method.""" def testMethod(self): pass def TestSuspiciousMethod(self): pass # pylint: enable=invalid-name def setUp(self): self.loader = absltest.TestLoader() def test_valid(self): suite = self.loader.loadTestsFromTestCase(TestLoaderTest.Valid) self.assertEqual(1, suite.countTestCases()) def testInvalid(self): with self.assertRaisesRegex(TypeError, 'TestSuspiciousMethod'): self.loader.loadTestsFromTestCase(TestLoaderTest.Invalid) class InitNotNecessaryForAssertsTest(absltest.TestCase): """TestCase assertions should work even if __init__ wasn't correctly called. This is a workaround, see comment in absltest.TestCase._getAssertEqualityFunc. We know that not calling __init__ of a superclass is a bad thing, but people keep doing them, and this (even if a little bit dirty) saves them from shooting themselves in the foot. """ def test_subclass(self): class Subclass(absltest.TestCase): def __init__(self): # pylint: disable=super-init-not-called pass Subclass().assertEqual({}, {}) def test_multiple_inheritance(self): class Foo(object): def __init__(self, *args, **kwargs): pass class Subclass(Foo, absltest.TestCase): pass Subclass().assertEqual({}, {}) @dataclasses.dataclass class _ExampleDataclass: comparable: str not_comparable: str = dataclasses.field(compare=False) comparable2: str = 'comparable2' @dataclasses.dataclass class _ExampleCustomEqualDataclass: value: str def __eq__(self, other): return False class TestAssertDataclassEqual(absltest.TestCase): def test_assert_dataclass_equal_checks_a_for_dataclass(self): b = _ExampleDataclass('a', 'b') message = 'First argument is not a dataclass instance.' with self.assertRaisesWithLiteralMatch(AssertionError, message): self.assertDataclassEqual('a', b) def test_assert_dataclass_equal_checks_b_for_dataclass(self): a = _ExampleDataclass('a', 'b') message = 'Second argument is not a dataclass instance.' with self.assertRaisesWithLiteralMatch(AssertionError, message): self.assertDataclassEqual(a, 'b') def test_assert_dataclass_equal_different_dataclasses(self): a = _ExampleDataclass('a', 'b') b = _ExampleCustomEqualDataclass('c') message = """Found different dataclass types: != """ with self.assertRaisesWithLiteralMatch(AssertionError, message): self.assertDataclassEqual(a, b) def test_assert_dataclass_equal(self): a = _ExampleDataclass(comparable='a', not_comparable='b') b = _ExampleDataclass(comparable='a', not_comparable='c') self.assertDataclassEqual(a, a) self.assertDataclassEqual(a, b) self.assertDataclassEqual(b, a) def test_assert_dataclass_fails_non_equal_classes_assert_dict_passes(self): a = _ExampleCustomEqualDataclass(value='a') b = _ExampleCustomEqualDataclass(value='a') message = textwrap.dedent("""\ _ExampleCustomEqualDataclass(value='a') != _ExampleCustomEqualDataclass(value='a') Cannot detect difference by examining the fields of the dataclass.""") with self.assertRaisesWithLiteralMatch(AssertionError, message): self.assertDataclassEqual(a, b) def test_assert_dataclass_fails_assert_dict_fails_one_field(self): a = _ExampleDataclass(comparable='a', not_comparable='b') b = _ExampleDataclass(comparable='c', not_comparable='d') message = textwrap.dedent("""\ _ExampleDataclass(comparable='a', not_comparable='b', comparable2='comparable2') != _ExampleDataclass(comparable='c', not_comparable='d', comparable2='comparable2') Fields that differ: comparable: 'a' != 'c'""") with self.assertRaisesWithLiteralMatch(AssertionError, message): self.assertDataclassEqual(a, b) def test_assert_dataclass_fails_assert_dict_fails_multiple_fields(self): a = _ExampleDataclass(comparable='a', not_comparable='b', comparable2='c') b = _ExampleDataclass(comparable='c', not_comparable='d', comparable2='e') message = textwrap.dedent("""\ _ExampleDataclass(comparable='a', not_comparable='b', comparable2='c') != _ExampleDataclass(comparable='c', not_comparable='d', comparable2='e') Fields that differ: comparable: 'a' != 'c' comparable2: 'c' != 'e'""") with self.assertRaisesWithLiteralMatch(AssertionError, message): self.assertDataclassEqual(a, b) class GetCommandStringTest(parameterized.TestCase): @parameterized.parameters( ([], '', ''), ([''], "''", ''), (['command', 'arg-0'], "'command' 'arg-0'", 'command arg-0'), ([u'command', u'arg-0'], "'command' 'arg-0'", u'command arg-0'), (["foo'bar"], "'foo'\"'\"'bar'", "foo'bar"), (['foo"bar'], "'foo\"bar'", 'foo"bar'), ('command arg-0', 'command arg-0', 'command arg-0'), (u'command arg-0', 'command arg-0', 'command arg-0')) def test_get_command_string( self, command, expected_non_windows, expected_windows): expected = expected_windows if os.name == 'nt' else expected_non_windows self.assertEqual(expected, absltest.get_command_string(command)) class TempFileTest(BaseTestCase): def assert_dir_exists(self, temp_dir): path = temp_dir.full_path self.assertTrue(os.path.exists(path), 'Dir {} does not exist'.format(path)) self.assertTrue(os.path.isdir(path), 'Path {} exists, but is not a directory'.format(path)) def assert_file_exists(self, temp_file, expected_content=b''): path = temp_file.full_path self.assertTrue(os.path.exists(path), 'File {} does not exist'.format(path)) self.assertTrue(os.path.isfile(path), 'Path {} exists, but is not a file'.format(path)) mode = 'rb' if isinstance(expected_content, bytes) else 'rt' with io.open(path, mode) as fp: actual = fp.read() self.assertEqual(expected_content, actual) def run_tempfile_helper(self, cleanup, expected_paths): tmpdir = self.create_tempdir('helper-test-temp-dir') env = { 'ABSLTEST_TEST_HELPER_TEMPFILE_CLEANUP': cleanup, 'TEST_TMPDIR': tmpdir.full_path, } stdout, stderr, _ = self.run_helper( 0, ['TempFileHelperTest'], env, expect_success=False ) output = ('\n=== Helper output ===\n' '----- stdout -----\n{}\n' '----- end stdout -----\n' '----- stderr -----\n{}\n' '----- end stderr -----\n' '===== end helper output =====').format(stdout, stderr) self.assertIn('test_failure', stderr, output) # Adjust paths to match on Windows expected_paths = {path.replace('/', os.sep) for path in expected_paths} actual = { os.path.relpath(f, tmpdir.full_path) for f in _listdir_recursive(tmpdir.full_path) if f != tmpdir.full_path } self.assertEqual(expected_paths, actual, output) def test_create_file_pre_existing_readonly(self): first = self.create_tempfile('foo', content='first') os.chmod(first.full_path, 0o444) second = self.create_tempfile('foo', content='second') self.assertEqual('second', first.read_text()) self.assertEqual('second', second.read_text()) def test_create_file_fails_cleanup(self): path = self.create_tempfile().full_path # Removing the write bit from the file makes it undeletable on Windows. os.chmod(path, 0) # Removing the write bit from the whole directory makes all contained files # undeletable on unix. We also need it to be exec so that os.path.isfile # returns true, and we reach the buggy branch. os.chmod(os.path.dirname(path), stat.S_IEXEC) # The test should pass, even though that file cannot be deleted in teardown. def test_temp_file_path_like(self): tempdir = self.create_tempdir('foo') tempfile_ = tempdir.create_file('bar') self.assertEqual(tempfile_.read_text(), pathlib.Path(tempfile_).read_text()) # assertIsInstance causes the types to be narrowed, so calling create_file # and read_text() must be done before these assertions to avoid type errors. self.assertIsInstance(tempdir, os.PathLike) self.assertIsInstance(tempfile_, os.PathLike) def test_unnamed(self): td = self.create_tempdir() self.assert_dir_exists(td) tdf = td.create_file() self.assert_file_exists(tdf) tdd = td.mkdir() self.assert_dir_exists(tdd) tf = self.create_tempfile() self.assert_file_exists(tf) def test_named(self): td = self.create_tempdir('d') self.assert_dir_exists(td) tdf = td.create_file('df') self.assert_file_exists(tdf) tdd = td.mkdir('dd') self.assert_dir_exists(tdd) tf = self.create_tempfile('f') self.assert_file_exists(tf) def test_nested_paths(self): td = self.create_tempdir('d1/d2') self.assert_dir_exists(td) tdf = td.create_file('df1/df2') self.assert_file_exists(tdf) tdd = td.mkdir('dd1/dd2') self.assert_dir_exists(tdd) tf = self.create_tempfile('f1/f2') self.assert_file_exists(tf) def test_tempdir_create_file(self): td = self.create_tempdir() td.create_file(content='text') def test_tempfile_text(self): tf = self.create_tempfile(content='text') self.assert_file_exists(tf, 'text') self.assertEqual('text', tf.read_text()) with tf.open_text() as fp: self.assertEqual('text', fp.read()) with tf.open_text('w') as fp: fp.write(u'text-from-open-write') self.assertEqual('text-from-open-write', tf.read_text()) tf.write_text('text-from-write-text') self.assertEqual('text-from-write-text', tf.read_text()) def test_tempfile_bytes(self): tf = self.create_tempfile(content=b'\x00\x01\x02') self.assert_file_exists(tf, b'\x00\x01\x02') self.assertEqual(b'\x00\x01\x02', tf.read_bytes()) with tf.open_bytes() as fp: self.assertEqual(b'\x00\x01\x02', fp.read()) with tf.open_bytes('wb') as fp: fp.write(b'\x03') self.assertEqual(b'\x03', tf.read_bytes()) tf.write_bytes(b'\x04') self.assertEqual(b'\x04', tf.read_bytes()) def test_tempdir_same_name(self): """Make sure the same directory name can be used.""" td1 = self.create_tempdir('foo') td2 = self.create_tempdir('foo') self.assert_dir_exists(td1) self.assert_dir_exists(td2) def test_tempfile_cleanup_success(self): expected = { 'TempFileHelperTest', 'TempFileHelperTest/test_failure', 'TempFileHelperTest/test_failure/failure', 'TempFileHelperTest/test_success', 'TempFileHelperTest/test_subtest_failure', 'TempFileHelperTest/test_subtest_failure/parent', 'TempFileHelperTest/test_subtest_failure/successful_child', 'TempFileHelperTest/test_subtest_failure/failed_child', 'TempFileHelperTest/test_subtest_success', } self.run_tempfile_helper('SUCCESS', expected) def test_tempfile_cleanup_always(self): expected = { 'TempFileHelperTest', 'TempFileHelperTest/test_failure', 'TempFileHelperTest/test_success', 'TempFileHelperTest/test_subtest_failure', 'TempFileHelperTest/test_subtest_success', } self.run_tempfile_helper('ALWAYS', expected) def test_tempfile_cleanup_off(self): expected = { 'TempFileHelperTest', 'TempFileHelperTest/test_failure', 'TempFileHelperTest/test_failure/failure', 'TempFileHelperTest/test_success', 'TempFileHelperTest/test_success/success', 'TempFileHelperTest/test_subtest_failure', 'TempFileHelperTest/test_subtest_failure/parent', 'TempFileHelperTest/test_subtest_failure/successful_child', 'TempFileHelperTest/test_subtest_failure/failed_child', 'TempFileHelperTest/test_subtest_success', 'TempFileHelperTest/test_subtest_success/parent', 'TempFileHelperTest/test_subtest_success/child0', 'TempFileHelperTest/test_subtest_success/child1', } self.run_tempfile_helper('OFF', expected) class SkipClassTest(absltest.TestCase): def test_incorrect_decorator_call(self): with self.assertRaises(TypeError): # Disabling type checking because pytype correctly picks up that # @absltest.skipThisClass is being used incorrectly. # pytype: disable=wrong-arg-types @absltest.skipThisClass class Test(absltest.TestCase): # pylint: disable=unused-variable pass # pytype: enable=wrong-arg-types def test_incorrect_decorator_subclass(self): with self.assertRaises(TypeError): @absltest.skipThisClass('reason') def test_method(): # pylint: disable=unused-variable pass def test_correct_decorator_class(self): @absltest.skipThisClass('reason') class Test(absltest.TestCase): pass with self.assertRaises(absltest.SkipTest): Test.setUpClass() def test_correct_decorator_subclass(self): @absltest.skipThisClass('reason') class Test(absltest.TestCase): pass class Subclass(Test): pass with self.subTest('Base class should be skipped'): with self.assertRaises(absltest.SkipTest): Test.setUpClass() with self.subTest('Subclass should not be skipped'): Subclass.setUpClass() # should not raise. def test_setup(self): @absltest.skipThisClass('reason') class Test(absltest.TestCase): @classmethod def setUpClass(cls): super(Test, cls).setUpClass() cls.foo = 1 class Subclass(Test): pass Subclass.setUpClass() self.assertEqual(Subclass.foo, 1) def test_setup_chain(self): @absltest.skipThisClass('reason') class BaseTest(absltest.TestCase): foo: int @classmethod def setUpClass(cls): super(BaseTest, cls).setUpClass() cls.foo = 1 @absltest.skipThisClass('reason') class SecondBaseTest(BaseTest): @classmethod def setUpClass(cls): super(SecondBaseTest, cls).setUpClass() cls.bar = 2 class Subclass(SecondBaseTest): pass Subclass.setUpClass() self.assertEqual(Subclass.foo, 1) self.assertEqual(Subclass.bar, 2) def test_setup_args(self): @absltest.skipThisClass('reason') class Test(absltest.TestCase): foo: str bar: Optional[str] @classmethod def setUpClass(cls, foo, bar=None): super(Test, cls).setUpClass() cls.foo = foo cls.bar = bar class Subclass(Test): @classmethod def setUpClass(cls): super(Subclass, cls).setUpClass('foo', bar='baz') Subclass.setUpClass() self.assertEqual(Subclass.foo, 'foo') self.assertEqual(Subclass.bar, 'baz') def test_setup_multiple_inheritance(self): # Test that skipping this class doesn't break the MRO chain and stop # RequiredBase.setUpClass from running. @absltest.skipThisClass('reason') class Left(absltest.TestCase): pass class RequiredBase(absltest.TestCase): foo: str @classmethod def setUpClass(cls): super(RequiredBase, cls).setUpClass() cls.foo = 'foo' class Right(RequiredBase): @classmethod def setUpClass(cls): super(Right, cls).setUpClass() # Test will fail unless Left.setUpClass() follows mro properly # Right.setUpClass() class Subclass(Left, Right): @classmethod def setUpClass(cls): super(Subclass, cls).setUpClass() class Test(Subclass): pass Test.setUpClass() self.assertEqual(Test.foo, 'foo') def test_skip_class(self): @absltest.skipThisClass('reason') class BaseTest(absltest.TestCase): def test_foo(self): _ = 1 / 0 class Test(BaseTest): def test_foo(self): self.assertEqual(1, 1) with self.subTest('base class'): ts = unittest.makeSuite(BaseTest) self.assertEqual(1, ts.countTestCases()) res = unittest.TestResult() ts.run(res) self.assertTrue(res.wasSuccessful()) self.assertLen(res.skipped, 1) self.assertEqual(0, res.testsRun) self.assertEmpty(res.failures) self.assertEmpty(res.errors) with self.subTest('real test'): ts = unittest.makeSuite(Test) self.assertEqual(1, ts.countTestCases()) res = unittest.TestResult() ts.run(res) self.assertTrue(res.wasSuccessful()) self.assertEqual(1, res.testsRun) self.assertEmpty(res.skipped) self.assertEmpty(res.failures) self.assertEmpty(res.errors) def test_skip_class_unittest(self): @absltest.skipThisClass('reason') class Test(unittest.TestCase): # note: unittest not absltest def test_foo(self): _ = 1 / 0 ts = unittest.makeSuite(Test) self.assertEqual(1, ts.countTestCases()) res = unittest.TestResult() ts.run(res) self.assertTrue(res.wasSuccessful()) self.assertLen(res.skipped, 1) self.assertEqual(0, res.testsRun) self.assertEmpty(res.failures) self.assertEmpty(res.errors) class ExitCodeTest(BaseTestCase): def test_exits_5_when_no_tests(self): expect_success = sys.version_info < (3, 12) _, _, exit_code = self.run_helper( None, [], {}, expect_success=expect_success, helper_name='absltest_test_helper_skipped', ) if not expect_success: self.assertEqual(exit_code, 5) def test_exits_5_when_all_skipped(self): self.run_helper( None, [], {'ABSLTEST_TEST_HELPER_DEFINE_CLASS': '1'}, expect_success=True, helper_name='absltest_test_helper_skipped', ) def _listdir_recursive(path): for dirname, _, filenames in os.walk(path): yield dirname for filename in filenames: yield os.path.join(dirname, filename) def _env_for_command_tests(): if os.name == 'nt' and 'PATH' in os.environ: # get_command_stderr and assertCommandXXX don't inherit environment # variables by default. This makes sure msys commands can be found on # Windows. return {'PATH': os.environ['PATH']} else: return None if __name__ == '__main__': absltest.main()