From 7486af92e4d52a8c88aaaedeeebc8fa55143e8d0 Mon Sep 17 00:00:00 2001 From: Faisal Alsrheed <47912291+Faisal-Alsrheed@users.noreply.github.com> Date: Sat, 16 Sep 2023 01:26:59 +0300 Subject: [PATCH] Increase tests in `utils` (#880) * Increase tests in utils + Fix Bug in `count_loc` * Increase tests in utils + Fix Bug in `count_loc` * Increase tests in utils + Fix Bug in `count_loc` * Increase tests in utils + Fix Bug in `count_loc` * Increase tests in utils + Fix Bug in count_loc * Increase tests in utils * Increase tests in utils * Increase tests in `utils` * Increase tests in utils * Increase tests in utils * Increase tests in `utils` * Increase tests in utils * Increase tests in utils * Increase tests in utils * Increase tests in `utils`+ Improve `to_snake_case` * Increase tests in `utils`+ Improve `to_snake_case` * Increase tests in `utils`+ Improve `to_snake_case` * Increase tests in `utils` * Increase tests in `utils` * Increase tests in `utils` --- keras_core/utils/code_stats_test.py | 142 ++++++++++++++++++++++++++++ keras_core/utils/io_utils_test.py | 57 +++++++++++ keras_core/utils/naming_test.py | 99 +++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 keras_core/utils/code_stats_test.py create mode 100644 keras_core/utils/io_utils_test.py diff --git a/keras_core/utils/code_stats_test.py b/keras_core/utils/code_stats_test.py new file mode 100644 index 000000000..512303abc --- /dev/null +++ b/keras_core/utils/code_stats_test.py @@ -0,0 +1,142 @@ +import os +import sys +from io import StringIO + +from keras_core.testing import test_case +from keras_core.utils.code_stats import count_loc + + +class TestCountLoc(test_case.TestCase): + def setUp(self): + self.test_dir = "test_directory" + os.makedirs(self.test_dir, exist_ok=True) + + def tearDown(self): + for root, dirs, files in os.walk(self.test_dir, topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + for name in dirs: + os.rmdir(os.path.join(root, name)) + + def create_file(self, filename, content): + with open( + os.path.join(self.test_dir, filename), "w", encoding="utf-8" + ) as f: + f.write(content) + + def test_count_loc_valid_python(self): + self.create_file( + "sample.py", "# This is a test file\n\nprint('Hello')\n" + ) + loc = count_loc(self.test_dir) + self.assertEqual(loc, 1) + + def test_exclude_test_files(self): + self.create_file("sample_test.py", "print('Hello')\n") + loc = count_loc(self.test_dir, exclude=("_test",)) + self.assertEqual(loc, 0) + + def test_other_extensions(self): + self.create_file("sample.txt", "Hello\n") + loc = count_loc(self.test_dir, extensions=(".py",)) + self.assertEqual(loc, 0) + + def test_comment_lines(self): + self.create_file( + "sample.py", "# Comment\nprint('Hello')\n# Another comment\n" + ) + loc = count_loc(self.test_dir) + self.assertEqual(loc, 1) + + def test_empty_file(self): + self.create_file("empty.py", "") + loc = count_loc(self.test_dir) + self.assertEqual(loc, 0) + + def test_whitespace_only(self): + self.create_file("whitespace.py", " \n\t\n") + loc = count_loc(self.test_dir) + self.assertEqual(loc, 0) + + def test_inline_comments_after_code(self): + content = 'print("Hello") # This is an inline comment' + self.create_file("inline_comment_sample.py", content) + loc = count_loc(self.test_dir) + self.assertEqual(loc, 1) # The comment shouldn't affect the count + + def test_directory_structure(self): + content1 = 'print("Hello from file1")' + content2 = 'print("Hello from file2")' + os.mkdir(os.path.join(self.test_dir, "subdir")) + self.create_file("sample1.py", content1) + self.create_file(os.path.join("subdir", "sample2.py"), content2) + loc = count_loc(self.test_dir) + self.assertEqual(loc, 2) # Both files should be counted + + def test_normal_directory_name(self): + content = 'print("Hello from a regular directory")' + os.makedirs(os.path.join(self.test_dir, "some_test_dir")) + self.create_file(os.path.join("some_test_dir", "sample.py"), content) + loc = count_loc(self.test_dir) + self.assertEqual(loc, 1) # Should count normally + + def test_exclude_directory_name(self): + content = 'print("Hello from an excluded directory")' + os.makedirs(os.path.join(self.test_dir, "dir_test")) + self.create_file(os.path.join("dir_test", "sample.py"), content) + loc = count_loc(self.test_dir) + self.assertEqual(loc, 0) + # Shouldn't count the file in dir_test due to the exclusion pattern + + def test_verbose_output(self): + content = 'print("Hello")' + self.create_file("sample.py", content) + original_stdout = sys.stdout + sys.stdout = StringIO() + count_loc(self.test_dir, verbose=1) + output = sys.stdout.getvalue() + sys.stdout = original_stdout + self.assertIn("Count LoCs in", output) + + def test_multiline_string_same_line(self): + content = '''"""This is a multiline string ending on the same line""" + print("Outside string")''' + self.create_file("same_line_multiline.py", content) + loc = count_loc(self.test_dir) + self.assertEqual(loc, 1) # Only the print statement should count + + def test_multiline_string_ends_on_same_line(self): + content = '"""a multiline string end on same line"""\nprint("Outstr")' + self.create_file("same_line_multiline.py", content) + loc = count_loc(self.test_dir) + self.assertEqual(loc, 1) # Only the print statement should count + + def test_multiline_string_ends_in_middle_of_line(self): + content = '''print("Start") + """This is a multiline string ending in the middle of a line""" + """This is another multiline string.""" + print("End")''' + self.create_file("multiline_in_middle.py", content) + loc = count_loc(self.test_dir) + self.assertEqual(loc, 2) # Both print statements should count + + def test_line_starting_with_triple_quotes_not_ending(self): + content = '"""\nThis is a multiline string\n' + self.create_file("test_file_2.py", content) + path = os.path.join(self.test_dir, "test_file_2.py") + self.assertEqual(count_loc(path), 0) + # Because it's part of a multiline string + + def test_line_starting_and_ending_with_triple_quotes(self): + content = '"""This is a one-liner docstring."""\n' + self.create_file("test_file_3.py", content) + path = os.path.join(self.test_dir, "test_file_3.py") + self.assertEqual(count_loc(path), 0) + # This is still considered a comment/docstring + + def test_string_open_true_line_starting_with_triple_quotes(self): + content = '"""\nEnd of the multiline string."""\n' + self.create_file("test_file_4.py", content) + path = os.path.join(self.test_dir, "test_file_4.py") + self.assertEqual(count_loc(path), 0) + # Entire content is a multiline string/comment diff --git a/keras_core/utils/io_utils_test.py b/keras_core/utils/io_utils_test.py new file mode 100644 index 000000000..bf57f5ca2 --- /dev/null +++ b/keras_core/utils/io_utils_test.py @@ -0,0 +1,57 @@ +from unittest.mock import patch + +from keras_core.testing import test_case +from keras_core.utils import io_utils + + +class TestIoUtils(test_case.TestCase): + def test_enable_interactive_logging(self): + io_utils.enable_interactive_logging() + self.assertTrue(io_utils.is_interactive_logging_enabled()) + + def test_disable_interactive_logging(self): + io_utils.disable_interactive_logging() + self.assertFalse(io_utils.is_interactive_logging_enabled()) + + def test_set_logging_verbosity_valid(self): + valid_levels = ["FATAL", "ERROR", "WARNING", "INFO", "DEBUG"] + for level in valid_levels: + io_utils.set_logging_verbosity(level) + + def test_set_logging_verbosity_invalid(self): + with self.assertRaises(ValueError): + io_utils.set_logging_verbosity("INVALID") + + @patch("builtins.input", side_effect=["y"]) + def test_ask_to_proceed_with_overwrite_yes(self, _): + self.assertTrue(io_utils.ask_to_proceed_with_overwrite("test_path")) + + @patch("builtins.input", side_effect=["n"]) + def test_ask_to_proceed_with_overwrite_no(self, _): + self.assertFalse(io_utils.ask_to_proceed_with_overwrite("test_path")) + + @patch("sys.stdout.write") + def test_print_msg_interactive_with_line_break(self, mock_write): + io_utils.enable_interactive_logging() + io_utils.print_msg("Hello", line_break=True) + mock_write.assert_called_once_with("Hello\n") + + @patch("sys.stdout.write") + def test_print_msg_interactive_without_line_break(self, mock_write): + io_utils.enable_interactive_logging() + io_utils.print_msg("Hello", line_break=False) + mock_write.assert_called_once_with("Hello") + + @patch("absl.logging.info") + def test_print_msg_non_interactive(self, mock_logging): + io_utils.disable_interactive_logging() + io_utils.print_msg("Hello") + mock_logging.assert_called_once_with("Hello") + + @patch("builtins.input", side_effect=["invalid", "invalid", "y"]) + def test_ask_to_proceed_with_overwrite_invalid_then_yes(self, _): + self.assertTrue(io_utils.ask_to_proceed_with_overwrite("test_path")) + + @patch("builtins.input", side_effect=["invalid", "n"]) + def test_ask_to_proceed_with_overwrite_invalid_then_no(self, _): + self.assertFalse(io_utils.ask_to_proceed_with_overwrite("test_path")) diff --git a/keras_core/utils/naming_test.py b/keras_core/utils/naming_test.py index b6e9552a9..40e9fa8fe 100644 --- a/keras_core/utils/naming_test.py +++ b/keras_core/utils/naming_test.py @@ -3,6 +3,11 @@ class NamingUtilsTest(test_case.TestCase): + def test_uniquify_unique_name(self): + name = "the_unique_name" + unique_name = naming.uniquify(name) + self.assertEqual(unique_name, name) + def test_auto_name(self): self.assertEqual(naming.auto_name("unique_name"), "unique_name") self.assertEqual(naming.auto_name("unique_name"), "unique_name_1") @@ -12,3 +17,97 @@ def test_get_uid(self): self.assertEqual(naming.get_uid("very_unique_name"), 1) self.assertEqual(naming.get_uid("very_unique_name"), 2) self.assertEqual(naming.get_uid("very_unique_name"), 3) + + def test_uniquify_non_unique_name(self): + name = "non_unique_name" + naming.uniquify(name) + unique_name = naming.uniquify(name) + self.assertEqual(unique_name, name + "_1") + + def test_to_snake_case_snake_case_name(self): + name = "snake_case_name" + snake_case_name = naming.to_snake_case(name) + self.assertEqual(snake_case_name, name) + + def test_get_uid_existing_prefix(self): + prefix = "existing_prefix" + naming.get_uid(prefix) + uid = naming.get_uid(prefix) + self.assertEqual(uid, 2) + + def test_reset_uids(self): + naming.get_uid("unique_name") + naming.reset_uids() + uid = naming.get_uid("unique_name") + self.assertEqual(uid, 1) + + def test_get_object_name_no_name_attribute(self): + class ObjectWithoutName: + __name__ = "ObjectWithoutName" + + obj = ObjectWithoutName() + object_name = naming.get_object_name(obj) + self.assertEqual(object_name, "object_without_name") + + def test_get_object_name_no_name_or_class_attribute(self): + class ObjectWithoutNameOrClass: + pass + + obj = ObjectWithoutNameOrClass() + object_name = naming.get_object_name(obj) + self.assertEqual(object_name, "object_without_name_or_class") + + def test_uniquify_already_uniquified_name(self): + name = "unique_name" + unique_name = naming.uniquify(name) + new_unique_name = naming.uniquify(unique_name) + self.assertEqual(new_unique_name, unique_name) + + def test_to_snake_case_capital_after_any_character(self): + name = "myVariableNameHere" + snake_case_name = naming.to_snake_case(name) + self.assertEqual(snake_case_name, "my_variable_name_here") + + def test_to_snake_case_lower_before_upper(self): + name = "convertTHIS" + snake_case_name = naming.to_snake_case(name) + self.assertEqual(snake_case_name, "convert_this") + + def test_to_snake_case_already_snake_cased(self): + name = "already_snake_cased" + snake_case_name = naming.to_snake_case(name) + self.assertEqual(snake_case_name, name) + + def test_to_snake_case_no_changes(self): + name = "lowercase" + snake_case_name = naming.to_snake_case(name) + self.assertEqual(snake_case_name, name) + + def test_to_snake_case_single_uppercase_word(self): + name = "UPPERCASE" + snake_case_name = naming.to_snake_case(name) + self.assertEqual(snake_case_name, "uppercase") + + def test_get_object_name_for_keras_objects(self): + class MockKerasObject: + name = "mock_object" + + obj = MockKerasObject() + result = naming.get_object_name(obj) + self.assertEqual( + result, "mock_object", f"Expected 'mock_object' but got {result}" + ) + + # Test for function objects that have a `__name__` attribute. + def test_get_object_name_for_functions(self): + def mock_function(): + pass + + result = naming.get_object_name(mock_function) + # Assumes to_snake_case works correctly. + expected_name = naming.to_snake_case(mock_function.__name__) + self.assertEqual( + result, + expected_name, + f"Expected '{expected_name}' but got {result}", + )