Repository: jackdewinter/pyscan

Issue:
Add proper column support.
When using project_summarizer on certain platforms, the columns in the report goes weird, causing the program to fail.

Need to add a `-quiet` mode and a `-columns X` mode for controlling the columns.

Discussion:

Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 60f7a4f..6a2112d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -3,10 +3,10 @@ name: Main
 on:
   push:
     branches:
-      - main
+      - master
   pull_request:
     branches:
-      - main
+      - master
   schedule:
     - cron: '0 0 * * *'  # daily at midnight
 
diff --git a/changelog.md b/changelog.md
index 9e1b8be..f03ca35 100644
--- a/changelog.md
+++ b/changelog.md
@@ -8,7 +8,8 @@
 
 ### Changed
 
-- None
+- [Issue 15](https://github.com/jackdewinter/pyscan/issues/15)
+  - added better control of number of columns on display or quiet mode for no display
 
 ### Fixed
 
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..b81610f
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,2 @@
+codecov:
+  token: 9ddb9b0b-aed4-428b-9222-26a7072656f5
\ No newline at end of file
diff --git a/project_summarizer/cobertura_plugin.py b/project_summarizer/cobertura_plugin.py
index 73e2a89..34f6759 100644
--- a/project_summarizer/cobertura_plugin.py
+++ b/project_summarizer/cobertura_plugin.py
@@ -75,7 +75,7 @@ class CoberturaPlugin(ProjectSummarizerPlugin):
             else "Unknown"
         )
 
-    def generate_report(self, only_changes, report_file):
+    def generate_report(self, only_changes, column_width, report_file):
         """
         Generate the report and display it.
         """
@@ -90,14 +90,18 @@ class CoberturaPlugin(ProjectSummarizerPlugin):
         )
         self.save_summary_file(self.__output_path, new_stats, "test coverage")
 
-        published_coverage_summary_path = (
-            ProjectSummarizerPlugin.compute_published_path_to_file(self.__output_path)
-        )
-        loaded_stats = self.__load_coverage_results_summary_file(
-            published_coverage_summary_path
-        )
-
-        self.__report_coverage_files(new_stats, loaded_stats, only_changes)
+        if column_width:
+            published_coverage_summary_path = (
+                ProjectSummarizerPlugin.compute_published_path_to_file(
+                    self.__output_path
+                )
+            )
+            loaded_stats = self.__load_coverage_results_summary_file(
+                published_coverage_summary_path
+            )
+            self.__report_coverage_files(
+                new_stats, loaded_stats, only_changes, column_width
+            )
 
     def __compose_summary_from_cobertura_document(
         self, cobertura_document, coverage_provider_name
@@ -183,7 +187,9 @@ class CoberturaPlugin(ProjectSummarizerPlugin):
             test_totals = CoverageTotals.from_dict(results_dictionary)
         return test_totals
 
-    def __report_coverage_files(self, new_stats, loaded_stats, only_report_changes):
+    def __report_coverage_files(
+        self, new_stats, loaded_stats, only_report_changes, column_width
+    ):
         """
         Create a report on coverage.
         """
@@ -204,6 +210,7 @@ class CoberturaPlugin(ProjectSummarizerPlugin):
                 headers=hdrs,
                 no_borders=True,
                 justify=["l", "r", "r", "r"],
+                terminal_width=column_width if column_width != -1 else None,
             )
             split_rows = table.split("\n")
             new_rows = [next_row.rstrip() for next_row in split_rows]
@@ -312,16 +319,14 @@ class CoberturaPlugin(ProjectSummarizerPlugin):
             column_value = next_row[primary_column_index].rjust(column_maximums[0], " ")
         if column_maximums[1] != 0:
             if next_row[primary_column_index + 1] == "-":
-                column_value = (
-                    column_value + " " + " ".rjust(column_maximums[1] + 2, " ")
-                )
+                column_value = (f'{column_value} ' + " ".rjust(column_maximums[1] + 2, " "))
             else:
                 partial_value = next_row[primary_column_index + 1]
                 has_changes = partial_value.startswith("+") or partial_value.startswith(
                     "-"
                 )
                 partial_value = partial_value.rjust(column_maximums[1], " ")
-                column_value = column_value + " (" + partial_value + ")"
+                column_value = f'{column_value} ({partial_value})'
         return column_value, has_changes
 
     @classmethod
@@ -384,8 +389,7 @@ class CoberturaPlugin(ProjectSummarizerPlugin):
                     f"{percentage_value:.2f}",
                 )
         else:
-            for _ in range(6):
-                new_row.append("-")
+            new_row.extend("-" for _ in range(6))
         return new_row
 
     # pylint: disable=unused-private-member
@@ -401,7 +405,7 @@ class CoberturaPlugin(ProjectSummarizerPlugin):
             and delta_value != "0.00"
             and not delta_value.startswith("-")
         ):
-            delta_value = "+" + delta_value
+            delta_value = f'+{delta_value}'
         new_row.append(delta_value)
 
     # pylint: enable=unused-private-member
diff --git a/project_summarizer/junit_plugin.py b/project_summarizer/junit_plugin.py
index 2d91a3f..34f9009 100644
--- a/project_summarizer/junit_plugin.py
+++ b/project_summarizer/junit_plugin.py
@@ -50,7 +50,7 @@ class JUnitPlugin(ProjectSummarizerPlugin):
             JUnitPlugin.__COMMAND_LINE_OPTION,
         )
 
-    def generate_report(self, only_changes, report_file):
+    def generate_report(self, only_changes, column_width, report_file):
         """
         Generate the report and display it.
         """
@@ -63,16 +63,21 @@ class JUnitPlugin(ProjectSummarizerPlugin):
         )
         self.save_summary_file(self.__output_path, new_stats, "test report")
 
-        published_test_summary_path = self.compute_published_path_to_file(
-            self.__output_path
-        )
-        loaded_stats, loaded_totals = self.load_test_results_summary_file(
-            published_test_summary_path
-        )
-
-        self.__report_test_files(
-            new_stats, new_totals, loaded_stats, loaded_totals, only_changes
-        )
+        if column_width:
+            published_test_summary_path = self.compute_published_path_to_file(
+                self.__output_path
+            )
+            loaded_stats, loaded_totals = self.load_test_results_summary_file(
+                published_test_summary_path
+            )
+            self.__report_test_files(
+                new_stats,
+                new_totals,
+                loaded_stats,
+                loaded_totals,
+                only_changes,
+                column_width,
+            )
 
     def __compose_summary_from_junit_document(self, junit_document):
         """
@@ -158,9 +163,15 @@ class JUnitPlugin(ProjectSummarizerPlugin):
             grand_totals = self.__build_totals(test_totals)
         return test_totals, grand_totals
 
-    # pylint: disable=too-many-arguments
+    # pylint: disable=too-many-arguments, too-many-locals
     def __report_test_files(
-        self, new_stats, new_totals, loaded_stats, loaded_totals, only_report_changes
+        self,
+        new_stats,
+        new_totals,
+        loaded_stats,
+        loaded_totals,
+        only_report_changes,
+        column_width,
     ):
         """
         Generate a report comparing the current stats with the loaded/previous stats.
@@ -202,9 +213,11 @@ class JUnitPlugin(ProjectSummarizerPlugin):
                     row_to_add, test_report_rows, only_report_changes
                 )
 
-        self.print_test_summary(test_report_rows, new_totals, loaded_totals)
+        self.print_test_summary(
+            test_report_rows, new_totals, loaded_totals, column_width
+        )
 
-    # pylint: enable=too-many-arguments
+    # pylint: enable=too-many-arguments, too-many-locals
 
     @classmethod
     def __generate_match_column(cls, value_to_display, delta_to_display):
@@ -310,13 +323,15 @@ class JUnitPlugin(ProjectSummarizerPlugin):
             next_row[column_index] = new_value
             if max_width_2 != 0:
                 if split_row[1] != "0":
-                    new_value = "(" + split_row[1] + ")"
+                    new_value = f"({split_row[1]})"
                     new_value = new_value.rjust(max_width_2, " ")
                 else:
                     new_value = "".rjust(max_width_2, " ")
-                next_row[column_index] = next_row[column_index] + " " + new_value
+                next_row[column_index] = f"{next_row[column_index]} {new_value}"
 
-    def print_test_summary(self, test_report_rows, new_totals, loaded_totals):
+    def print_test_summary(
+        self, test_report_rows, new_totals, loaded_totals, column_width
+    ):
         """
         Print the actual test summaries.
         """
@@ -341,6 +356,7 @@ class JUnitPlugin(ProjectSummarizerPlugin):
                 headers=hdrs,
                 no_borders=True,
                 justify=["l", "r", "r", "r"],
+                terminal_width=column_width if column_width != -1 else None,
             )
             split_rows = table.split("\n")
             new_rows = [next_row.rstrip() for next_row in split_rows]
diff --git a/project_summarizer/main.py b/project_summarizer/main.py
index 1a37200..56ad16e 100644
--- a/project_summarizer/main.py
+++ b/project_summarizer/main.py
@@ -12,11 +12,15 @@ from project_summarizer.junit_plugin import JUnitPlugin
 from project_summarizer.project_summarizer_plugin import ProjectSummarizerPlugin
 
 
+# pylint: disable=too-few-public-methods
 class ProjectSummarizer:
     """
     Class to provide for a simple summarization of relevant output files from a build.
     """
 
+    __MINIMUM_DISPLAY_COLUMNS = 50
+    __MAXIMUM_DISPLAY_COLUMNS = 200
+
     def __init__(self):
         self.__version_number = ProjectSummarizer.__get_semantic_version()
         self.test_summary_publish_path = ProjectSummarizerPlugin.SUMMARY_PUBLISH_PATH
@@ -31,15 +35,38 @@ class ProjectSummarizer:
         assert os.path.isabs(file_path)
         file_path = file_path.replace(os.sep, "/")
         last_index = file_path.rindex("/")
-        file_path = file_path[: last_index + 1] + "version.py"
+        file_path = f"{file_path[: last_index + 1]}version.py"
         version_meta = runpy.run_path(file_path)
         return version_meta["__version__"]
 
-    def __parse_arguments(self):
-        """
-        Handle any arguments for the program.
-        """
+    def __add_command_line_arguments_for_plugins(self, parser):
+        for next_plugin_instance in self.__available_plugins:
+            (
+                plugin_argument_name,
+                plugin_variable_name,
+            ) = next_plugin_instance.add_command_line_arguments(parser)
+            self.__plugin_argument_names[plugin_argument_name] = next_plugin_instance
+            self.__plugin_variable_names[plugin_argument_name] = plugin_variable_name
+
+    def __show_help_if_no_meaningful_arguments_found(self, args, parser):
+        if args.publish_summaries:
+            return
+
+        are_plugin_arguments_present = False
+        arguments_as_dictionary = vars(args)
+        for next_plugin_argument in self.__plugin_argument_names:
+            plugin_variable_name = self.__plugin_variable_names[next_plugin_argument]
+            assert plugin_variable_name in arguments_as_dictionary
+            argument_value = arguments_as_dictionary[plugin_variable_name]
+            are_plugin_arguments_present = bool(argument_value.strip())
+            if are_plugin_arguments_present:
+                break
+
+        if not are_plugin_arguments_present:
+            parser.print_help()
+            sys.exit(2)
 
+    def __parse_arguments(self):
         parser = argparse.ArgumentParser(
             description="Summarize Python files.", allow_abbrev=False
         )
@@ -47,17 +74,9 @@ class ProjectSummarizer:
         parser.add_argument(
             "--version",
             action="version",
-            version="%(prog)s " + self.__version_number,
+            version=f"%(prog)s {self.__version_number}",
         )
-
-        for next_plugin_instance in self.__available_plugins:
-            (
-                plugin_argument_name,
-                plugin_variable_name,
-            ) = next_plugin_instance.add_command_line_arguments(parser)
-            self.__plugin_argument_names[plugin_argument_name] = next_plugin_instance
-            self.__plugin_variable_names[plugin_argument_name] = plugin_variable_name
-
+        self.__add_command_line_arguments_for_plugins(parser)
         parser.add_argument(
             "--only-changes",
             dest="only_changes",
@@ -72,31 +91,41 @@ class ProjectSummarizer:
             default=False,
             help="publish",
         )
+        parser.add_argument(
+            "--quiet",
+            dest="quiet_mode",
+            action="store_true",
+            default=False,
+            help="quiet_mode",
+        )
+        parser.add_argument(
+            "--columns",
+            dest="display_columns",
+            action="store",
+            default=-1,
+            help="display_columns",
+            type=ProjectSummarizer.__verify_display_columns,
+        )
 
         args = parser.parse_args()
-        if not args.publish_summaries and not args.test_report_file:
-            are_plugin_arguments_present = False
-            arguments_as_dictionary = vars(args)
-            for next_plugin_argument in self.__plugin_argument_names:
-                plugin_variable_name = self.__plugin_variable_names[
-                    next_plugin_argument
-                ]
-                assert plugin_variable_name in arguments_as_dictionary
-                argument_value = arguments_as_dictionary[plugin_variable_name]
-                are_plugin_arguments_present = bool(argument_value.strip())
-                if are_plugin_arguments_present:
-                    break
-
-            if not are_plugin_arguments_present:
-                parser.print_help()
-                sys.exit(2)
+        self.__show_help_if_no_meaningful_arguments_found(args, parser)
         return args
 
-    def __publish_file(self, file_to_publish):
-        """
-        Publish the specified file to the set publish directory.
-        """
+    @staticmethod
+    def __verify_display_columns(argument):
+        argument_as_integer = int(argument)
+        if (
+            argument_as_integer < ProjectSummarizer.__MINIMUM_DISPLAY_COLUMNS
+            or argument_as_integer > ProjectSummarizer.__MAXIMUM_DISPLAY_COLUMNS
+        ):
+            raise ValueError(
+                f"Value '{argument}' is not an integer between "
+                + f"between {ProjectSummarizer.__MINIMUM_DISPLAY_COLUMNS} "
+                + f"and {ProjectSummarizer.__MAXIMUM_DISPLAY_COLUMNS}."
+            )
+        return argument_as_integer
 
+    def __publish_file(self, file_to_publish):
         if not os.path.exists(self.test_summary_publish_path):
             print(
                 f"Publish directory '{self.test_summary_publish_path}' does not exist.  Creating."
@@ -120,7 +149,7 @@ class ProjectSummarizer:
                 print(f"Publishing file '{file_to_publish}' failed ({ex}).")
                 sys.exit(1)
 
-    def publish_summaries(self):
+    def __publish_summaries(self):
         """
         Respond to a request to publish any existing summaries.
         """
@@ -139,19 +168,9 @@ class ProjectSummarizer:
         for plugin_output_path in valid_paths:
             self.__publish_file(plugin_output_path)
 
-    def main(self):
-        """
-        Main entrance point.
-        """
-        self.__available_plugins = [CoberturaPlugin(), JUnitPlugin()]
-
-        args = self.__parse_arguments()
-
-        if args.publish_summaries:
-            self.publish_summaries()
-            sys.exit(0)
-
+    def __create_summaries(self, args):
         arguments_as_dictionary = vars(args)
+        column_width = 0 if args.quiet_mode else args.display_columns
         for next_command_line_argument in sys.argv:
             if next_command_line_argument in self.__plugin_argument_names:
                 plugin_instance = self.__plugin_argument_names[
@@ -161,9 +180,26 @@ class ProjectSummarizer:
                     next_command_line_argument
                 ]
                 plugin_instance.generate_report(
-                    args.only_changes, arguments_as_dictionary[plugin_variable_name]
+                    args.only_changes,
+                    column_width,
+                    arguments_as_dictionary[plugin_variable_name],
                 )
 
+    def main(self):
+        """
+        Main entrance point.
+        """
+        self.__available_plugins = [CoberturaPlugin(), JUnitPlugin()]
+
+        args = self.__parse_arguments()
+
+        if args.publish_summaries:
+            self.__publish_summaries()
+            sys.exit(0)
+
+        self.__create_summaries(args)
+
 
 if __name__ == "__main__":
     ProjectSummarizer().main()
+# pylint: enable=too-few-public-methods
diff --git a/project_summarizer/project_summarizer_plugin.py b/project_summarizer/project_summarizer_plugin.py
index 4d5d366..3e4fa1a 100644
--- a/project_summarizer/project_summarizer_plugin.py
+++ b/project_summarizer/project_summarizer_plugin.py
@@ -26,18 +26,26 @@ class ProjectSummarizerPlugin(ABC):
     def get_output_path(self):
         """
         Get the output path for the reporting file.
+
+        Used as the destination for the report. Also called from `__publish_summaries`
+        to denote the files that need to be published.
         """
 
     @abstractmethod
     def add_command_line_arguments(self, parser):
         """
         Add a command line argument to denote the file to scan.
+
+        Called from function `__parse_arguments` when building the command line.
         """
 
     @abstractmethod
-    def generate_report(self, only_changes, report_file):
+    def generate_report(self, only_changes, column_width, report_file):
         """
         Generate the report and display it.
+
+        Called from function `__create_sumamries` to create the summary file and
+        display any results on the console.
         """
 
     @classmethod
diff --git a/publish/coverage.json b/publish/coverage.json
index b5ea835..7333265 100644
--- a/publish/coverage.json
+++ b/publish/coverage.json
@@ -2,12 +2,12 @@
     "projectName": "project_summarizer",
     "reportSource": "Coverage.py",
     "branchLevel": {
-        "totalMeasured": 186,
-        "totalCovered": 186
+        "totalMeasured": 192,
+        "totalCovered": 192
     },
     "lineLevel": {
-        "totalMeasured": 576,
-        "totalCovered": 576
+        "totalMeasured": 596,
+        "totalCovered": 596
     }
 }
 
diff --git a/publish/pylint_suppression.json b/publish/pylint_suppression.json
index 2e36620..0222aba 100644
--- a/publish/pylint_suppression.json
+++ b/publish/pylint_suppression.json
@@ -10,9 +10,12 @@
             "too-many-arguments": 1
         },
         "project_summarizer/junit_plugin.py": {
-            "too-many-arguments": 2
+            "too-many-arguments": 2,
+            "too-many-locals": 1
+        },
+        "project_summarizer/main.py": {
+            "too-few-public-methods": 1
         },
-        "project_summarizer/main.py": {},
         "project_summarizer/project_summarizer_plugin.py": {},
         "project_summarizer/test_results_model.py": {
             "too-many-arguments": 1
@@ -22,6 +25,8 @@
     "disables-by-name": {
         "unused-private-member": 1,
         "too-many-instance-attributes": 1,
-        "too-many-arguments": 4
+        "too-many-arguments": 4,
+        "too-many-locals": 1,
+        "too-few-public-methods": 1
     }
 }
\ No newline at end of file


Test patch:
diff --git a/publish/test-results.json b/publish/test-results.json
index 1376b0d..04482f3 100644
--- a/publish/test-results.json
+++ b/publish/test-results.json
@@ -20,7 +20,7 @@
         },
         {
             "name": "test.test_coverage_scenarios",
-            "totalTests": 12,
+            "totalTests": 14,
             "failedTests": 0,
             "errorTests": 0,
             "skippedTests": 0,
@@ -28,7 +28,7 @@
         },
         {
             "name": "test.test_main",
-            "totalTests": 4,
+            "totalTests": 8,
             "failedTests": 0,
             "errorTests": 0,
             "skippedTests": 0,
@@ -44,7 +44,7 @@
         },
         {
             "name": "test.test_results_scenarios",
-            "totalTests": 19,
+            "totalTests": 21,
             "failedTests": 0,
             "errorTests": 0,
             "skippedTests": 0,
diff --git a/test/test_coverage_scenarios.py b/test/test_coverage_scenarios.py
index 4ba720d..9ea461a 100644
--- a/test/test_coverage_scenarios.py
+++ b/test/test_coverage_scenarios.py
@@ -101,6 +101,135 @@ Test Coverage Summary
     )
 
 
+def test_summarize_simple_cobertura_report_with_reduced_columns(
+    create_publish_directory=False, temporary_work_directory=None
+):
+    """
+    Test the summarizing of a simple cobertura report with no previous summary.
+    """
+
+    # Arrange
+    executor = MainlineExecutor()
+    temporary_work_directory, report_directory, publish_directory = setup_directories(
+        create_publish_directory=create_publish_directory,
+        temporary_work_directory=temporary_work_directory,
+    )
+    cobertura_coverage_file = os.path.join(
+        temporary_work_directory.name, get_coverage_file_name()
+    )
+    copyfile(
+        os.path.join(executor.resource_directory, get_coverage_file_name()),
+        cobertura_coverage_file,
+    )
+    summary_coverage_file = os.path.join(report_directory, COVERAGE_SUMMARY_FILE_NAME)
+
+    suppplied_arguments = [
+        COBERTURA_COMMAND_LINE_FLAG,
+        cobertura_coverage_file,
+        "--columns",
+        "50",
+    ]
+
+    expected_output = """
+
+Test Coverage Summary
+---------------------
+
+
+  TYPE           COVERED  MEASURED    PERCENTAGE
+
+  Instructions  --        --        -----
+
+  Lines         10 (+10)  15 (+15)  66.67 (+66.6
+                                              7)
+  Branches       2 ( +2)   4 ( +4)  50.00 (+50.0
+                                              0)
+  Complexity    --        --        -----
+
+  Methods       --        --        -----
+
+  Classes       --        --        -----
+
+
+
+"""
+    expected_error = ""
+    expected_return_code = 0
+    expected_test_coverage_file = compose_coverage_summary_file()
+
+    # Act
+    execute_results = executor.invoke_main(
+        arguments=suppplied_arguments, cwd=temporary_work_directory.name
+    )
+
+    # Assert
+    execute_results.assert_results(
+        expected_output, expected_error, expected_return_code
+    )
+    execute_results.assert_resultant_file(
+        summary_coverage_file, expected_test_coverage_file
+    )
+    return (
+        executor,
+        temporary_work_directory,
+        publish_directory,
+        cobertura_coverage_file,
+    )
+
+
+def test_summarize_simple_cobertura_report_with_quiet(
+    create_publish_directory=False, temporary_work_directory=None
+):
+    """
+    Test the summarizing of a simple cobertura report with no previous summary.
+    """
+
+    # Arrange
+    executor = MainlineExecutor()
+    temporary_work_directory, report_directory, publish_directory = setup_directories(
+        create_publish_directory=create_publish_directory,
+        temporary_work_directory=temporary_work_directory,
+    )
+    cobertura_coverage_file = os.path.join(
+        temporary_work_directory.name, get_coverage_file_name()
+    )
+    copyfile(
+        os.path.join(executor.resource_directory, get_coverage_file_name()),
+        cobertura_coverage_file,
+    )
+    summary_coverage_file = os.path.join(report_directory, COVERAGE_SUMMARY_FILE_NAME)
+
+    suppplied_arguments = [
+        COBERTURA_COMMAND_LINE_FLAG,
+        cobertura_coverage_file,
+        "--quiet",
+    ]
+
+    expected_output = ""
+    expected_error = ""
+    expected_return_code = 0
+    expected_test_coverage_file = compose_coverage_summary_file()
+
+    # Act
+    execute_results = executor.invoke_main(
+        arguments=suppplied_arguments, cwd=temporary_work_directory.name
+    )
+
+    # Assert
+    execute_results.assert_results(
+        expected_output, expected_error, expected_return_code
+    )
+    execute_results.assert_resultant_file(
+        summary_coverage_file, expected_test_coverage_file
+    )
+    return (
+        executor,
+        temporary_work_directory,
+        publish_directory,
+        cobertura_coverage_file,
+    )
+
+
 def test_summarize_cobertura_report_with_bad_source():
     """
     Test to make sure that summarizing a test coverage file that does not exist.
diff --git a/test/test_main.py b/test/test_main.py
index 591ef83..3841a6c 100644
--- a/test/test_main.py
+++ b/test/test_main.py
@@ -16,18 +16,21 @@ def test_scanner_with_no_parameters():
 
     expected_return_code = 2
     expected_output = """usage: main.py [-h] [--version] [--cobertura path] [--junit path]
-               [--only-changes] [--publish]
+               [--only-changes] [--publish] [--quiet]
+               [--columns DISPLAY_COLUMNS]
 
 Summarize Python files.
 
 optional arguments:
-  -h, --help        show this help message and exit
-  --version         show program's version number and exit
-  --cobertura path  source file name for cobertura test coverage reporting
-  --junit path      source file name for junit test result reporting
-  --only-changes    only_changes
-  --publish         publish
-"""
+  -h, --help            show this help message and exit
+  --version             show program's version number and exit
+  --cobertura path      source file name for cobertura test coverage reporting
+  --junit path          source file name for junit test result reporting
+  --only-changes        only_changes
+  --publish             publish
+  --quiet               quiet_mode
+  --columns DISPLAY_COLUMNS
+                        display_columns"""
     expected_error = ""
 
     # Act
@@ -51,18 +54,21 @@ def test_scanner_with_no_parameters_through_module():
 
     expected_return_code = 2
     expected_output = """usage: __main.py__ [-h] [--version] [--cobertura path] [--junit path]
-                   [--only-changes] [--publish]
+                   [--only-changes] [--publish] [--quiet]
+                   [--columns DISPLAY_COLUMNS]
 
 Summarize Python files.
 
 optional arguments:
-  -h, --help        show this help message and exit
-  --version         show program's version number and exit
-  --cobertura path  source file name for cobertura test coverage reporting
-  --junit path      source file name for junit test result reporting
-  --only-changes    only_changes
-  --publish         publish
-"""
+  -h, --help            show this help message and exit
+  --version             show program's version number and exit
+  --cobertura path      source file name for cobertura test coverage reporting
+  --junit path          source file name for junit test result reporting
+  --only-changes        only_changes
+  --publish             publish
+  --quiet               quiet_mode
+  --columns DISPLAY_COLUMNS
+                        display_columns"""
     expected_error = ""
 
     # Act
@@ -86,18 +92,21 @@ def test_scanner_with_no_parameters_through_main():
 
     expected_return_code = 2
     expected_output = """usage: main.py [-h] [--version] [--cobertura path] [--junit path]
-               [--only-changes] [--publish]
+               [--only-changes] [--publish] [--quiet]
+               [--columns DISPLAY_COLUMNS]
 
 Summarize Python files.
 
 optional arguments:
-  -h, --help        show this help message and exit
-  --version         show program's version number and exit
-  --cobertura path  source file name for cobertura test coverage reporting
-  --junit path      source file name for junit test result reporting
-  --only-changes    only_changes
-  --publish         publish
-"""
+  -h, --help            show this help message and exit
+  --version             show program's version number and exit
+  --cobertura path      source file name for cobertura test coverage reporting
+  --junit path          source file name for junit test result reporting
+  --only-changes        only_changes
+  --publish             publish
+  --quiet               quiet_mode
+  --columns DISPLAY_COLUMNS
+                        display_columns"""
     expected_error = ""
 
     # Act
@@ -120,18 +129,120 @@ def test_scanner_with_dash_h():
 
     expected_return_code = 0
     expected_output = """usage: main.py [-h] [--version] [--cobertura path] [--junit path]
-               [--only-changes] [--publish]
+               [--only-changes] [--publish] [--quiet]
+               [--columns DISPLAY_COLUMNS]
 
 Summarize Python files.
 
 optional arguments:
-  -h, --help        show this help message and exit
-  --version         show program's version number and exit
-  --cobertura path  source file name for cobertura test coverage reporting
-  --junit path      source file name for junit test result reporting
-  --only-changes    only_changes
-  --publish         publish
-"""
+  -h, --help            show this help message and exit
+  --version             show program's version number and exit
+  --cobertura path      source file name for cobertura test coverage reporting
+  --junit path          source file name for junit test result reporting
+  --only-changes        only_changes
+  --publish             publish
+  --quiet               quiet_mode
+  --columns DISPLAY_COLUMNS
+                        display_columns"""
+    expected_error = ""
+
+    # Act
+    execute_results = scanner.invoke_main(arguments=supplied_arguments)
+
+    # Assert
+    execute_results.assert_results(
+        expected_output, expected_error, expected_return_code
+    )
+
+
+def test_scanner_with_dash_dash_quiet():
+    """
+    Test to make sure we get help if '--quiet' is supplied.
+    """
+
+    # Arrange
+    scanner = MainlineExecutor()
+    supplied_arguments = ["--quiet"]
+
+    expected_return_code = 2
+    expected_output = """usage: main.py [-h] [--version] [--cobertura path] [--junit path]
+               [--only-changes] [--publish] [--quiet]
+               [--columns DISPLAY_COLUMNS]
+
+Summarize Python files.
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version             show program's version number and exit
+  --cobertura path      source file name for cobertura test coverage reporting
+  --junit path          source file name for junit test result reporting
+  --only-changes        only_changes
+  --publish             publish
+  --quiet               quiet_mode
+  --columns DISPLAY_COLUMNS
+                        display_columns"""
+    expected_error = ""
+
+    # Act
+    execute_results = scanner.invoke_main(arguments=supplied_arguments)
+
+    # Assert
+    execute_results.assert_results(
+        expected_output, expected_error, expected_return_code
+    )
+
+
+def test_scanner_with_dash_dash_columns():
+    """
+    Test to make sure we get help if '--columns' is supplied without a number.
+    """
+
+    # Arrange
+    scanner = MainlineExecutor()
+    supplied_arguments = ["--columns"]
+
+    expected_return_code = 2
+    expected_output = ""
+    expected_error = """usage: main.py [-h] [--version] [--cobertura path] [--junit path]
+               [--only-changes] [--publish] [--quiet]
+               [--columns DISPLAY_COLUMNS]
+main.py: error: argument --columns: expected one argument"""
+
+    # Act
+    execute_results = scanner.invoke_main(arguments=supplied_arguments)
+
+    # Assert
+    execute_results.assert_results(
+        expected_output, expected_error, expected_return_code
+    )
+
+
+def test_scanner_with_dash_dash_columns_good_number():
+    """
+    Test to make sure we get help if '--columns' is supplied with a good number.
+    """
+
+    # Arrange
+    scanner = MainlineExecutor()
+    supplied_arguments = ["--columns", "100"]
+
+    expected_return_code = 2
+    expected_output = """usage: main.py [-h] [--version] [--cobertura path] [--junit path]
+               [--only-changes] [--publish] [--quiet]
+               [--columns DISPLAY_COLUMNS]
+
+Summarize Python files.
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version             show program's version number and exit
+  --cobertura path      source file name for cobertura test coverage reporting
+  --junit path          source file name for junit test result reporting
+  --only-changes        only_changes
+  --publish             publish
+  --quiet               quiet_mode
+  --columns DISPLAY_COLUMNS
+                        display_columns"""
     expected_error = ""
 
     # Act
@@ -141,3 +252,28 @@ optional arguments:
     execute_results.assert_results(
         expected_output, expected_error, expected_return_code
     )
+
+
+def test_scanner_with_dash_dash_columns_bad_number():
+    """
+    Test to make sure we get help if '--columns' is supplied with a bad number.
+    """
+
+    # Arrange
+    scanner = MainlineExecutor()
+    supplied_arguments = ["--columns", "20"]
+
+    expected_return_code = 2
+    expected_output = ""
+    expected_error = """usage: main.py [-h] [--version] [--cobertura path] [--junit path]
+               [--only-changes] [--publish] [--quiet]
+               [--columns DISPLAY_COLUMNS]
+main.py: error: argument --columns: invalid __verify_display_columns value: '20'"""
+
+    # Act
+    execute_results = scanner.invoke_main(arguments=supplied_arguments)
+
+    # Assert
+    execute_results.assert_results(
+        expected_output, expected_error, expected_return_code
+    )
diff --git a/test/test_results_scenarios.py b/test/test_results_scenarios.py
index b335886..7cde6a4 100644
--- a/test/test_results_scenarios.py
+++ b/test/test_results_scenarios.py
@@ -97,6 +97,108 @@ Test Results Summary
     return executor, temporary_work_directory, publish_directory, junit_test_file
 
 
+def test_summarize_simple_junit_report_with_reduced_columns(
+    create_publish_directory=False, temporary_work_directory=None
+):
+    """
+    Test the summarizing of a simple junit report with no previous summary with columns set.
+    """
+
+    # Arrange
+    executor = MainlineExecutor()
+    temporary_work_directory, report_directory, publish_directory = setup_directories(
+        create_publish_directory=create_publish_directory,
+        temporary_work_directory=temporary_work_directory,
+    )
+    junit_test_file = os.path.join(
+        temporary_work_directory.name, JUNIT_RESULTS_FILE_NAME
+    )
+    copyfile(
+        os.path.join(executor.resource_directory, JUNIT_RESULTS_FILE_NAME),
+        junit_test_file,
+    )
+    summary_result_file = os.path.join(report_directory, RESULTS_SUMMARY_FILE_NAME)
+
+    suppplied_arguments = [JUNIT_COMMAND_LINE_FLAG, junit_test_file, "--columns", "50"]
+
+    expected_output = """\
+
+Test Results Summary
+--------------------
+
+
+  CLASS NAME  TOTAL TEST  FAILED TES  SKIPPED TE
+                       S          TS         STS
+
+  test.test_      3 (+3)           0           0
+  scenarios
+  ---             -                -           -
+  TOTALS          3 (+3)           0           0
+
+"""
+    expected_error = ""
+    expected_return_code = 0
+    expected_test_results_file = compose_test_results(3)
+
+    # Act
+    execute_results = executor.invoke_main(
+        arguments=suppplied_arguments, cwd=temporary_work_directory.name
+    )
+
+    # Assert
+    execute_results.assert_results(
+        expected_output, expected_error, expected_return_code
+    )
+    execute_results.assert_resultant_file(
+        summary_result_file, expected_test_results_file
+    )
+    return executor, temporary_work_directory, publish_directory, junit_test_file
+
+
+def test_summarize_simple_junit_report_with_quiet(
+    create_publish_directory=False, temporary_work_directory=None
+):
+    """
+    Test the summarizing of a simple junit report with no previous summary with columns set.
+    """
+
+    # Arrange
+    executor = MainlineExecutor()
+    temporary_work_directory, report_directory, publish_directory = setup_directories(
+        create_publish_directory=create_publish_directory,
+        temporary_work_directory=temporary_work_directory,
+    )
+    junit_test_file = os.path.join(
+        temporary_work_directory.name, JUNIT_RESULTS_FILE_NAME
+    )
+    copyfile(
+        os.path.join(executor.resource_directory, JUNIT_RESULTS_FILE_NAME),
+        junit_test_file,
+    )
+    summary_result_file = os.path.join(report_directory, RESULTS_SUMMARY_FILE_NAME)
+
+    suppplied_arguments = [JUNIT_COMMAND_LINE_FLAG, junit_test_file, "--quiet"]
+
+    expected_output = ""
+    expected_error = ""
+    expected_return_code = 0
+    expected_test_results_file = compose_test_results(3)
+
+    # Act
+    execute_results = executor.invoke_main(
+        arguments=suppplied_arguments, cwd=temporary_work_directory.name
+    )
+
+    # Assert
+    execute_results.assert_results(
+        expected_output, expected_error, expected_return_code
+    )
+    execute_results.assert_resultant_file(
+        summary_result_file, expected_test_results_file
+    )
+    return executor, temporary_work_directory, publish_directory, junit_test_file
+
+
 def test_summarize_junit_report_with_bad_source():
     """
     Test to make sure that summarizing a test report file that does not exist.