Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14.0] [ADD] tests_summary #3124

Open
wants to merge 1 commit into
base: 14.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions setup/tests_summary/odoo/addons/tests_summary
6 changes: 6 additions & 0 deletions setup/tests_summary/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
94 changes: 94 additions & 0 deletions tests_summary/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
============
Test Summary
============

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:126364c9ac7a093bc9b07176ff3b3cf0026146b57743ec5486d79788f96cd1e7
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github
:target: https://github.com/OCA/server-tools/tree/14.0/tests_summary
:alt: OCA/server-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-tools-14-0/server-tools-14-0-tests_summary
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=14.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Add this module to your ``server_wide_modules`` list in your odoo.conf
file to enable the tests summary feature.

At the end of the tests, a summary will be displayed in the logs for
quick review of the test failures grouped by module.

The summary will look like this:

|Tests Summary Tests Summary|

.. |Tests Summary Tests Summary| image:: https://raw.githubusercontent.com/OCA/server-tools/14.0/tests_summary/static/description/tests_summary.png

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20tests_summary%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Akretion

Contributors
------------

- Florian Mounier [email protected]

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-paradoxxxzero| image:: https://github.com/paradoxxxzero.png?size=40px
:target: https://github.com/paradoxxxzero
:alt: paradoxxxzero

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-paradoxxxzero|

This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/14.0/tests_summary>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
165 changes: 165 additions & 0 deletions tests_summary/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Florian Mounier <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from unittest.mock import patch
from collections import defaultdict
import logging
import re
import os

from odoo.tools import config
from odoo.service.server import preload_registries
from odoo.modules.registry import Registry


class Colorize:
colors = ("black", "red", "green", "yellow", "blue", "magenta", "cyan", "white")

def __init__(self, logger):
self.active = os.name == "posix" and all(
isinstance(handler, logging.StreamHandler)
and hasattr(handler.stream, "fileno")
and os.isatty(handler.stream.fileno())
for handler in logger.handlers
)

def bold(self, text):
if not self.active:
return text
return f"\033[1m{text}\033[0m"

Check warning on line 30 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L29-L30

Added lines #L29 - L30 were not covered by tests

def color(self, text, color, bold=False):
if not self.active:
return text
return (

Check warning on line 35 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L34-L35

Added lines #L34 - L35 were not covered by tests
f"\033[{30 + self.colors.index(color)}{';1' if bold else ''}m{text}\033[0m"
)

def __getattr__(self, attr):
if attr in self.colors:
return lambda text, bold=False: self.color(text, attr, bold)
return super().__getattr__(attr)

Check warning on line 42 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L42

Added line #L42 was not covered by tests


def maybe_pluralize(n, singular, plural=None):
return f"{n} {singular if n == 1 else (plural or singular + 's')}"

Check warning on line 46 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L46

Added line #L46 was not covered by tests


module_re = re.compile(r"odoo\.addons\.(\w+)")


def get_module(test):
if hasattr(test, "test_module"):
return test.test_module

Check warning on line 54 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L54

Added line #L54 was not covered by tests
for key in ("description", "test_description", "test_id"):
if hasattr(test, key):
match = module_re.search(getattr(test, key))

Check warning on line 57 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L57

Added line #L57 was not covered by tests
if match:
return match.group(1)
return "unknown (%s)" % repr(test)

Check warning on line 60 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L59-L60

Added lines #L59 - L60 were not covered by tests


def get_test_name(test):
if hasattr(test, "_testMethodName"):
name = ""

Check warning on line 65 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L65

Added line #L65 was not covered by tests
if hasattr(test, "test_class"):
name += test.test_class + "."
name += test._testMethodName
return name

Check warning on line 69 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L67-L69

Added lines #L67 - L69 were not covered by tests
for key in ("description", "test_description", "test_id"):
if getattr(test, key, False):
return getattr(test, key)
return "unknown (%s)" % repr(test)

Check warning on line 73 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L72-L73

Added lines #L72 - L73 were not covered by tests


def preload_registries_and_display_test_results(dbnames):
_logger = logging.getLogger(__name__)
rc = preload_registries(dbnames)
c = Colorize(_logger)
types = {

Check warning on line 80 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L77-L80

Added lines #L77 - L80 were not covered by tests
"errors": "error",
"failures": "failure",
"skipped": "skip",
"expectedFailures": "expected failure",
"unexpectedSuccesses": "unexpected success",
}
colors = {

Check warning on line 87 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L87

Added line #L87 was not covered by tests
"errors": "red",
"failures": "red",
"skipped": "yellow",
"expectedFailures": "green",
"unexpectedSuccesses": "cyan",
}
for db in dbnames:
report = Registry.registries[db]._assertion_report

Check warning on line 95 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L95

Added line #L95 was not covered by tests

modules_infos = defaultdict(list)

Check warning on line 97 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L97

Added line #L97 was not covered by tests
for type_ in types:
for test_info in getattr(report, type_):
test, info = (

Check warning on line 100 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L100

Added line #L100 was not covered by tests
test_info
if isinstance(test_info, tuple) and len(test_info) == 2
else (test_info, "Success")
)
modules_infos[get_module(test)].append(

Check warning on line 105 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L105

Added line #L105 was not covered by tests
{"type": type_, "info": info, "test": test}
)

message = "\n\n" + c.bold(f"Database {db}: ") + f"{report.testsRun} tests run"

Check warning on line 109 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L109

Added line #L109 was not covered by tests
for type_, type_name in types.items():
if getattr(report, type_):
message += ", " + c.color(

Check warning on line 112 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L112

Added line #L112 was not covered by tests
maybe_pluralize(len(getattr(report, type_)), type_name),
colors[type_],
True,
)

if len(modules_infos):
message += f" in {maybe_pluralize(len(modules_infos), 'module')}."

Check warning on line 119 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L119

Added line #L119 was not covered by tests

message += "\n\n"

Check warning on line 121 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L121

Added line #L121 was not covered by tests

for module, infos in modules_infos.items():
message += c.black("+" + "-" * (len(module) + 2) + "+", True) + "\n"
message += c.black("| ", True) + c.bold(module) + c.black(" |", True) + "\n"
message += c.black("+" + "-" * (len(module) + 2) + "+", True) + "\n"

Check warning on line 126 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L124-L126

Added lines #L124 - L126 were not covered by tests
for type_, type_name in types.items():
type_infos = [info for info in infos if info["type"] == type_]
if type_infos:
message += (

Check warning on line 130 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L130

Added line #L130 was not covered by tests
c.color(
maybe_pluralize(len(type_infos), type_name) + ":",
colors[type_],
True,
)
+ "\n"
)
for info in type_infos:
message += (

Check warning on line 139 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L139

Added line #L139 was not covered by tests
c.black(
" - "
+ (
(info["test"].__module__.split(".")[-1] + ":")
if getattr(info["test"], "__module__", False)
else ""
),
True,
)
+ c.bold(get_test_name(info["test"]) + ":")
+ "\n"
)
message += info["info"].rstrip("\n") + "\n\n"

Check warning on line 152 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L152

Added line #L152 was not covered by tests

getattr(_logger, "error" if report.errors or report.failures else "info")(

Check warning on line 154 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L154

Added line #L154 was not covered by tests
message
)

return rc

Check warning on line 158 in tests_summary/__init__.py

View check run for this annotation

Codecov / codecov/patch

tests_summary/__init__.py#L158

Added line #L158 was not covered by tests


if config["test_enable"]:
patch(
"odoo.service.server.preload_registries",
preload_registries_and_display_test_results,
).start()
17 changes: 17 additions & 0 deletions tests_summary/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Florian Mounier <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "Test Summary",
"version": "14.0.1.0.0",
"author": "Akretion,Odoo Community Association (OCA)",
"summary": "Odoo patch to display a summary of failed tests at the end "
"of odoo startup",
"website": "https://github.com/OCA/server-tools",
"maintainers": ["paradoxxxzero"],
"license": "AGPL-3",
"category": "Generic Modules",
"depends": [],
"data": [],
}
1 change: 1 addition & 0 deletions tests_summary/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Florian Mounier <[email protected]>
9 changes: 9 additions & 0 deletions tests_summary/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Add this module to your `server_wide_modules` list in your odoo.conf file to enable the
tests summary feature.

At the end of the tests, a summary will be displayed in the logs for quick review of the
test failures grouped by module.

The summary will look like this:

![Tests Summary Tests Summary](../static/description/tests_summary.png)
Loading
Loading