Skip to content

Commit b2fd1b2

Browse files
authored
Fix crash in ManifestBrowser when only hidden manifests are present (BugFix) (#1996)
* Fix crash in ManifestBrowser when only hidden manifests are present. * The manifest screen is not shown even in interactive mode if all the manifests are hidde. * Comply the code with Python 3.5. * Adjust tests by putting them in a proper test classes + comments. Reduce code duplication. * Rename ManifestBrowser.get_flattened_values, apply the same fix for _save_manifest in RemoteController, make tests more readable.
1 parent ad85ae7 commit b2fd1b2

File tree

6 files changed

+354
-64
lines changed

6 files changed

+354
-64
lines changed

checkbox-ng/checkbox_ng/launcher/controller.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -628,24 +628,20 @@ def _save_manifest(self, interactive):
628628
if not manifest_repr:
629629
_logger.info("Skipping saving of the manifest")
630630
return
631-
if interactive:
631+
632+
if interactive and ManifestBrowser.has_visible_manifests(
633+
manifest_repr
634+
):
632635
# Ask the user the values
633636
to_save_manifest = ManifestBrowser(
634637
"System Manifest:", manifest_repr
635638
).run()
636639
else:
637-
# Use the one provided in repr
638-
# repr is question : [manifests]
639-
# manifest ex m1 is [conf_m1_1, conf_m1_2, ...]
640-
# here we recover [conf_m1_1, conf_m1_2, ..., conf_m2_1, ...]
641-
all_preconf = (
642-
conf
643-
for conf_list in manifest_repr.values()
644-
for conf in conf_list
640+
# Use the one provided in repr (either non-interactive or no visible manifests)
641+
to_save_manifest = ManifestBrowser.get_flattened_values(
642+
manifest_repr
645643
)
646-
to_save_manifest = {
647-
conf["id"]: conf["value"] for conf in all_preconf
648-
}
644+
649645
self.sa.save_manifest_json(json.dumps(to_save_manifest))
650646

651647
def select_jobs(self, all_jobs):

checkbox-ng/checkbox_ng/launcher/subcommands.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -692,21 +692,20 @@ def _save_manifest(self, interactive):
692692
if not manifest_repr:
693693
_logger.info("Skipping saving of the manifest")
694694
return
695-
if interactive:
695+
696+
if interactive and ManifestBrowser.has_visible_manifests(
697+
manifest_repr
698+
):
696699
# Ask the user the values
697700
to_save_manifest = ManifestBrowser(
698701
"System Manifest:", manifest_repr
699702
).run()
700703
else:
701-
# Use the one provided in repr
702-
# repr is question : [manifests]
703-
# manifest ex m1 is [conf_m1_1, conf_m1_2, ...]
704-
# here we recover [conf_m1_1, conf_m1_2, ..., conf_m2_1, ...]
705-
to_save_manifest = {
706-
conf["id"]: conf["value"]
707-
for conf_list in manifest_repr.values()
708-
for conf in conf_list
709-
}
704+
# Use the one provided in repr (either non-interactive or no visible manifests)
705+
to_save_manifest = ManifestBrowser.get_flattened_values(
706+
manifest_repr
707+
)
708+
710709
self.ctx.sa.save_manifest(to_save_manifest)
711710

712711
def _pick_jobs_to_run(self):

checkbox-ng/checkbox_ng/launcher/test_controller.py

Lines changed: 80 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from checkbox_ng.launcher.controller import is_hostname_a_loopback
2929

3030

31-
class ControllerTests(TestCase):
31+
class TestRemoteController(TestCase):
3232
@mock.patch("checkbox_ng.launcher.controller.is_hostname_a_loopback")
3333
@mock.patch("time.time")
3434
@mock.patch("builtins.print")
@@ -1063,54 +1063,98 @@ def test_start_session_runtime_error(self):
10631063
with self.assertRaises(SystemExit) as _:
10641064
RemoteController.start_session(self_mock)
10651065

1066-
def test__save_manifest_no_repr(self):
1067-
self_mock = mock.MagicMock()
1068-
self_mock.sa.get_manifest_repr_json.return_value = "{}"
1069-
RemoteController._save_manifest(self_mock, False)
1070-
10711066
@mock.patch("checkbox_ng.launcher.controller.ManifestBrowser")
1072-
def test__save_manifest_interactive(self, manifest_browser_mock):
1073-
self_mock = mock.MagicMock()
1074-
manifest_repr = {"Question 1": [{"id": "conf1", "value": "val1"}]}
1075-
self_mock.sa.get_manifest_repr_json.return_value = json.dumps(
1076-
manifest_repr
1077-
)
1078-
to_save_manifest = {"conf1": "new_val"}
1079-
manifest_browser_mock.return_value.run.return_value = to_save_manifest
1067+
def test__save_manifest_interactive_with_visible_manifests(
1068+
self, mock_browser_class
1069+
):
1070+
controller = RemoteController()
1071+
sa_mock = mock.MagicMock()
1072+
controller._sa = sa_mock
10801073

1081-
RemoteController._save_manifest(self_mock, True)
1074+
manifest_repr = {
1075+
"section1": [
1076+
{"id": "visible1", "value": 0, "hidden": False},
1077+
{"id": "visible2", "value": False, "hidden": False},
1078+
]
1079+
}
1080+
sa_mock.get_manifest_repr_json.return_value = json.dumps(manifest_repr)
10821081

1083-
manifest_browser_mock.assert_called_once_with(
1084-
"System Manifest:", manifest_repr
1085-
)
1086-
self_mock.sa.save_manifest_json.assert_called_once_with(
1087-
json.dumps(to_save_manifest)
1082+
mock_browser = mock.MagicMock()
1083+
mock_browser.run.return_value = {
1084+
"visible1": 5,
1085+
"visible2": True,
1086+
}
1087+
mock_browser_class.return_value = mock_browser
1088+
mock_browser_class.has_visible_manifests.return_value = True
1089+
1090+
controller._save_manifest(interactive=True)
1091+
1092+
sa_mock.save_manifest_json.assert_called_with(
1093+
json.dumps({"visible1": 5, "visible2": True})
10881094
)
10891095

1090-
def test__save_manifest_non_interactive(self):
1091-
self_mock = mock.MagicMock()
1096+
@mock.patch("checkbox_ng.launcher.controller.ManifestBrowser")
1097+
def test__save_manifest_interactive_no_visible_manifests(
1098+
self, mock_browser_class
1099+
):
1100+
controller = RemoteController()
1101+
sa_mock = mock.MagicMock()
1102+
controller._sa = sa_mock
1103+
10921104
manifest_repr = {
1093-
"Question 1": [
1094-
{"id": "conf1", "value": "val1"},
1095-
{"id": "conf2", "value": "val2"},
1096-
],
1097-
"Question 2": [{"id": "conf3", "value": "val3"}],
1105+
"section1": [
1106+
{"id": "hidden1", "value": True, "hidden": True},
1107+
{"id": "hidden2", "value": 2, "hidden": True},
1108+
]
1109+
}
1110+
sa_mock.get_manifest_repr_json.return_value = json.dumps(manifest_repr)
1111+
mock_browser_class.has_visible_manifests.return_value = False
1112+
mock_browser_class.get_flattened_values.return_value = {
1113+
"hidden1": True,
1114+
"hidden2": 2,
10981115
}
1099-
self_mock.sa.get_manifest_repr_json.return_value = json.dumps(
1100-
manifest_repr
1116+
1117+
controller._save_manifest(interactive=True)
1118+
1119+
self.assertEqual(mock_browser_class.call_count, 0)
1120+
self.assertEqual(
1121+
mock_browser_class.has_visible_manifests.call_count, 1
1122+
)
1123+
self.assertEqual(mock_browser_class.get_flattened_values.call_count, 1)
1124+
self.assertEqual(sa_mock.save_manifest_json.call_count, 1)
1125+
sa_mock.save_manifest_json.assert_called_with(
1126+
json.dumps({"hidden1": True, "hidden2": 2})
11011127
)
11021128

1103-
RemoteController._save_manifest(self_mock, False)
1129+
@mock.patch("checkbox_ng.launcher.controller.ManifestBrowser")
1130+
def test__save_manifest_non_interactive(self, mock_browser_class):
1131+
controller = RemoteController()
1132+
sa_mock = mock.MagicMock()
1133+
controller._sa = sa_mock
11041134

1105-
expected_to_save = {
1106-
"conf1": "val1",
1107-
"conf2": "val2",
1108-
"conf3": "val3",
1135+
manifest_repr = {
1136+
"section1": [
1137+
{"id": "manifest1", "value": False, "hidden": False},
1138+
{"id": "manifest2", "value": 7, "hidden": True},
1139+
]
1140+
}
1141+
sa_mock.get_manifest_repr_json.return_value = json.dumps(manifest_repr)
1142+
mock_browser_class.get_flattened_values.return_value = {
1143+
"manifest1": False,
1144+
"manifest2": 7,
11091145
}
1110-
self_mock.sa.save_manifest_json.assert_called_once_with(
1111-
json.dumps(expected_to_save)
1146+
1147+
controller._save_manifest(interactive=False)
1148+
1149+
sa_mock.save_manifest_json.assert_called_with(
1150+
json.dumps({"manifest1": False, "manifest2": 7})
11121151
)
11131152

1153+
def test__save_manifest_no_repr(self):
1154+
self_mock = mock.MagicMock()
1155+
self_mock.sa.get_manifest_repr_json.return_value = "{}"
1156+
RemoteController._save_manifest(self_mock, False)
1157+
11141158
def test_select_jobs_forced_with_manifest(self):
11151159
self_mock = mock.MagicMock()
11161160
self_mock.launcher.get_value.return_value = True

checkbox-ng/checkbox_ng/launcher/test_subcommands.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
get_testplan_id_by_id,
4444
print_objs,
4545
)
46+
from checkbox_ng.urwid_ui import ManifestBrowser
4647

4748

4849
class TestSharedFunctions(TestCase):
@@ -780,6 +781,104 @@ def test_invoked_resume(self, load_config_mock):
780781

781782
Launcher.invoked(self_mock, ctx_mock)
782783

784+
def test__save_manifest_no_or_empty_manifest_repr(self):
785+
launcher = Launcher()
786+
ctx_mock = MagicMock()
787+
launcher.ctx = ctx_mock
788+
789+
cases = [
790+
("None", None),
791+
("Empty", {}),
792+
]
793+
794+
for case_name, manifest_repr in cases:
795+
with self.subTest(case=case_name):
796+
ctx_mock.sa.get_manifest_repr.return_value = manifest_repr
797+
launcher._save_manifest(interactive=True)
798+
self.assertEqual(ctx_mock.sa.save_manifest.call_count, 0)
799+
800+
@patch("checkbox_ng.launcher.subcommands.ManifestBrowser")
801+
def test__save_manifest_interactive_with_visible_manifests(
802+
self, mock_browser_class
803+
):
804+
805+
launcher = Launcher()
806+
ctx_mock = MagicMock()
807+
launcher.ctx = ctx_mock
808+
809+
manifest_repr = {
810+
"section1": [
811+
{"id": "visible1", "value": 0, "hidden": False},
812+
{"id": "visible2", "value": False, "hidden": False},
813+
]
814+
}
815+
ctx_mock.sa.get_manifest_repr.return_value = manifest_repr
816+
817+
mock_browser = MagicMock()
818+
mock_browser.run.return_value = {
819+
"visible1": 5,
820+
"visible2": True,
821+
}
822+
mock_browser_class.return_value = mock_browser
823+
mock_browser_class.has_visible_manifests.return_value = True
824+
825+
launcher._save_manifest(interactive=True)
826+
827+
ctx_mock.sa.save_manifest.assert_called_with(
828+
{"visible1": 5, "visible2": True}
829+
)
830+
831+
@patch("checkbox_ng.launcher.subcommands.ManifestBrowser")
832+
def test__save_manifest_interactive_no_visible_manifests(
833+
self, mock_browser_class
834+
):
835+
launcher = Launcher()
836+
ctx_mock = MagicMock()
837+
launcher.ctx = ctx_mock
838+
839+
manifest_repr = {
840+
"section1": [
841+
{"id": "hidden1", "value": True, "hidden": True},
842+
{"id": "hidden2", "value": 2, "hidden": True},
843+
]
844+
}
845+
ctx_mock.sa.get_manifest_repr.return_value = manifest_repr
846+
mock_browser_class.has_visible_manifests.return_value = False
847+
mock_browser_class.get_flattened_values.return_value = {
848+
"hidden1": True,
849+
"hidden2": 2,
850+
}
851+
852+
launcher._save_manifest(interactive=True)
853+
854+
ctx_mock.sa.save_manifest.assert_called_with(
855+
{"hidden1": True, "hidden2": 2}
856+
)
857+
858+
@patch("checkbox_ng.launcher.subcommands.ManifestBrowser")
859+
def test__save_manifest_non_interactive(self, mock_browser_class):
860+
launcher = Launcher()
861+
ctx_mock = MagicMock()
862+
launcher.ctx = ctx_mock
863+
864+
manifest_repr = {
865+
"section1": [
866+
{"id": "manifest1", "value": False, "hidden": False},
867+
{"id": "manifest2", "value": 7, "hidden": True},
868+
]
869+
}
870+
ctx_mock.sa.get_manifest_repr.return_value = manifest_repr
871+
mock_browser_class.get_flattened_values.return_value = {
872+
"manifest1": False,
873+
"manifest2": 7,
874+
}
875+
876+
launcher._save_manifest(interactive=False)
877+
878+
ctx_mock.sa.save_manifest.assert_called_with(
879+
{"manifest1": False, "manifest2": 7}
880+
)
881+
783882

784883
@patch("os.makedirs", new=MagicMock())
785884
class TestLauncherReturnCodes(TestCase):

0 commit comments

Comments
 (0)