blob: 40ab24b2a6b70bc21d4f87bc565e51f87ef59150 [file] [log] [blame]
#!/usr/bin/python3
#
# Copyright (C) 2020 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from build_log_simplifier import collapse_consecutive_blank_lines
from build_log_simplifier import collapse_tasks_having_no_output
from build_log_simplifier import extract_task_names
from build_log_simplifier import remove_unmatched_exemptions
from build_log_simplifier import suggest_missing_exemptions
from build_log_simplifier import normalize_paths
from build_log_simplifier import regexes_matcher
from build_log_simplifier import remove_control_characters
import re
def fail(message):
print(message)
exit(1)
def test_regexes_matcher_get_matching_regexes():
print("test_regexes_matcher_get_matching_regexes")
# For each of the given queries, we ask a regexes_matcher to identify which regexes
# match them, and compare to the right answer
queries = ["", "a", "aa", "aaa", "b", "bb", "ab", "c", "a*"]
regexes = ["a", "a*", "aa*", "b", "b*", "a*b"]
matcher = regexes_matcher(regexes)
for query in queries:
simple_matches = [regex for regex in regexes if re.compile(regex).fullmatch(query)]
fast_matches = matcher.get_matching_regexes(query)
if simple_matches != fast_matches:
fail("regexes_matcher returned incorrect results for '" + query + "'. Expected = " + str(simple_matches) + ", actual = " + str(fast_matches))
print("Query = '" + query + "', matching regexes = " + str(simple_matches))
def test_normalize_paths():
print("test_normalize_paths")
lines = [
"CHECKOUT=/usr/home/me/workspace",
"/usr/home/me/workspace/external/protobuf/somefile.h: somewarning",
"Building CXX object protobuf-target/CMakeFiles/libprotobuf.dir/usr/home/me/workspace/external/somefile.cc"
]
expected_normalized = [
"CHECKOUT=$CHECKOUT",
"$CHECKOUT/external/protobuf/somefile.h: somewarning",
"Building CXX object protobuf-target/CMakeFiles/libprotobuf.dir$CHECKOUT/external/somefile.cc"
]
actual = normalize_paths(lines)
if expected_normalized != actual:
fail("test_normalize_paths returned incorrect response.\n" +
"Input: " + str(lines) + "\n" +
"Output: " + str(actual) + "\n" +
"Expected output: " + str(expected_normalized)
)
def test_regexes_matcher_index_first_matching_regex():
print("test_regexes_matcher_index_first_matching_regex")
regexes = ["first", "double", "single", "double"]
matcher = regexes_matcher(regexes)
assert(matcher.index_first_matching_regex("first") == 0)
assert(matcher.index_first_matching_regex("double") == 1)
assert(matcher.index_first_matching_regex("single") == 2)
assert(matcher.index_first_matching_regex("absent") is None)
def test_detect_task_names():
print("test_detect_task_names")
lines = [
"> Task :one\n",
"some output\n",
"> Task :two\n",
"more output\n"
]
task_names = [":one", ":two"]
detected_names = extract_task_names(lines)
if detected_names != task_names:
fail("extract_task_names returned incorrect response\n" +
"Input : " + str(lines) + "\n" +
"Output : " + str(detected_names) + "\n" +
"Expected: " + str(task_names)
)
def test_remove_unmatched_exemptions():
print("test_remove_unmatched_exemptions")
lines = [
"task two message one",
"task four message one",
]
current_config = [
"# > Task :one",
"task one message one",
"# TODO(bug): remove this",
"# > Task :two",
"task two message one",
"# TODO(bug): remove this too",
"# > Task :three",
"task three message one",
"# > Task :four",
"task four message one",
"# TODO: maybe remove this too?",
"# > Task :five",
"task five message one"
]
expected_config = [
"# TODO(bug): remove this",
"# > Task :two",
"task two message one",
"# > Task :four",
"task four message one",
]
actual_updated_config = remove_unmatched_exemptions(lines, current_config)
if actual_updated_config != expected_config:
fail("test_remove_unmatched_exemptions gave incorrect response.\n\n" +
"Input log : " + str(lines) + "\n\n" +
"Input config : " + str(current_config) + "\n\n" +
"Expected output config: " + str(expected_config) + "\n\n" +
"Actual output config : " + str(actual_updated_config))
def test_suggest_missing_exemptions():
print("test_suggest_missing_exemptions")
lines = [
"> Task :one",
"task one message one",
"task one message two",
"> Task :two",
"task two message one",
"duplicate line",
"> Task :three",
"task three message one",
"duplicate line"
]
expect_config = [
"# > Task :one",
"task one message one",
"task one message two",
"# > Task :two",
"task two message one",
"duplicate line",
"# > Task :three",
"task three message one"
]
# generate config starting with nothing
validate_suggested_exemptions(lines, [], expect_config)
# remove one line from config, regenerate config, line should return
config2 = expect_config[:1] + expect_config[2:]
validate_suggested_exemptions(lines, config2, expect_config)
# if there is an existing config with the tasks in the other order, the tasks should stay in that order
# and the new line should be inserted after the previous matching line
config3 = [
"# > Task :two",
"task two message one",
"duplicate line",
"# > Task :one",
"task one message two",
"# > Task :three",
"task three message one"
]
expect_config3 = [
"# > Task :two",
"task two message one",
"duplicate line",
"# > Task :one",
"task one message one",
"task one message two",
"# > Task :three",
"task three message one"
]
validate_suggested_exemptions(lines, config3, expect_config3)
# also validate that "> Configure project" gets ignored too
config4 = [
"# > Configure project a",
"some warning"
]
lines4 = [
"> Configure project b",
"some warning"
]
expect_config4 = config4
validate_suggested_exemptions(lines4, config4, expect_config4)
def test_collapse_tasks_having_no_output():
print("test_collapse_tasks_having_no_output")
lines = [
"> Task :no-output1",
"> Task :some-output1",
"output1",
"> Task :empty-output",
"",
"> Task :blanks-around-output",
"",
"output inside blanks",
"",
"> Task :no-output2",
"> Task :no-output3",
"FAILURE: Build failed with an exception.\n"
]
expected = [
"> Task :some-output1",
"output1",
"> Task :blanks-around-output",
"",
"output inside blanks",
""
]
actual = collapse_tasks_having_no_output(lines)
if (actual != expected):
fail("collapse_tasks_having_no_output gave incorrect error.\n" +
"Expected: " + str(expected) + "\n" +
"Actual = " + str(actual))
def test_collapse_consecutive_blank_lines():
print("test_collapse_consecutive_blank_lines")
lines = [
"",
"> Task :a",
"",
" ",
"\n\n",
"> Task :b",
" ",
""
]
expected_collapsed = [
"> Task :a",
"",
"> Task :b",
" "
]
actual_collapsed = collapse_consecutive_blank_lines(lines)
if actual_collapsed != expected_collapsed:
fail("collapse_consecutive_blank_lines returned incorrect response.\n"
"Input: " + lines + "\n" +
"Output: " + actual_collapsed + "\n" +
"Expected output: " + expected_collapsed
)
def validate_suggested_exemptions(lines, config, expected_config):
suggested_config = suggest_missing_exemptions(lines, config)
if suggested_config != expected_config:
fail("suggest_missing_exemptions incorrect response.\n" +
"Lines: " + str(lines) + ",\n" +
"config: " + str(config) + ",\n" +
"expected suggestion: " + str(expected_config) + ",\n"
"actual suggestion : " + str(suggested_config))
def test_remove_control_characters():
print("test_remove_control_characters")
given = [
# a line starting with several color codes in it
"src/main/java/androidx/arch/core/internal/FastSafeIterableMap.java:39: warning: Method androidx.arch.core.internal.FastSafeIterableMap.get(K) references hidden type androidx.arch.core.internal.SafeIterableMap.Entry<K,V>. [HiddenTypeParameter]",
# a line with a variety of characters, none of which are color codes
"space tab\tCAPITAL underscore_ slash/ colon: number 1 newline\n",
]
expected = [
"src/main/java/androidx/arch/core/internal/FastSafeIterableMap.java:39: warning: Method androidx.arch.core.internal.FastSafeIterableMap.get(K) references hidden type androidx.arch.core.internal.SafeIterableMap.Entry<K,V>. [HiddenTypeParameter]",
"space tab\tCAPITAL underscore_ slash/ colon: number 1 newline\n",
]
actual = [remove_control_characters(line) for line in given]
if actual != expected:
fail("remove_control_charactres gave incorrect response.\n\n" +
"Input : " + str(given) + ".\n\n" +
"Expected output: " + str(expected) + ".\n\n" +
"Actual output : " + str(actual) + ".")
def main():
test_collapse_consecutive_blank_lines()
test_collapse_tasks_having_no_output()
test_detect_task_names()
test_suggest_missing_exemptions()
test_normalize_paths()
test_regexes_matcher_get_matching_regexes()
test_regexes_matcher_index_first_matching_regex()
test_remove_control_characters()
test_remove_unmatched_exemptions()
if __name__ == "__main__":
main()