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

Update possible certification status values for Job units and modify its default (new) #1204

Merged
merged 11 commits into from
Sep 3, 2024
2 changes: 1 addition & 1 deletion checkbox-ng/checkbox_ng/launcher/merge_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def _populate_session_state(self, job, state):
"category_id", "com.canonical.plainbox::uncategorised"
)
job_state.effective_certification_status = job.get_record_value(
"certification_status", "unspecified"
"certification_status", "non-blocker"
)

def _create_exporter(self, exporter_id):
Expand Down
2 changes: 1 addition & 1 deletion checkbox-ng/checkbox_ng/launcher/subcommands.py
pieqq marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -1378,7 +1378,7 @@ def get_effective_certification_status(self, unit):
return value
if hasattr(unit, "certification_status"):
return unit.certification_status
return "unspecified"
return "non-blocker"


class ListBootstrapped:
Expand Down
32 changes: 20 additions & 12 deletions checkbox-ng/checkbox_ng/launcher/test_merge_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,22 @@
# You should have received a copy of the GNU General Public License
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.

from unittest import TestCase, mock
from unittest import TestCase
from unittest.mock import patch, MagicMock
from functools import partial

from checkbox_ng.launcher.merge_reports import MergeReports


class MergeReportsTests(TestCase):
@mock.patch("checkbox_ng.launcher.merge_reports.TemporaryDirectory")
@mock.patch("checkbox_ng.launcher.merge_reports.SessionManager")
@mock.patch("checkbox_ng.launcher.merge_reports.JobDefinition")
@mock.patch("checkbox_ng.launcher.merge_reports.CategoryUnit")
@mock.patch("builtins.print")
@mock.patch("os.path.join")
@mock.patch("tarfile.open")
@mock.patch("json.load")
@patch("checkbox_ng.launcher.merge_reports.TemporaryDirectory")
@patch("checkbox_ng.launcher.merge_reports.SessionManager")
@patch("checkbox_ng.launcher.merge_reports.JobDefinition")
@patch("checkbox_ng.launcher.merge_reports.CategoryUnit")
@patch("builtins.print")
@patch("os.path.join")
@patch("tarfile.open")
@patch("json.load")
# used to load an empty launcher with no error
def test_invoked_ok(
self,
Expand All @@ -43,11 +44,11 @@ def test_invoked_ok(
session_manager_mock,
temp_dir_mock,
):
ctx_mock = mock.MagicMock()
ctx_mock = MagicMock()
ctx_mock.args.submission = ["submission"]
ctx_mock.args.output_file = "file_location"

self_mock = mock.MagicMock()
self_mock = MagicMock()
self_mock._parse_submission = partial(
MergeReports._parse_submission, self_mock
)
Expand All @@ -65,11 +66,18 @@ def test_invoked_ok(
}
json_mock.return_value = sub_to_read

with mock.patch("builtins.open"):
with patch("builtins.open"):
MergeReports.invoked(self_mock, ctx_mock)

# output path was printed
print_mock.assert_any_call(ctx_mock.args.output_file)
exporter = self_mock._create_exporter.return_value
# exporter was created and dumped
self.assertTrue(exporter.dump_from_session_manager_list.called)

def test_populate_session_state(self):
job_mock = MagicMock()
state_mock = MagicMock()
self_mock = MagicMock()
MergeReports._populate_session_state(self_mock, job_mock, state_mock)
self.assertTrue(job_mock.get_record_value.called)
6 changes: 3 additions & 3 deletions checkbox-ng/checkbox_ng/launcher/test_subcommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ def setUp(self):
"summary": "fake-job1",
"plugin": "manual",
"description": "fake-description1",
"certification_status": "unspecified",
"certification_status": "non-blocker",
},
id="namespace1::test-job1",
partial_id="test-job1",
Expand All @@ -745,7 +745,7 @@ def setUp(self):
"summary": "fake-job2",
"plugin": "shell",
"command": "ls",
"certification_status": "unspecified",
"certification_status": "non-blocker",
},
id="namespace2::test-job2",
partial_id="test-job2",
Expand Down Expand Up @@ -903,7 +903,7 @@ def test_get_effective_certificate_status(self):
)
self.assertEqual(
self.launcher.get_effective_certification_status(template1),
"unspecified",
"non-blocker",
)


Expand Down
61 changes: 2 additions & 59 deletions checkbox-ng/plainbox/impl/exporter/test_html.py
Hook25 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -162,23 +162,12 @@ def _get_all_exporter_units(self):

def prepare_manager_without_certification_status(self):
return self._get_session_manager(
"unspecified", "unspecified", "unspecified"
"non-blocker", "non-blocker", "non-blocker"
)

def prepare_manager_with_certification_blocker(self):
return self._get_session_manager(
"blocker", "unspecified", "unspecified"
)

def prepare_manager_with_certification_non_blocker(self):
return self._get_session_manager(
"non-blocker", "unspecified", "unspecified"
)

def prepare_manager_with_both_certification_status(self):
self.session_state.update_job_result(self.job2, self.result_fail)
return self._get_session_manager(
"blocker", "non-blocker", "unspecified"
"blocker", "non-blocker", "non-blocker"
)

def test_perfect_match_without_certification_status(self):
Expand Down Expand Up @@ -226,49 +215,3 @@ def test_perfect_match_with_certification_blocker(self):
"test-data/html-exporter/with_certification_blocker.html",
) # unintuitively, resource_string returns bytes
self.assertEqual(actual_result, expected_result)

def test_perfect_match_with_certification_non_blocker(self):
"""
Test that output from the exporter exactly matches known
good HTML output, inlining and everything included.
"""
exporter = Jinja2SessionStateExporter(
system_id="",
timestamp="2012-12-21T12:00:00",
client_version="Checkbox 1.0",
exporter_unit=self.exporter_unit,
)
stream = io.BytesIO()
exporter.dump_from_session_manager(
self.prepare_manager_with_certification_non_blocker(), stream
)
actual_result = stream.getvalue() # This is bytes
self.assertIsInstance(actual_result, bytes)
expected_result = resource_string(
"plainbox",
"test-data/html-exporter/with_certification_non_blocker.html",
) # unintuitively, resource_string returns bytes
self.assertEqual(actual_result, expected_result)

def test_perfect_match_with_both_certification_status(self):
"""
Test that output from the exporter exactly matches known
good HTML output, inlining and everything included.
"""
exporter = Jinja2SessionStateExporter(
system_id="",
timestamp="2012-12-21T12:00:00",
client_version="Checkbox 1.0",
exporter_unit=self.exporter_unit,
)
stream = io.BytesIO()
exporter.dump_from_session_manager(
self.prepare_manager_with_both_certification_status(), stream
)
actual_result = stream.getvalue() # This is bytes
self.assertIsInstance(actual_result, bytes)
expected_result = resource_string(
"plainbox",
"test-data/html-exporter/with_both_certification_status.html",
) # unintuitively, resource_string returns bytes
self.assertEqual(actual_result, expected_result)
4 changes: 2 additions & 2 deletions checkbox-ng/plainbox/impl/exporter/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ def test_all_at_once(self):
("requires", 'job_b.ready == "yes"'),
("command", "echo testing && true"),
("io_log", ["dGVzdGluZwo="]),
("certification_status", "unspecified"),
("certification_status", "non-blocker"),
]
),
"job_b": OrderedDict(
Expand All @@ -278,7 +278,7 @@ def test_all_at_once(self):
("plugin", "resource"),
("command", "echo ready: yes"),
("io_log", ["cmVhZHk6IHllcwo="]),
("certification_status", "unspecified"),
("certification_status", "non-blocker"),
]
),
},
Expand Down
106 changes: 106 additions & 0 deletions checkbox-ng/plainbox/impl/exporter/test_xlsx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# This file is part of Checkbox.
#
# Copyright 2024 Canonical Ltd.
# Written by:
# Pierre Equoy <pierre.equoy@canonical.com>
#
# Checkbox is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3,
# as published by the Free Software Foundation.
#
# Checkbox is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.

from collections import OrderedDict
from unittest import TestCase
from unittest.mock import MagicMock, ANY

from plainbox.impl.exporter.xlsx import XLSXSessionStateExporter
from plainbox.impl.session import SessionState
from plainbox.impl.unit.job import JobDefinition
from plainbox.impl.unit.category import CategoryUnit


class XLSXSessionStateExporterTests(TestCase):
def test_write_job(self):
self_mock = MagicMock()
tree = {"job1": {}}
result_map = {
"job1": OrderedDict(
[
("summary", "job1"),
("category_id", "com.canonical.plainbox::uncategorised"),
("outcome", "pass"),
("plugin", "shell"),
("io_log", ""),
("comments", ""),
("command", 'echo "This is job1"'),
("certification_status", "non-blocker"),
]
),
"Uncategorised": {
"category_status": "pass",
"plugin": "local",
"summary": "Uncategorised",
},
}
XLSXSessionStateExporter._write_job(self_mock, tree, result_map, 2)
self_mock.worksheet3.write.assert_any_call(ANY, ANY, "", ANY)

tree = {"Uncategorised": {"job1": {}}}
XLSXSessionStateExporter._write_job(self_mock, tree, result_map, 2)
self.assertTrue(self_mock._write_job.called)

def test_category_map(self):
self_mock = MagicMock()
A = JobDefinition({"id": "A", "category_id": "test"})
B = JobDefinition({"id": "B", "certification-status": "blocker"})
job_list = [A, B]
unit = MagicMock(name="unit", spec_set=CategoryUnit)
unit.id = "test"
unit.tr_name.return_value = "Test"
unit.Meta.name = "category"

state = SessionState(job_list)
state.update_desired_job_list(job_list)
state.unit_list.append(unit)

self.assertEqual(
XLSXSessionStateExporter._category_map(self_mock, state),
{"test": "Test"},
)

def test_write_tp_export(self):
self_mock = MagicMock()
self_mock._category_map.return_value = {
"com.canonical.plainbox::uncategorised": "test"
}
data = MagicMock()
A = JobDefinition({"id": "A"})
B = JobDefinition({"id": "B", "certification-status": "blocker"})
job_list = [A, B]
unit = MagicMock(name="unit", spec_set=CategoryUnit)
unit.Meta.name = "category"

state = SessionState(job_list)
data["manager"].default_device_context.state = state
XLSXSessionStateExporter.write_tp_export(self_mock, data)
self_mock.worksheet4.write_row.assert_called_with(
ANY, 0, ["test", "", ""], ANY
)
state.update_desired_job_list(job_list)
state.unit_list.append(unit)

data["manager"].default_device_context.state = state
XLSXSessionStateExporter.write_tp_export(self_mock, data)
self_mock.worksheet4.write_row.assert_any_call(
ANY, 0, ["A", "", "A"], ANY
)
self_mock.worksheet4.write_row.assert_any_call(
ANY, 0, ["B", "blocker", "B"], ANY
)
37 changes: 18 additions & 19 deletions checkbox-ng/plainbox/impl/exporter/xlsx.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ def _write_job(self, tree, result_map, max_level, level=0):
cert_status = ""
if "certification_status" in result_map[job]:
cert_status = result_map[job]["certification_status"]
if cert_status == "unspecified":
if cert_status == "non-blocker":
cert_status = ""
self.worksheet3.write(
self._lineno,
Expand Down Expand Up @@ -877,24 +877,23 @@ def write_results(self, data):
)
self.worksheet3.autofilter(5, max_level, self._lineno, max_level + 3)

def write_tp_export(self, data):
def _category_map(state):
"""Map from category id to their corresponding translated names."""
wanted_category_ids = frozenset(
{
job_state.effective_category_id
for job_state in state.job_state_map.values()
if job_state.job in state.run_list
and job_state.job.plugin not in ("resource", "attachment")
}
)
return {
unit.id: unit.tr_name()
for unit in state.unit_list
if unit.Meta.name == "category"
and unit.id in wanted_category_ids
def _category_map(self, state):
"""Map from category id to their corresponding translated names."""
wanted_category_ids = frozenset(
{
job_state.effective_category_id
for job_state in state.job_state_map.values()
if job_state.job in state.run_list
and job_state.job.plugin not in ("resource", "attachment")
}
)
return {
unit.id: unit.tr_name()
for unit in state.unit_list
if unit.Meta.name == "category" and unit.id in wanted_category_ids
}

def write_tp_export(self, data):
self.worksheet4.set_header(
"&C{}".format(data["manager"].test_plans[0])
)
Expand All @@ -912,7 +911,7 @@ def _category_map(state):
self.worksheet4.repeat_rows(0)
self._lineno = 0
state = data["manager"].default_device_context.state
cat_map = _category_map(state)
cat_map = self._category_map(state)
run_list_ids = [job.id for job in state.run_list]
for cat_id in sorted(cat_map, key=lambda x: cat_map[x].casefold()):
self._lineno += 1
Expand All @@ -931,7 +930,7 @@ def _category_map(state):
certification_status = (
job_state.effective_certification_status
)
if certification_status == "unspecified":
if certification_status == "non-blocker":
certification_status = ""
description = job_state.job.description
if not description:
Expand Down
Loading
Loading