You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by GitBox <gi...@apache.org> on 2020/08/02 19:54:54 UTC

[GitHub] [airflow] potiuk opened a new pull request #10119: Status of quarantined tests is stored in Github Issue

potiuk opened a new pull request #10119:
URL: https://github.com/apache/airflow/pull/10119


   This is an automated tracking solution for our Quarantined tests.
   
   Thanks to it we will be able to track the "flakiness of our tests. The status of last 10 test executions for all quarantined runs are kept in in the issue https://github.com/apache/airflow/issues/10118 and once last 10 runs of a test are ""success" we can remove it from Quarantine. 
   
   The quarantine test are run now in a separate workflow and repeated 4 times a day on schedule.
   
   I hope this way we will be able to clean-up the tests quickly.
   
   
   ---
   **^ Add meaningful description above**
   
   Read the **[Pull Request Guidelines](https://github.com/apache/airflow/blob/master/CONTRIBUTING.rst#pull-request-guidelines)** for more information.
   In case of fundamental code change, Airflow Improvement Proposal ([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvements+Proposals)) is needed.
   In case of a new dependency, check compliance with the [ASF 3rd Party License Policy](https://www.apache.org/legal/resolved.html#category-x).
   In case of backwards incompatible changes please leave a note in [UPDATING.md](https://github.com/apache/airflow/blob/master/UPDATING.md).
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] potiuk commented on a change in pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
potiuk commented on a change in pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#discussion_r464121818



##########
File path: scripts/ci/in_container/update_quarantined_test_status.py
##########
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+import os
+import re
+import sys
+from datetime import datetime
+from typing import Dict, List, NamedTuple, Optional
+from urllib.parse import urlsplit
+
+from bs4 import BeautifulSoup
+from github3 import login
+from tabulate import tabulate
+
+
+class TestResult(NamedTuple):
+    test_id: str
+    file: str
+    name: str
+    classname: str
+    line: str
+    result: bool
+
+
+class TestHistory(NamedTuple):
+    test_id: str
+    name: str
+    url: str
+    states: List[bool]
+
+
+test_results = []
+
+user = ""
+repo = ""
+issue_id = 0
+num_runs = 10
+
+url_pattern = re.compile(r'\[([^]]*)]\(([^)]*)\)')
+
+status_map: Dict[str, bool] = {
+    ":heavy_check_mark:": True,
+    ":x:": False,
+}
+
+reverse_status_map: Dict[bool, str] = {status_map[key]: key for key in status_map.keys()}
+
+
+def get_url(result: TestResult) -> str:
+    return f"[{result.name}](https://github.com/{user}/{repo}/blob/" \
+           f"master/{result.file}?test_id={result.test_id}#L{result.line})"
+
+
+def parse_state_history(history_string: str) -> List[bool]:
+    history_array = history_string.split(' ')
+    status_array: List[bool] = []
+    for value in history_array:
+        if value:
+            status_array.append(status_map[value])
+    return status_array
+
+
+def parse_test_history(line: str) -> Optional[TestHistory]:
+    values = line.split("|")
+    match_url = url_pattern.match(values[1].strip())
+    if match_url:
+        name = match_url.group(1)
+        url = match_url.group(0)
+        http_url = match_url.group(2)
+        parsed_url = urlsplit(http_url)
+        the_id = parsed_url[3].split("=")[1]
+        # noinspection PyBroadException
+        try:
+            states = parse_state_history(values[3])
+        except Exception:
+            states = []
+        return TestHistory(
+            test_id=the_id,
+            name=name,
+            states=states,
+            url=url
+        )
+    return None
+
+
+def parse_body(body: str) -> Dict[str, TestHistory]:
+    parse = False
+    test_history_map: Dict[str, TestHistory] = {}
+    for line in body.splitlines(keepends=False):
+        if line.startswith("|-"):
+            parse = True
+            continue
+        if parse:
+            if not line.startswith("|"):
+                break
+            # noinspection PyBroadException
+            try:
+                status = parse_test_history(line)
+            except Exception:
+                continue
+            if status:
+                test_history_map[status.test_id] = status
+    return test_history_map
+
+
+def update_test_history(history: TestHistory, last_status: bool):
+    print(f"Adding status to test history: {history}, {last_status}")
+    return TestHistory(
+        test_id=history.test_id,
+        name=history.name,
+        url=history.url,
+        states=([last_status] + history.states)[0:num_runs]
+    )
+
+
+def create_test_history(result: TestResult) -> TestHistory:
+    print(f"Creating test history {result}")
+    return TestHistory(
+        test_id=result.test_id,
+        name=result.name,
+        url=get_url(result),
+        states=[result.result]
+    )
+
+
+def get_history_status(history: TestHistory):
+    if len(history.states) < num_runs:
+        if all(history.states):
+            return "So far, so good"
+        return "Flaky"
+    if all(history.states):
+        return "Stable"
+    if all(history.states[0:num_runs - 1]):
+        return "Just one more"
+    if all(history.states[0:int(num_runs / 2)]):
+        return "Almost there"
+    return "Flaky"
+
+
+def get_table(history_map: Dict[str, TestHistory]) -> str:
+    headers = ["Test", "Last run", f"Last {num_runs} runs", "Status"]
+    the_table: List[List[str]] = []
+    for ordered_key in sorted(history_map.keys()):
+        history = history_map[ordered_key]
+        the_table.append([
+            history.url,
+            "Succeeded" if history.states[0] else "Failed",
+            " ".join([reverse_status_map[state] for state in history.states]),
+            get_history_status(history)
+        ])
+    return tabulate(the_table, headers, tablefmt="github")
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print("Provide XML JUNIT FILE as first argument")
+        sys.exit(1)
+
+    with open(sys.argv[1], "r") as f:
+        text = f.read()
+    y = BeautifulSoup(text, "html.parser")
+    res = y.testsuites.testsuite.findAll("testcase")
+    for test in res:
+        print("Parsing: " + test['classname'] + "::" + test['name'])
+        if len(test.contents) > 0 and test.contents[0].name == 'skipped':
+            print(f"skipping {test['name']}")
+            continue
+        test_results.append(TestResult(
+            test_id=test['classname'] + "::" + test['name'],
+            file=test['file'],
+            line=test['line'],
+            name=test['name'],
+            classname=test['classname'],
+            result=len(test.contents) == 0
+        ))
+
+    token = os.environ.get("GITHUB_TOKEN")
+    print(f"Token: {token}")

Review comment:
       Just by seing ***** you know you get it right. girthub automatically mask github token when it is printed 😱 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] turbaszek commented on pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
turbaszek commented on pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#issuecomment-667903766


   Should we add in #10118 some info about the process and the issue itself? 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] potiuk commented on pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
potiuk commented on pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#issuecomment-667923001


   Good Point. I will add it :)
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] potiuk merged pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
potiuk merged pull request #10119:
URL: https://github.com/apache/airflow/pull/10119


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] turbaszek commented on a change in pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
turbaszek commented on a change in pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#discussion_r464274461



##########
File path: scripts/ci/in_container/update_quarantined_test_status.py
##########
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+import os
+import re
+import sys
+from datetime import datetime
+from typing import Dict, List, NamedTuple, Optional
+from urllib.parse import urlsplit
+
+from bs4 import BeautifulSoup
+from github3 import login
+from tabulate import tabulate
+
+
+class TestResult(NamedTuple):
+    test_id: str
+    file: str
+    name: str
+    classname: str
+    line: str
+    result: bool
+
+
+class TestHistory(NamedTuple):
+    test_id: str
+    name: str
+    url: str
+    states: List[bool]
+
+
+test_results = []
+
+user = ""
+repo = ""
+issue_id = 0
+num_runs = 10
+
+url_pattern = re.compile(r'\[([^]]*)]\(([^)]*)\)')
+
+status_map: Dict[str, bool] = {
+    ":heavy_check_mark:": True,
+    ":x:": False,
+}
+
+reverse_status_map: Dict[bool, str] = {status_map[key]: key for key in status_map.keys()}
+
+
+def get_url(result: TestResult) -> str:
+    return f"[{result.name}](https://github.com/{user}/{repo}/blob/" \
+           f"master/{result.file}?test_id={result.test_id}#L{result.line})"
+
+
+def parse_state_history(history_string: str) -> List[bool]:
+    history_array = history_string.split(' ')
+    status_array: List[bool] = []
+    for value in history_array:
+        if value:
+            status_array.append(status_map[value])
+    return status_array
+
+
+def parse_test_history(line: str) -> Optional[TestHistory]:
+    values = line.split("|")
+    match_url = url_pattern.match(values[1].strip())
+    if match_url:
+        name = match_url.group(1)
+        url = match_url.group(0)
+        http_url = match_url.group(2)
+        parsed_url = urlsplit(http_url)
+        the_id = parsed_url[3].split("=")[1]
+        # noinspection PyBroadException
+        try:
+            states = parse_state_history(values[3])
+        except Exception:
+            states = []
+        return TestHistory(
+            test_id=the_id,
+            name=name,
+            states=states,
+            url=url
+        )
+    return None
+
+
+def parse_body(body: str) -> Dict[str, TestHistory]:
+    parse = False
+    test_history_map: Dict[str, TestHistory] = {}
+    for line in body.splitlines(keepends=False):
+        if line.startswith("|-"):
+            parse = True
+            continue
+        if parse:
+            if not line.startswith("|"):
+                break
+            # noinspection PyBroadException
+            try:
+                status = parse_test_history(line)
+            except Exception:
+                continue
+            if status:
+                test_history_map[status.test_id] = status
+    return test_history_map
+
+
+def update_test_history(history: TestHistory, last_status: bool):
+    print(f"Adding status to test history: {history}, {last_status}")
+    return TestHistory(
+        test_id=history.test_id,
+        name=history.name,
+        url=history.url,
+        states=([last_status] + history.states)[0:num_runs]
+    )
+
+
+def create_test_history(result: TestResult) -> TestHistory:
+    print(f"Creating test history {result}")
+    return TestHistory(
+        test_id=result.test_id,
+        name=result.name,
+        url=get_url(result),
+        states=[result.result]
+    )
+
+
+def get_history_status(history: TestHistory):
+    if len(history.states) < num_runs:
+        if all(history.states):
+            return "So far, so good"
+        return "Flaky"
+    if all(history.states):
+        return "Stable"
+    if all(history.states[0:num_runs - 1]):
+        return "Just one more"
+    if all(history.states[0:int(num_runs / 2)]):
+        return "Almost there"
+    return "Flaky"
+
+
+def get_table(history_map: Dict[str, TestHistory]) -> str:
+    headers = ["Test", "Last run", f"Last {num_runs} runs", "Status"]
+    the_table: List[List[str]] = []
+    for ordered_key in sorted(history_map.keys()):
+        history = history_map[ordered_key]
+        the_table.append([
+            history.url,
+            "Succeeded" if history.states[0] else "Failed",
+            " ".join([reverse_status_map[state] for state in history.states]),
+            get_history_status(history)
+        ])
+    return tabulate(the_table, headers, tablefmt="github")
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print("Provide XML JUNIT FILE as first argument")
+        sys.exit(1)
+
+    with open(sys.argv[1], "r") as f:
+        text = f.read()
+    y = BeautifulSoup(text, "html.parser")
+    res = y.testsuites.testsuite.findAll("testcase")
+    for test in res:
+        print("Parsing: " + test['classname'] + "::" + test['name'])
+        if len(test.contents) > 0 and test.contents[0].name == 'skipped':
+            print(f"skipping {test['name']}")
+            continue
+        test_results.append(TestResult(
+            test_id=test['classname'] + "::" + test['name'],
+            file=test['file'],
+            line=test['line'],
+            name=test['name'],
+            classname=test['classname'],
+            result=len(test.contents) == 0
+        ))
+
+    token = os.environ.get("GITHUB_TOKEN")
+    print(f"Token: {token}")

Review comment:
       > girthub automatically mask github token when it is printed 😱
   
   I didn't know that. If that's the case I see no problem




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] potiuk commented on pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
potiuk commented on pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#issuecomment-668136797


   I would still run the action anyway - it's not too costly one, and also we should re-open the issue if we add a new quarantined test - so the action (if at all) should be based on a number of tests that we mark as quarantined (so leaving it as is is best) 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] potiuk commented on pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
potiuk commented on pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#issuecomment-668135560


   I've added a few things:
   
   1) Documentation in CI.rst
   2) Added header to the issue explaining how it gets updated.
   3) added a "Comment" section that is preserved (so you can for example add link to issue if you are working on flaky test)
   4) added configurable ISSUE_ID -> we need different set of tests for master/v1-10-test/v1-10-stable
   
   @feluelle  -> I think we should not close it - flaky tests will happen sooner or later. But indeed closing/opening the issue might be a good idea indeed. 
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] ad-m commented on a change in pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
ad-m commented on a change in pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#discussion_r464145073



##########
File path: scripts/ci/in_container/update_quarantined_test_status.py
##########
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+import os
+import re
+import sys
+from datetime import datetime
+from typing import Dict, List, NamedTuple, Optional
+from urllib.parse import urlsplit
+
+from bs4 import BeautifulSoup
+from github3 import login
+from tabulate import tabulate
+
+
+class TestResult(NamedTuple):
+    test_id: str
+    file: str
+    name: str
+    classname: str
+    line: str
+    result: bool
+
+
+class TestHistory(NamedTuple):
+    test_id: str
+    name: str
+    url: str
+    states: List[bool]
+
+
+test_results = []
+
+user = ""
+repo = ""
+issue_id = 0
+num_runs = 10
+
+url_pattern = re.compile(r'\[([^]]*)]\(([^)]*)\)')
+
+status_map: Dict[str, bool] = {
+    ":heavy_check_mark:": True,
+    ":x:": False,
+}
+
+reverse_status_map: Dict[bool, str] = {status_map[key]: key for key in status_map.keys()}
+
+
+def get_url(result: TestResult) -> str:
+    return f"[{result.name}](https://github.com/{user}/{repo}/blob/" \
+           f"master/{result.file}?test_id={result.test_id}#L{result.line})"
+
+
+def parse_state_history(history_string: str) -> List[bool]:
+    history_array = history_string.split(' ')
+    status_array: List[bool] = []
+    for value in history_array:
+        if value:
+            status_array.append(status_map[value])
+    return status_array
+
+
+def parse_test_history(line: str) -> Optional[TestHistory]:
+    values = line.split("|")
+    match_url = url_pattern.match(values[1].strip())
+    if match_url:
+        name = match_url.group(1)
+        url = match_url.group(0)
+        http_url = match_url.group(2)
+        parsed_url = urlsplit(http_url)
+        the_id = parsed_url[3].split("=")[1]
+        # noinspection PyBroadException
+        try:
+            states = parse_state_history(values[3])
+        except Exception:
+            states = []
+        return TestHistory(
+            test_id=the_id,
+            name=name,
+            states=states,
+            url=url
+        )
+    return None
+
+
+def parse_body(body: str) -> Dict[str, TestHistory]:
+    parse = False
+    test_history_map: Dict[str, TestHistory] = {}
+    for line in body.splitlines(keepends=False):
+        if line.startswith("|-"):
+            parse = True
+            continue
+        if parse:
+            if not line.startswith("|"):
+                break
+            # noinspection PyBroadException
+            try:
+                status = parse_test_history(line)
+            except Exception:
+                continue
+            if status:
+                test_history_map[status.test_id] = status
+    return test_history_map
+
+
+def update_test_history(history: TestHistory, last_status: bool):
+    print(f"Adding status to test history: {history}, {last_status}")
+    return TestHistory(
+        test_id=history.test_id,
+        name=history.name,
+        url=history.url,
+        states=([last_status] + history.states)[0:num_runs]
+    )
+
+
+def create_test_history(result: TestResult) -> TestHistory:
+    print(f"Creating test history {result}")
+    return TestHistory(
+        test_id=result.test_id,
+        name=result.name,
+        url=get_url(result),
+        states=[result.result]
+    )
+
+
+def get_history_status(history: TestHistory):
+    if len(history.states) < num_runs:
+        if all(history.states):
+            return "So far, so good"
+        return "Flaky"
+    if all(history.states):
+        return "Stable"
+    if all(history.states[0:num_runs - 1]):
+        return "Just one more"
+    if all(history.states[0:int(num_runs / 2)]):
+        return "Almost there"
+    return "Flaky"
+
+
+def get_table(history_map: Dict[str, TestHistory]) -> str:
+    headers = ["Test", "Last run", f"Last {num_runs} runs", "Status"]
+    the_table: List[List[str]] = []
+    for ordered_key in sorted(history_map.keys()):
+        history = history_map[ordered_key]
+        the_table.append([
+            history.url,
+            "Succeeded" if history.states[0] else "Failed",
+            " ".join([reverse_status_map[state] for state in history.states]),
+            get_history_status(history)
+        ])
+    return tabulate(the_table, headers, tablefmt="github")
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print("Provide XML JUNIT FILE as first argument")
+        sys.exit(1)
+
+    with open(sys.argv[1], "r") as f:
+        text = f.read()
+    y = BeautifulSoup(text, "html.parser")
+    res = y.testsuites.testsuite.findAll("testcase")
+    for test in res:
+        print("Parsing: " + test['classname'] + "::" + test['name'])
+        if len(test.contents) > 0 and test.contents[0].name == 'skipped':
+            print(f"skipping {test['name']}")
+            continue
+        test_results.append(TestResult(
+            test_id=test['classname'] + "::" + test['name'],
+            file=test['file'],
+            line=test['line'],
+            name=test['name'],
+            classname=test['classname'],
+            result=len(test.contents) == 0
+        ))
+
+    token = os.environ.get("GITHUB_TOKEN")
+    print(f"Token: {token}")
+    github_repository = os.environ.get('GITHUB_REPOSITORY')
+    if not github_repository:
+        raise Exception("Github Repository must be defined!")
+    user, repo = github_repository.split("/")
+    print(f"User: {user}, Repo: {repo}")
+    issue_id = int(os.environ.get('ISSUE_ID', 0))

Review comment:
       Have you considered using the title of the issue here? Some bots, eg renovate-bot, use issues as a title-based database (not create new pull request when previous one is closed without merge; create new pull request when expected not exist).




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] potiuk commented on a change in pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
potiuk commented on a change in pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#discussion_r464240952



##########
File path: scripts/ci/in_container/update_quarantined_test_status.py
##########
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+import os
+import re
+import sys
+from datetime import datetime
+from typing import Dict, List, NamedTuple, Optional
+from urllib.parse import urlsplit
+
+from bs4 import BeautifulSoup
+from github3 import login
+from tabulate import tabulate
+
+
+class TestResult(NamedTuple):
+    test_id: str
+    file: str
+    name: str
+    classname: str
+    line: str
+    result: bool
+
+
+class TestHistory(NamedTuple):
+    test_id: str
+    name: str
+    url: str
+    states: List[bool]
+
+
+test_results = []
+
+user = ""
+repo = ""
+issue_id = 0
+num_runs = 10
+
+url_pattern = re.compile(r'\[([^]]*)]\(([^)]*)\)')
+
+status_map: Dict[str, bool] = {
+    ":heavy_check_mark:": True,
+    ":x:": False,
+}
+
+reverse_status_map: Dict[bool, str] = {status_map[key]: key for key in status_map.keys()}
+
+
+def get_url(result: TestResult) -> str:
+    return f"[{result.name}](https://github.com/{user}/{repo}/blob/" \
+           f"master/{result.file}?test_id={result.test_id}#L{result.line})"
+
+
+def parse_state_history(history_string: str) -> List[bool]:
+    history_array = history_string.split(' ')
+    status_array: List[bool] = []
+    for value in history_array:
+        if value:
+            status_array.append(status_map[value])
+    return status_array
+
+
+def parse_test_history(line: str) -> Optional[TestHistory]:
+    values = line.split("|")
+    match_url = url_pattern.match(values[1].strip())
+    if match_url:
+        name = match_url.group(1)
+        url = match_url.group(0)
+        http_url = match_url.group(2)
+        parsed_url = urlsplit(http_url)
+        the_id = parsed_url[3].split("=")[1]
+        # noinspection PyBroadException
+        try:
+            states = parse_state_history(values[3])
+        except Exception:
+            states = []
+        return TestHistory(
+            test_id=the_id,
+            name=name,
+            states=states,
+            url=url
+        )
+    return None
+
+
+def parse_body(body: str) -> Dict[str, TestHistory]:
+    parse = False
+    test_history_map: Dict[str, TestHistory] = {}
+    for line in body.splitlines(keepends=False):
+        if line.startswith("|-"):
+            parse = True
+            continue
+        if parse:
+            if not line.startswith("|"):
+                break
+            # noinspection PyBroadException
+            try:
+                status = parse_test_history(line)
+            except Exception:
+                continue
+            if status:
+                test_history_map[status.test_id] = status
+    return test_history_map
+
+
+def update_test_history(history: TestHistory, last_status: bool):
+    print(f"Adding status to test history: {history}, {last_status}")
+    return TestHistory(
+        test_id=history.test_id,
+        name=history.name,
+        url=history.url,
+        states=([last_status] + history.states)[0:num_runs]
+    )
+
+
+def create_test_history(result: TestResult) -> TestHistory:
+    print(f"Creating test history {result}")
+    return TestHistory(
+        test_id=result.test_id,
+        name=result.name,
+        url=get_url(result),
+        states=[result.result]
+    )
+
+
+def get_history_status(history: TestHistory):
+    if len(history.states) < num_runs:
+        if all(history.states):
+            return "So far, so good"
+        return "Flaky"
+    if all(history.states):
+        return "Stable"
+    if all(history.states[0:num_runs - 1]):
+        return "Just one more"
+    if all(history.states[0:int(num_runs / 2)]):
+        return "Almost there"
+    return "Flaky"
+
+
+def get_table(history_map: Dict[str, TestHistory]) -> str:
+    headers = ["Test", "Last run", f"Last {num_runs} runs", "Status"]
+    the_table: List[List[str]] = []
+    for ordered_key in sorted(history_map.keys()):
+        history = history_map[ordered_key]
+        the_table.append([
+            history.url,
+            "Succeeded" if history.states[0] else "Failed",
+            " ".join([reverse_status_map[state] for state in history.states]),
+            get_history_status(history)
+        ])
+    return tabulate(the_table, headers, tablefmt="github")
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print("Provide XML JUNIT FILE as first argument")
+        sys.exit(1)
+
+    with open(sys.argv[1], "r") as f:
+        text = f.read()
+    y = BeautifulSoup(text, "html.parser")
+    res = y.testsuites.testsuite.findAll("testcase")
+    for test in res:
+        print("Parsing: " + test['classname'] + "::" + test['name'])
+        if len(test.contents) > 0 and test.contents[0].name == 'skipped':
+            print(f"skipping {test['name']}")
+            continue
+        test_results.append(TestResult(
+            test_id=test['classname'] + "::" + test['name'],
+            file=test['file'],
+            line=test['line'],
+            name=test['name'],
+            classname=test['classname'],
+            result=len(test.contents) == 0
+        ))
+
+    token = os.environ.get("GITHUB_TOKEN")
+    print(f"Token: {token}")
+    github_repository = os.environ.get('GITHUB_REPOSITORY')
+    if not github_repository:
+        raise Exception("Github Repository must be defined!")
+    user, repo = github_repository.split("/")
+    print(f"User: {user}, Repo: {repo}")
+    issue_id = int(os.environ.get('ISSUE_ID', 0))

Review comment:
       There is no point in that. We have one single issue where we will keep status of all the tests. We are not going to open/close the issue - it's description is merely used as both - visualisation of status of quarantined issues as well as database where we keep status of last tests. So we can define one issue where we keep those issues. and we are only limiting that to apache/airflow repo (in all other repos the script is not executed.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] potiuk commented on a change in pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
potiuk commented on a change in pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#discussion_r464121698



##########
File path: scripts/ci/in_container/update_quarantined_test_status.py
##########
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+import os
+import re
+import sys
+from datetime import datetime
+from typing import Dict, List, NamedTuple, Optional
+from urllib.parse import urlsplit
+
+from bs4 import BeautifulSoup
+from github3 import login
+from tabulate import tabulate
+
+
+class TestResult(NamedTuple):
+    test_id: str
+    file: str
+    name: str
+    classname: str
+    line: str
+    result: bool
+
+
+class TestHistory(NamedTuple):
+    test_id: str
+    name: str
+    url: str
+    states: List[bool]
+
+
+test_results = []
+
+user = ""
+repo = ""
+issue_id = 0
+num_runs = 10
+
+url_pattern = re.compile(r'\[([^]]*)]\(([^)]*)\)')
+
+status_map: Dict[str, bool] = {
+    ":heavy_check_mark:": True,
+    ":x:": False,
+}
+
+reverse_status_map: Dict[bool, str] = {status_map[key]: key for key in status_map.keys()}
+
+
+def get_url(result: TestResult) -> str:
+    return f"[{result.name}](https://github.com/{user}/{repo}/blob/" \
+           f"master/{result.file}?test_id={result.test_id}#L{result.line})"
+
+
+def parse_state_history(history_string: str) -> List[bool]:
+    history_array = history_string.split(' ')
+    status_array: List[bool] = []
+    for value in history_array:
+        if value:
+            status_array.append(status_map[value])
+    return status_array
+
+
+def parse_test_history(line: str) -> Optional[TestHistory]:
+    values = line.split("|")
+    match_url = url_pattern.match(values[1].strip())
+    if match_url:
+        name = match_url.group(1)
+        url = match_url.group(0)
+        http_url = match_url.group(2)
+        parsed_url = urlsplit(http_url)
+        the_id = parsed_url[3].split("=")[1]
+        # noinspection PyBroadException
+        try:
+            states = parse_state_history(values[3])
+        except Exception:
+            states = []
+        return TestHistory(
+            test_id=the_id,
+            name=name,
+            states=states,
+            url=url
+        )
+    return None
+
+
+def parse_body(body: str) -> Dict[str, TestHistory]:
+    parse = False
+    test_history_map: Dict[str, TestHistory] = {}
+    for line in body.splitlines(keepends=False):
+        if line.startswith("|-"):
+            parse = True
+            continue
+        if parse:
+            if not line.startswith("|"):
+                break
+            # noinspection PyBroadException
+            try:
+                status = parse_test_history(line)
+            except Exception:
+                continue
+            if status:
+                test_history_map[status.test_id] = status
+    return test_history_map
+
+
+def update_test_history(history: TestHistory, last_status: bool):
+    print(f"Adding status to test history: {history}, {last_status}")
+    return TestHistory(
+        test_id=history.test_id,
+        name=history.name,
+        url=history.url,
+        states=([last_status] + history.states)[0:num_runs]
+    )
+
+
+def create_test_history(result: TestResult) -> TestHistory:
+    print(f"Creating test history {result}")
+    return TestHistory(
+        test_id=result.test_id,
+        name=result.name,
+        url=get_url(result),
+        states=[result.result]
+    )
+
+
+def get_history_status(history: TestHistory):
+    if len(history.states) < num_runs:
+        if all(history.states):
+            return "So far, so good"
+        return "Flaky"
+    if all(history.states):
+        return "Stable"
+    if all(history.states[0:num_runs - 1]):
+        return "Just one more"
+    if all(history.states[0:int(num_runs / 2)]):
+        return "Almost there"
+    return "Flaky"
+
+
+def get_table(history_map: Dict[str, TestHistory]) -> str:
+    headers = ["Test", "Last run", f"Last {num_runs} runs", "Status"]
+    the_table: List[List[str]] = []
+    for ordered_key in sorted(history_map.keys()):
+        history = history_map[ordered_key]
+        the_table.append([
+            history.url,
+            "Succeeded" if history.states[0] else "Failed",
+            " ".join([reverse_status_map[state] for state in history.states]),
+            get_history_status(history)
+        ])
+    return tabulate(the_table, headers, tablefmt="github")
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print("Provide XML JUNIT FILE as first argument")
+        sys.exit(1)
+
+    with open(sys.argv[1], "r") as f:
+        text = f.read()
+    y = BeautifulSoup(text, "html.parser")
+    res = y.testsuites.testsuite.findAll("testcase")
+    for test in res:
+        print("Parsing: " + test['classname'] + "::" + test['name'])
+        if len(test.contents) > 0 and test.contents[0].name == 'skipped':
+            print(f"skipping {test['name']}")
+            continue
+        test_results.append(TestResult(
+            test_id=test['classname'] + "::" + test['name'],
+            file=test['file'],
+            line=test['line'],
+            name=test['name'],
+            classname=test['classname'],
+            result=len(test.contents) == 0
+        ))
+
+    token = os.environ.get("GITHUB_TOKEN")
+    print(f"Token: {token}")

Review comment:
       Does not matter. It is masked anyway :)




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] potiuk commented on a change in pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
potiuk commented on a change in pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#discussion_r464125229



##########
File path: scripts/ci/in_container/update_quarantined_test_status.py
##########
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+import os
+import re
+import sys
+from datetime import datetime
+from typing import Dict, List, NamedTuple, Optional
+from urllib.parse import urlsplit
+
+from bs4 import BeautifulSoup
+from github3 import login
+from tabulate import tabulate
+
+
+class TestResult(NamedTuple):
+    test_id: str
+    file: str
+    name: str
+    classname: str
+    line: str
+    result: bool
+
+
+class TestHistory(NamedTuple):
+    test_id: str
+    name: str
+    url: str
+    states: List[bool]
+
+
+test_results = []
+
+user = ""
+repo = ""
+issue_id = 0
+num_runs = 10
+
+url_pattern = re.compile(r'\[([^]]*)]\(([^)]*)\)')
+
+status_map: Dict[str, bool] = {
+    ":heavy_check_mark:": True,
+    ":x:": False,
+}
+
+reverse_status_map: Dict[bool, str] = {status_map[key]: key for key in status_map.keys()}
+
+
+def get_url(result: TestResult) -> str:
+    return f"[{result.name}](https://github.com/{user}/{repo}/blob/" \
+           f"master/{result.file}?test_id={result.test_id}#L{result.line})"
+
+
+def parse_state_history(history_string: str) -> List[bool]:
+    history_array = history_string.split(' ')
+    status_array: List[bool] = []
+    for value in history_array:
+        if value:
+            status_array.append(status_map[value])
+    return status_array
+
+
+def parse_test_history(line: str) -> Optional[TestHistory]:
+    values = line.split("|")
+    match_url = url_pattern.match(values[1].strip())
+    if match_url:
+        name = match_url.group(1)
+        url = match_url.group(0)
+        http_url = match_url.group(2)
+        parsed_url = urlsplit(http_url)
+        the_id = parsed_url[3].split("=")[1]
+        # noinspection PyBroadException
+        try:
+            states = parse_state_history(values[3])
+        except Exception:
+            states = []
+        return TestHistory(
+            test_id=the_id,
+            name=name,
+            states=states,
+            url=url
+        )
+    return None
+
+
+def parse_body(body: str) -> Dict[str, TestHistory]:
+    parse = False
+    test_history_map: Dict[str, TestHistory] = {}
+    for line in body.splitlines(keepends=False):
+        if line.startswith("|-"):
+            parse = True
+            continue
+        if parse:
+            if not line.startswith("|"):
+                break
+            # noinspection PyBroadException
+            try:
+                status = parse_test_history(line)
+            except Exception:
+                continue
+            if status:
+                test_history_map[status.test_id] = status
+    return test_history_map
+
+
+def update_test_history(history: TestHistory, last_status: bool):
+    print(f"Adding status to test history: {history}, {last_status}")
+    return TestHistory(
+        test_id=history.test_id,
+        name=history.name,
+        url=history.url,
+        states=([last_status] + history.states)[0:num_runs]
+    )
+
+
+def create_test_history(result: TestResult) -> TestHistory:
+    print(f"Creating test history {result}")
+    return TestHistory(
+        test_id=result.test_id,
+        name=result.name,
+        url=get_url(result),
+        states=[result.result]
+    )
+
+
+def get_history_status(history: TestHistory):
+    if len(history.states) < num_runs:
+        if all(history.states):
+            return "So far, so good"
+        return "Flaky"
+    if all(history.states):
+        return "Stable"
+    if all(history.states[0:num_runs - 1]):
+        return "Just one more"
+    if all(history.states[0:int(num_runs / 2)]):
+        return "Almost there"
+    return "Flaky"
+
+
+def get_table(history_map: Dict[str, TestHistory]) -> str:
+    headers = ["Test", "Last run", f"Last {num_runs} runs", "Status"]
+    the_table: List[List[str]] = []
+    for ordered_key in sorted(history_map.keys()):
+        history = history_map[ordered_key]
+        the_table.append([
+            history.url,
+            "Succeeded" if history.states[0] else "Failed",
+            " ".join([reverse_status_map[state] for state in history.states]),
+            get_history_status(history)
+        ])
+    return tabulate(the_table, headers, tablefmt="github")
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print("Provide XML JUNIT FILE as first argument")
+        sys.exit(1)
+
+    with open(sys.argv[1], "r") as f:
+        text = f.read()
+    y = BeautifulSoup(text, "html.parser")
+    res = y.testsuites.testsuite.findAll("testcase")
+    for test in res:
+        print("Parsing: " + test['classname'] + "::" + test['name'])
+        if len(test.contents) > 0 and test.contents[0].name == 'skipped':
+            print(f"skipping {test['name']}")
+            continue
+        test_results.append(TestResult(
+            test_id=test['classname'] + "::" + test['name'],
+            file=test['file'],
+            line=test['line'],
+            name=test['name'],
+            classname=test['classname'],
+            result=len(test.contents) == 0
+        ))
+
+    token = os.environ.get("GITHUB_TOKEN")
+    print(f"Token: {token}")

Review comment:
       See " https://github.com/apache/airflow/pull/10119/checks?check_run_id=938181654#step:6:1155




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] potiuk commented on pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
potiuk commented on pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#issuecomment-668148977


   @feluelle -> auto-closing/opening added  :)


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [airflow] turbaszek commented on a change in pull request #10119: Status of quarantined tests is stored in Github Issue

Posted by GitBox <gi...@apache.org>.
turbaszek commented on a change in pull request #10119:
URL: https://github.com/apache/airflow/pull/10119#discussion_r464117415



##########
File path: scripts/ci/in_container/update_quarantined_test_status.py
##########
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+import os
+import re
+import sys
+from datetime import datetime
+from typing import Dict, List, NamedTuple, Optional
+from urllib.parse import urlsplit
+
+from bs4 import BeautifulSoup
+from github3 import login
+from tabulate import tabulate
+
+
+class TestResult(NamedTuple):
+    test_id: str
+    file: str
+    name: str
+    classname: str
+    line: str
+    result: bool
+
+
+class TestHistory(NamedTuple):
+    test_id: str
+    name: str
+    url: str
+    states: List[bool]
+
+
+test_results = []
+
+user = ""
+repo = ""
+issue_id = 0
+num_runs = 10
+
+url_pattern = re.compile(r'\[([^]]*)]\(([^)]*)\)')
+
+status_map: Dict[str, bool] = {
+    ":heavy_check_mark:": True,
+    ":x:": False,
+}
+
+reverse_status_map: Dict[bool, str] = {status_map[key]: key for key in status_map.keys()}
+
+
+def get_url(result: TestResult) -> str:
+    return f"[{result.name}](https://github.com/{user}/{repo}/blob/" \
+           f"master/{result.file}?test_id={result.test_id}#L{result.line})"
+
+
+def parse_state_history(history_string: str) -> List[bool]:
+    history_array = history_string.split(' ')
+    status_array: List[bool] = []
+    for value in history_array:
+        if value:
+            status_array.append(status_map[value])
+    return status_array
+
+
+def parse_test_history(line: str) -> Optional[TestHistory]:
+    values = line.split("|")
+    match_url = url_pattern.match(values[1].strip())
+    if match_url:
+        name = match_url.group(1)
+        url = match_url.group(0)
+        http_url = match_url.group(2)
+        parsed_url = urlsplit(http_url)
+        the_id = parsed_url[3].split("=")[1]
+        # noinspection PyBroadException
+        try:
+            states = parse_state_history(values[3])
+        except Exception:
+            states = []
+        return TestHistory(
+            test_id=the_id,
+            name=name,
+            states=states,
+            url=url
+        )
+    return None
+
+
+def parse_body(body: str) -> Dict[str, TestHistory]:
+    parse = False
+    test_history_map: Dict[str, TestHistory] = {}
+    for line in body.splitlines(keepends=False):
+        if line.startswith("|-"):
+            parse = True
+            continue
+        if parse:
+            if not line.startswith("|"):
+                break
+            # noinspection PyBroadException
+            try:
+                status = parse_test_history(line)
+            except Exception:
+                continue
+            if status:
+                test_history_map[status.test_id] = status
+    return test_history_map
+
+
+def update_test_history(history: TestHistory, last_status: bool):
+    print(f"Adding status to test history: {history}, {last_status}")
+    return TestHistory(
+        test_id=history.test_id,
+        name=history.name,
+        url=history.url,
+        states=([last_status] + history.states)[0:num_runs]
+    )
+
+
+def create_test_history(result: TestResult) -> TestHistory:
+    print(f"Creating test history {result}")
+    return TestHistory(
+        test_id=result.test_id,
+        name=result.name,
+        url=get_url(result),
+        states=[result.result]
+    )
+
+
+def get_history_status(history: TestHistory):
+    if len(history.states) < num_runs:
+        if all(history.states):
+            return "So far, so good"
+        return "Flaky"
+    if all(history.states):
+        return "Stable"
+    if all(history.states[0:num_runs - 1]):
+        return "Just one more"
+    if all(history.states[0:int(num_runs / 2)]):
+        return "Almost there"
+    return "Flaky"
+
+
+def get_table(history_map: Dict[str, TestHistory]) -> str:
+    headers = ["Test", "Last run", f"Last {num_runs} runs", "Status"]
+    the_table: List[List[str]] = []
+    for ordered_key in sorted(history_map.keys()):
+        history = history_map[ordered_key]
+        the_table.append([
+            history.url,
+            "Succeeded" if history.states[0] else "Failed",
+            " ".join([reverse_status_map[state] for state in history.states]),
+            get_history_status(history)
+        ])
+    return tabulate(the_table, headers, tablefmt="github")
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print("Provide XML JUNIT FILE as first argument")
+        sys.exit(1)
+
+    with open(sys.argv[1], "r") as f:
+        text = f.read()
+    y = BeautifulSoup(text, "html.parser")
+    res = y.testsuites.testsuite.findAll("testcase")
+    for test in res:
+        print("Parsing: " + test['classname'] + "::" + test['name'])
+        if len(test.contents) > 0 and test.contents[0].name == 'skipped':
+            print(f"skipping {test['name']}")
+            continue
+        test_results.append(TestResult(
+            test_id=test['classname'] + "::" + test['name'],
+            file=test['file'],
+            line=test['line'],
+            name=test['name'],
+            classname=test['classname'],
+            result=len(test.contents) == 0
+        ))
+
+    token = os.environ.get("GITHUB_TOKEN")
+    print(f"Token: {token}")

Review comment:
       Should this be printed?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org