tobiko/tobiko/common/_shelves.py

153 lines
5.4 KiB
Python

# Copyright 2022 Red Hat
#
# 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 __future__ import absolute_import
import dbm
import os
import shelve
from oslo_log import log
import tobiko
LOG = log.getLogger(__name__)
TEST_RUN_SHELF = 'test_run'
def get_shelves_dir():
# ensure the directory exists
from tobiko import config
shelves_dir = os.path.expanduser(config.CONF.tobiko.common.shelves_dir)
return shelves_dir
def get_shelf_path(shelf):
return os.path.join(get_shelves_dir(), shelf)
def addme_to_shared_resource(shelf, resource):
shelf_path = get_shelf_path(shelf)
# this is needed for unit tests
resource = str(resource)
testcase_id = tobiko.get_test_case().id()
for attempt in tobiko.retry(timeout=10.0,
interval=0.5):
try:
with shelve.open(shelf_path) as db:
if db.get(resource) is None:
db[resource] = set()
# the add and remove methods do not work directly on the db
auxset = db[resource]
auxset.add(testcase_id)
db[resource] = auxset
return db[resource]
except dbm.error:
LOG.exception(f"Error accessing shelf {shelf}")
if attempt.is_last:
raise
def removeme_from_shared_resource(shelf, resource):
shelf_path = get_shelf_path(shelf)
# this is needed for unit tests
resource = str(resource)
testcase_id = tobiko.get_test_case().id()
for attempt in tobiko.retry(timeout=10.0,
interval=0.5):
try:
with shelve.open(shelf_path) as db:
# the add and remove methods do not work directly on the db
db[resource] = db.get(resource) or set()
if testcase_id in db[resource]:
auxset = db[resource]
auxset.remove(testcase_id)
db[resource] = auxset
return db[resource]
except dbm.error:
LOG.exception(f"Error accessing shelf {shelf}")
if attempt.is_last:
raise
def remove_test_from_shelf_resources(testcase_id, shelf):
shelf_path = get_shelf_path(shelf)
for attempt in tobiko.retry(timeout=10.0,
interval=0.5):
try:
with shelve.open(shelf_path) as db:
if not db:
return
for resource in db.keys():
if testcase_id in db[resource]:
auxset = db[resource]
auxset.remove(testcase_id)
db[resource] = auxset
return db
except dbm.error as err:
LOG.exception(f"Error accessing shelf {shelf}")
if "db type could not be determined" in str(err):
# remove the filename extension, which depends on the specific
# DBM implementation
shelf_path = '.'.join(shelf_path.split('.')[:-1])
if attempt.is_last:
raise
def remove_test_from_all_shared_resources(testcase_id):
LOG.debug(f'Removing test {testcase_id} from all shelf resources')
shelves_dir = get_shelves_dir()
for filename in os.listdir(shelves_dir):
if TEST_RUN_SHELF not in filename:
remove_test_from_shelf_resources(testcase_id, filename)
def initialize_shelves():
shelves_dir = get_shelves_dir()
shelf_path = os.path.join(shelves_dir, TEST_RUN_SHELF)
id_key = 'PYTEST_XDIST_TESTRUNUID'
test_run_uid = os.environ.get(id_key)
tobiko.makedirs(shelves_dir)
# if no PYTEST_XDIST_TESTRUNUID ->
# pytest was executed with only one worker
# if tobiko.initialize_shelves() == True ->
# this is the first pytest worker running cleanup_shelves
# then, cleanup the shelves directory
# else, another worker did it before
for attempt in tobiko.retry(timeout=15.0,
interval=0.5):
try:
with shelve.open(shelf_path) as db:
if test_run_uid is None:
LOG.debug("Only one pytest worker - Initializing shelves")
elif test_run_uid == db.get(id_key):
LOG.debug("Another pytest worker already initialized "
"the shelves")
return
else:
LOG.debug("Initializing shelves for the "
"test run uid %s", test_run_uid)
db[id_key] = test_run_uid
for filename in os.listdir(shelves_dir):
if TEST_RUN_SHELF not in filename:
file_path = os.path.join(shelves_dir, filename)
os.unlink(file_path)
return
except dbm.error:
LOG.exception(f"Error accessing shelf {TEST_RUN_SHELF}")
if attempt.is_last:
raise