2 # ============LICENSE_START====================================================
3 # org.onap.vvp/validation-scripts
4 # ===================================================================
5 # Copyright © 2019 AT&T Intellectual Property. All rights reserved.
6 # ===================================================================
8 # Unless otherwise specified, all software contained herein is licensed
9 # under the Apache License, Version 2.0 (the "License");
10 # you may not use this software except in compliance with the License.
11 # You may obtain a copy of the License at
13 # http://www.apache.org/licenses/LICENSE-2.0
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
23 # Unless otherwise specified, all documentation contained herein is licensed
24 # under the Creative Commons License, Attribution 4.0 Intl. (the "License");
25 # you may not use this documentation except in compliance with the License.
26 # You may obtain a copy of the License at
28 # https://creativecommons.org/licenses/by/4.0/
30 # Unless required by applicable law or agreed to in writing, documentation
31 # distributed under the License is distributed on an "AS IS" BASIS,
32 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33 # See the License for the specific language governing permissions and
34 # limitations under the License.
36 # ============LICENSE_END============================================
43 import subprocess #nosec
47 from flake8.main.application import Application
49 from update_reqs import get_requirements
51 THIS_DIR = os.path.dirname(os.path.abspath(__file__))
52 CURRENT_NEEDS_PATH = os.path.join(THIS_DIR, "ice_validator/heat_requirements.json")
55 def run_pytest(*args, msg="pytest failed"):
56 original_dir = os.getcwd()
58 os.chdir(os.path.join(THIS_DIR, "ice_validator"))
59 if pytest.main(list(args)) != 0:
62 os.chdir(original_dir)
67 PATH = os.path.join(THIS_DIR, "ice_validator/output/traceability.csv")
74 with open(self.PATH, "r") as f:
76 next(rows) # skip header
77 self.mappings = list(rows)
79 def unmapped_requirement_errors(self):
81 Returns list of errors where a requirement is testable, but no test was found.
83 testable_mappings = [m for m in self.mappings if m[self.IS_TESTABLE] == "True"]
85 f"Missing test for {m[self.REQ_ID]}"
86 for m in testable_mappings
87 if not m[self.TEST_NAME]
90 def mapped_non_testable_requirement_errors(self):
92 Returns list of errors where the requirement isn't testable, but a test was
95 non_testables = [m for m in self.mappings if m[self.IS_TESTABLE] == "False"]
98 f"No test for {m[0]} is needed, but found: "
99 f"{m[self.TEST_FILE]}::{m[self.TEST_NAME]} "
101 for m in non_testables
106 def current_version(needs):
107 """Extracts and returns the needs under the current version"""
108 return needs["versions"][needs["current_version"]]["needs"]
111 def in_scope(_, req_metadata):
113 Checks if requirement is relevant to VVP.
116 :param req_metadata: needs metadata about the requirement
117 :return: True if the requirement is a testable, Heat requirement
120 "Heat" in req_metadata.get("docname", "")
121 and "MUST" in req_metadata.get("keyword", "").upper()
122 and req_metadata.get("validation_mode", "").lower() != "none"
126 def select_items(predicate, source_dict):
128 Creates a new dict from the source dict where the items match the given predicate
129 :param predicate: predicate function that must accept a two arguments (key & value)
130 :param source_dict: input dictionary to select from
131 :return: filtered dict
133 return {k: v for k, v in source_dict.items() if predicate(k, v)}
136 def check_requirements_up_to_date():
138 Checks if the requirements file packaged with VVP has meaningful differences
139 to the requirements file published from VNFRQTS.
140 :return: list of errors found
142 msg = ["heat_requirements.json is out-of-date. Run update_reqs.py to update."]
143 latest_needs = json.load(get_requirements())
144 with open(CURRENT_NEEDS_PATH, "r") as f:
145 current_needs = json.load(f)
146 latest_reqs = select_items(in_scope, current_version(latest_needs))
147 current_reqs = select_items(in_scope, current_version(current_needs))
148 if set(latest_reqs.keys()) != set(current_reqs.keys()):
151 latest["description"] == current_reqs[r_id]["description"]
152 for r_id, latest in latest_reqs.items()
158 def check_app_tests_pass():
159 return run_pytest("tests", "--self-test",
160 msg="app_tests failed. Run pytest app_tests and fix errors.")
163 def check_self_test_pass():
164 return run_pytest("tests", "--self-test",
165 msg="self-test failed. Run pytest --self-test and fix errors.")
168 def check_testable_requirements_are_mapped():
169 tracing = Traceability()
170 return tracing.unmapped_requirement_errors()
173 def check_non_testable_requirements_are_not_mapped():
174 tracing = Traceability()
175 return tracing.mapped_non_testable_requirement_errors()
178 def check_flake8_passes():
179 output = io.StringIO()
180 with contextlib.redirect_stdout(output), contextlib.redirect_stderr(output):
182 app.run(["ice_validator"])
184 lines = [f" {l}" for l in output.readlines()]
185 return ["flake8 errors detected:"] + lines if lines else []
188 def check_bandit_passes():
189 result = subprocess.run( #nosec
190 ["bandit", "-c", "bandit.yaml", "-r", ".", "-x", "./.tox/**"], #nosec
191 encoding="utf-8", #nosec
192 stdout=subprocess.PIPE, #nosec
193 stderr=subprocess.PIPE, #nosec
195 msgs = result.stdout.split("\n") if result.returncode != 0 else []
196 return ["bandit errors detected:"] + [f" {e}" for e in msgs] if msgs else []
199 if __name__ == "__main__":
201 check_self_test_pass,
202 check_requirements_up_to_date,
203 check_testable_requirements_are_mapped,
204 check_non_testable_requirements_are_not_mapped,
208 results = [check() for check in checks]
209 errors = "\n".join("\n".join(msg) for msg in results if msg)
210 print(errors or "Everything looks good!")
211 sys.exit(1 if errors else 0)