From 8eca26617007eec7a18584f8ee1f7f373c204a38 Mon Sep 17 00:00:00 2001 From: Mike Shriver Date: Mon, 12 Aug 2019 14:19:03 -0400 Subject: [PATCH] Update pytest to 5+ various updates associated with this major version change Update pytest_generate_tests and fixtures in test_datastore_analysis Update parametrization for test_pytest_shortcuts Move marker/fixture inclusion to conftest, and list it in entrypoints explicitly Flattens plugin inclusion, instead of markers/filters being defined via pytest_plugins at various points in the framework Define a bunch of markers that were never actually registered with pytest Remove skip_selenium marker Remove references to pytest.config Move template fixtures, update tests hookimpl decorator, change env mark for provider rework the checking/handling of markers for provider fixture resolve provider fixture lookup failures DataProvider is where it shouldn't be, pytest_fixture_setup not replacing Remove known failures from test_modules_importable Update travis script options Move BaseLoggedInPage view class definition Remove duplicate chargeback destination Update pre-commit package versions --- .pre-commit-config.yaml | 8 +- .travis.yml | 6 +- cfme/ansible/credentials.py | 2 +- cfme/ansible/playbooks.py | 2 +- cfme/ansible/repositories.py | 2 +- cfme/ansible_tower/explorer.py | 2 +- cfme/ansible_tower/jobs.py | 2 +- cfme/automate/__init__.py | 2 +- cfme/automate/dialog_import_export.py | 2 +- cfme/automate/dialogs/__init__.py | 2 +- cfme/automate/explorer/__init__.py | 2 +- cfme/automate/import_export.py | 2 +- cfme/automate/provisioning_dialogs.py | 2 +- cfme/base/login.py | 129 -------- cfme/base/ui.py | 10 +- cfme/cloud/availability_zone.py | 2 +- cfme/cloud/host_aggregates.py | 2 +- cfme/cloud/instance/__init__.py | 2 +- cfme/cloud/provider/__init__.py | 2 +- cfme/common/__init__.py | 125 +++++++- cfme/common/host_views.py | 2 +- cfme/common/physical_server_views.py | 2 +- cfme/common/physical_switch_views.py | 2 +- cfme/common/provider_views.py | 2 +- cfme/common/topology.py | 2 +- cfme/common/vm.py | 2 +- cfme/common/vm_views.py | 2 +- .../configuration/analysis_profile.py | 2 +- cfme/configure/settings.py | 2 +- cfme/configure/tasks.py | 2 +- cfme/containers/build.py | 2 +- cfme/containers/overview.py | 2 +- cfme/containers/provider/__init__.py | 2 +- cfme/control/explorer/__init__.py | 2 +- cfme/control/explorer/alerts.py | 2 +- cfme/control/import_export.py | 2 +- cfme/control/log.py | 2 +- cfme/control/simulation.py | 2 +- cfme/dashboard.py | 2 +- cfme/fixtures/appliance_update.py | 14 +- cfme/fixtures/artifactor_plugin.py | 14 +- cfme/fixtures/blockers.py | 2 +- cfme/fixtures/cli.py | 12 +- cfme/fixtures/dev_branch.py | 16 +- cfme/fixtures/events.py | 2 +- cfme/fixtures/log.py | 27 +- cfme/fixtures/parallelizer/__init__.py | 87 +++--- cfme/fixtures/parallelizer/remote.py | 46 +-- cfme/fixtures/portset.py | 2 +- cfme/fixtures/provider.py | 281 +++--------------- cfme/fixtures/pytest_store.py | 6 +- cfme/fixtures/rbac.py | 6 +- cfme/fixtures/sauce.py | 2 +- cfme/fixtures/service_fixtures.py | 4 +- cfme/fixtures/skip_not_implemented.py | 4 +- cfme/fixtures/smtp.py | 2 +- cfme/fixtures/soft_assert.py | 4 +- cfme/fixtures/ssh_client.py | 2 +- cfme/fixtures/tccheck.py | 4 +- cfme/fixtures/templateloader.py | 2 +- cfme/fixtures/templates.py | 221 ++++++++++++++ cfme/fixtures/ui_coverage.py | 2 +- cfme/fixtures/v2v_fixtures.py | 9 +- cfme/fixtures/video.py | 6 +- cfme/fixtures/xunit_tools.py | 8 +- .../definition/definition_views.py | 2 +- cfme/generic_objects/instance/ui.py | 2 +- cfme/infrastructure/cluster.py | 2 +- cfme/infrastructure/config_management.py | 64 ++-- cfme/infrastructure/datastore.py | 2 +- cfme/infrastructure/pxe.py | 2 +- cfme/infrastructure/virtual_machines.py | 2 +- cfme/intelligence/chargeback/__init__.py | 17 +- cfme/intelligence/chargeback/assignments.py | 2 +- cfme/intelligence/chargeback/rates.py | 4 +- cfme/intelligence/reports/__init__.py | 2 +- cfme/intelligence/rss.py | 2 +- cfme/intelligence/timelines.py | 2 +- cfme/markers/__init__.py | 18 +- cfme/markers/destructive.py | 9 + cfme/markers/env.py | 13 + cfme/markers/env_markers/provider.py | 129 +++++--- cfme/markers/fixtureconf.py | 2 +- cfme/markers/isolation.py | 8 +- cfme/markers/manual.py | 4 +- cfme/markers/marker_filters.py | 10 +- cfme/markers/meta.py | 13 +- cfme/markers/perf.py | 10 + cfme/markers/polarion.py | 6 +- cfme/markers/regression.py | 9 + cfme/markers/rhel_tests.py | 9 + cfme/markers/rhv.py | 4 +- cfme/markers/skipper.py | 14 +- cfme/markers/smoke.py | 7 +- cfme/markers/stream_excluder.py | 2 +- cfme/markers/uncollect.py | 22 +- cfme/markers/uses.py | 23 +- cfme/networks/views.py | 2 +- cfme/optimize/__init__.py | 2 +- cfme/physical/physical_chassis.py | 2 +- cfme/physical/physical_rack.py | 2 +- cfme/physical/physical_storage.py | 2 +- cfme/physical/provider/__init__.py | 2 +- cfme/services/catalogs/__init__.py | 2 +- cfme/services/myservice/ui.py | 2 +- cfme/services/requests.py | 2 +- cfme/services/service_catalogs/ui.py | 2 +- cfme/services/workloads.py | 2 +- cfme/storage/volume_type.py | 2 +- cfme/test_framework/appliance.py | 3 +- cfme/test_framework/browser_isolation.py | 2 +- cfme/test_framework/pytest_plugin.py | 94 +----- .../ansible/test_embedded_ansible_services.py | 2 +- .../automate/custom_button/test_buttons.py | 10 +- .../custom_button/test_service_objects.py | 2 +- .../test_generic_class_custom_button.py | 2 +- .../generic_objects/test_definitions.py | 2 +- cfme/tests/automate/test_vmware_methods.py | 2 +- cfme/tests/cli/test_appliance_update.py | 18 +- cfme/tests/cloud/test_cloud_timelines.py | 2 +- .../cloud/test_instance_power_control.py | 32 +- cfme/tests/cloud/test_keypairs.py | 2 +- cfme/tests/cloud/test_providers.py | 12 +- cfme/tests/cloud/test_quota_tagging.py | 4 +- cfme/tests/cloud/test_tag_mapping.py | 2 +- .../test_cloud_init_provisioning.py | 2 +- .../test_custom_attributes_rest.py | 4 +- .../test_power_control_manual.py | 2 +- .../cloud_infra_common/test_tag_visibility.py | 14 +- .../cloud_infra_common/test_vm_ownership.py | 2 +- .../configure/test_log_depot_operation.py | 1 - cfme/tests/configure/test_tag.py | 2 +- cfme/tests/configure/test_workers.py | 1 - cfme/tests/conftest.py | 0 .../containers/test_configurable_menus.py | 2 +- cfme/tests/control/test_alerts.py | 8 +- .../infrastructure/test_config_management.py | 4 +- .../test_config_management_rest.py | 5 +- .../infrastructure/test_datastore_analysis.py | 50 ++-- cfme/tests/infrastructure/test_host.py | 2 +- cfme/tests/infrastructure/test_providers.py | 2 +- .../test_provisioning_dialog.py | 4 +- cfme/tests/infrastructure/test_snapshot.py | 28 +- .../tests/infrastructure/test_tenant_quota.py | 2 +- cfme/tests/infrastructure/test_timelines.py | 4 +- .../tests/infrastructure/test_vm_ownership.py | 2 +- .../infrastructure/test_vm_power_control.py | 8 +- .../infrastructure/test_vm_reconfigure.py | 45 +-- .../infrastructure/test_vmware_provider.py | 20 +- cfme/tests/networks/test_sdn_balancers.py | 2 +- .../networks/test_sdn_inventory_collection.py | 8 +- cfme/tests/pod/test_appliance_crud.py | 8 +- .../test_ansible_workflow_servicecatalogs.py | 3 +- cfme/tests/services/test_catalog_item.py | 2 +- .../test_config_provider_servicecatalogs.py | 3 +- cfme/tests/test_modules_importable.py | 15 +- cfme/tests/v2v/test_migration_throttling.py | 6 +- cfme/tests/v2v/test_post_migrations.py | 2 +- cfme/tests/v2v/test_v2v_ansible.py | 2 +- cfme/tests/v2v/test_v2v_cancel_migrations.py | 2 +- cfme/tests/v2v/test_v2v_migrations.py | 20 +- .../v2v/test_v2v_migrations_single_vcenter.py | 8 +- cfme/tests/v2v/test_v2v_migrations_ui.py | 2 +- cfme/tests/v2v/test_v2v_smoke.py | 2 +- cfme/utils/pytest_shortcuts.py | 14 +- cfme/utils/testgen.py | 12 +- cfme/utils/tests/test_datafile_fixture.py | 20 +- cfme/utils/tests/test_fixtureconf_fixture.py | 3 +- cfme/utils/tests/test_ipappliance.py | 4 +- cfme/utils/tests/test_pytest_shortcuts.py | 3 +- cfme/utils/tests/test_soft_assert.py | 8 +- cfme/utils/tests/test_ssh_client.py | 3 +- cfme/v2v/infrastructure_mapping.py | 2 +- cfme/v2v/migration_plans.py | 2 +- cfme/v2v/migration_settings.py | 2 +- conftest.py | 101 +++++++ docs/guides/tipsntricks.rst | 2 +- entry_points.txt | 2 +- requirements/constraints.txt | 1 - requirements/frozen.txt | 5 +- requirements/frozen_docs.py3.txt | 2 +- requirements/template_non_imported.txt | 1 + requirements/template_scanned_imports.txt | 1 + 183 files changed, 1177 insertions(+), 1073 deletions(-) delete mode 100644 cfme/base/login.py create mode 100644 cfme/fixtures/templates.py create mode 100644 cfme/markers/destructive.py create mode 100644 cfme/markers/perf.py create mode 100644 cfme/markers/regression.py create mode 100644 cfme/markers/rhel_tests.py delete mode 100644 cfme/tests/conftest.py create mode 100644 conftest.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c0d3aec2c2..c692ebe0b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,18 +5,18 @@ substitutions: exclude: ^(cfme|scripts) # for now disable - we shall gradually reenable it repos: - repo: https://github.com/asottile/reorder_python_imports - rev: v1.3.5 + rev: v1.6.1 hooks: - id: reorder-python-imports - repo: https://github.com/ambv/black - rev: 18.9b0 + rev: 19.3b0 hooks: - id: black args: [--safe, --line-length, '100'] language_version: python3.7 <<: *exclude - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.0.0 + rev: v2.3.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -27,7 +27,7 @@ repos: language_version: python3.7 additional_dependencies: [polarion-docstrings, bugzilla-docstrings] - repo: https://github.com/asottile/pyupgrade - rev: v1.10.1 + rev: v1.23.0 hooks: - id: pyupgrade language_version: python3.7 diff --git a/.travis.yml b/.travis.yml index 9cf86e8f35..f473b8f612 100644 --- a/.travis.yml +++ b/.travis.yml @@ -77,9 +77,9 @@ jobs: - cp conf/env.yaml.template conf/env.yaml - cp conf/credentials.yaml.template conf/credentials.yaml script: - - pytest cfme/utils/tests cfme/modeling/tests requirements -v --dummy-appliance - - pytest -p no:cfme cfme/tests/test_modules_importable.py - - pytest -v --dummy-appliance --collectonly --include-manual + - pytest cfme/utils/tests cfme/modeling/tests requirements --dummy-appliance + - pytest -p no:cfme cfme/tests/test_modules_importable.py --dummy-appliance --long-running + - pytest -v --dummy-appliance --collect-only --include-manual --long-running after_success: - coveralls diff --git a/cfme/ansible/credentials.py b/cfme/ansible/credentials.py index 0db009b877..d623626cee 100644 --- a/cfme/ansible/credentials.py +++ b/cfme/ansible/credentials.py @@ -16,7 +16,7 @@ from widgetastic_patternfly import Input from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import Taggable from cfme.common import TagPageView from cfme.exceptions import ItemNotFound diff --git a/cfme/ansible/playbooks.py b/cfme/ansible/playbooks.py index faecf74e08..fad47d3568 100644 --- a/cfme/ansible/playbooks.py +++ b/cfme/ansible/playbooks.py @@ -10,7 +10,7 @@ from widgetastic_patternfly import Dropdown from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import Taggable from cfme.common import TagPageView from cfme.modeling.base import BaseCollection diff --git a/cfme/ansible/repositories.py b/cfme/ansible/repositories.py index b16ee4fc11..911ed2bbde 100644 --- a/cfme/ansible/repositories.py +++ b/cfme/ansible/repositories.py @@ -14,7 +14,7 @@ from widgetastic_patternfly import Input from cfme.ansible.playbooks import PlaybooksCollection -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import Taggable from cfme.common import TagPageView from cfme.exceptions import ItemNotFound diff --git a/cfme/ansible_tower/explorer.py b/cfme/ansible_tower/explorer.py index e6811b6f99..cd6afb610b 100644 --- a/cfme/ansible_tower/explorer.py +++ b/cfme/ansible_tower/explorer.py @@ -7,7 +7,7 @@ from widgetastic_patternfly import Dropdown from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import Taggable from cfme.common import TaggableCollection from cfme.exceptions import ItemNotFound diff --git a/cfme/ansible_tower/jobs.py b/cfme/ansible_tower/jobs.py index 69ab14cd8a..5b08d05991 100644 --- a/cfme/ansible_tower/jobs.py +++ b/cfme/ansible_tower/jobs.py @@ -6,7 +6,7 @@ from widgetastic_patternfly import BootstrapNav from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.utils.appliance.implementations.ui import CFMENavigateStep diff --git a/cfme/automate/__init__.py b/cfme/automate/__init__.py index 1733cc9e8e..9e1ea247d2 100644 --- a/cfme/automate/__init__.py +++ b/cfme/automate/__init__.py @@ -8,7 +8,7 @@ from widgetastic_patternfly import Input from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator from widgetastic_manageiq import Accordion diff --git a/cfme/automate/dialog_import_export.py b/cfme/automate/dialog_import_export.py index 29a772b6ac..571ea4f4d1 100644 --- a/cfme/automate/dialog_import_export.py +++ b/cfme/automate/dialog_import_export.py @@ -5,7 +5,7 @@ from widgetastic.widget import View from widgetastic_patternfly import Button -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance import NavigatableMixin from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigate_to diff --git a/cfme/automate/dialogs/__init__.py b/cfme/automate/dialogs/__init__.py index 05434ca93b..f0d8bd8297 100644 --- a/cfme/automate/dialogs/__init__.py +++ b/cfme/automate/dialogs/__init__.py @@ -5,7 +5,7 @@ from widgetastic_patternfly import Dropdown from widgetastic_patternfly import Input -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from widgetastic_manageiq import Accordion from widgetastic_manageiq import DialogButton from widgetastic_manageiq import DialogElement diff --git a/cfme/automate/explorer/__init__.py b/cfme/automate/explorer/__init__.py index 554e173ab4..cdb750d98d 100644 --- a/cfme/automate/explorer/__init__.py +++ b/cfme/automate/explorer/__init__.py @@ -6,7 +6,7 @@ from widgetastic_patternfly import Dropdown from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator from widgetastic_manageiq import Accordion diff --git a/cfme/automate/import_export.py b/cfme/automate/import_export.py index 9d39b7f6c9..7a82a2a3ed 100644 --- a/cfme/automate/import_export.py +++ b/cfme/automate/import_export.py @@ -10,7 +10,7 @@ from widgetastic_patternfly import FlashMessages from widgetastic_patternfly import Input -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.utils.appliance.implementations.ui import CFMENavigateStep diff --git a/cfme/automate/provisioning_dialogs.py b/cfme/automate/provisioning_dialogs.py index 455a773566..aba7b6dd68 100644 --- a/cfme/automate/provisioning_dialogs.py +++ b/cfme/automate/provisioning_dialogs.py @@ -9,7 +9,7 @@ from widgetastic_patternfly import Dropdown from cfme.automate import AutomateCustomizationView -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.utils.appliance.implementations.ui import CFMENavigateStep diff --git a/cfme/base/login.py b/cfme/base/login.py deleted file mode 100644 index 372156736a..0000000000 --- a/cfme/base/login.py +++ /dev/null @@ -1,129 +0,0 @@ -from widgetastic.exceptions import NoSuchElementException -from widgetastic.widget import Text -from widgetastic.widget import View -from widgetastic_patternfly import BreadCrumb -from widgetastic_patternfly import FlashMessages -from widgetastic_patternfly import NavDropdown -from widgetastic_patternfly import VerticalNavigation - -from cfme.exceptions import CFMEException -from widgetastic_manageiq import SettingsNavDropdown - - -class BaseLoggedInPage(View): - """This page should be subclassed by any page that models any other page that is available as - logged in. - """ - CSRF_TOKEN = '//meta[@name="csrf-token"]' - flash = FlashMessages('.//div[@id="flash_msg_div"]') - # TODO don't use `help` here, its a built-in - help = NavDropdown(id="help-menu") - configuration_settings = Text('//li[.//a[@title="Configuration"]]') # 5.11+ - settings = SettingsNavDropdown(id="dropdownMenu2") - navigation = VerticalNavigation('#maintab') - breadcrumb = BreadCrumb() - - @property - def is_displayed(self): - return self.logged_in_as_current_user - - def logged_in_as_user(self, user): - if self.logged_out: - return False - - return user.name == self.current_fullname - - def change_group(self, group_name): - """ From the settings menu change to the group specified by 'group_name' - Only available in versions >= 5.9 - - User is required to be currently logged in - """ - if not self.logged_in_as_user: - raise CFMEException("Unable to change group when a user is not logged in") - - if group_name not in self.group_names: - raise CFMEException("{} is not an assigned group for {}".format( - group_name, self.current_username)) - - self.settings.groups.select_item(group_name) - - return True - - @property - def logged_in_as_current_user(self): - return self.logged_in_as_user(self.extra.appliance.user) - - # TODO remove this property, it is erroneous. View properties should be returning data from UI - @property - def current_username(self): - try: - return self.extra.appliance.user.principal - except AttributeError: - return None - - @property - def current_fullname(self): - try: - # When the view isn't displayed self.settings.text is None, resulting in AttributeError - return self.settings.text.strip().split('|', 1)[0].strip() - except AttributeError: - return None - - @property - def current_groupname(self): - current_groups = self.settings.groups.items - - # User is only assigned to one group - if len(current_groups) == 1: - return current_groups[0] - - for group in current_groups: - if self.settings.groups.SELECTED_GROUP_MARKER in group: - return group.replace(self.settings.groups.SELECTED_GROUP_MARKER, '') - else: - # Handle some weird case where we don't detect a current group - raise CFMEException("User is not currently assigned to a group") - - @property - def group_names(self): - """ Return a list of the logged in user's assigned groups. - - Returns: - list containing all groups the logged in user is assigned to - """ - - return [ - group.replace(self.settings.groups.SELECTED_GROUP_MARKER, '') - for group in self.settings.groups.items] - - @property - def logged_in(self): - return self.settings.is_displayed - - @property - def logged_out(self): - return not self.logged_in - - def logout(self): - self.settings.select_item('Logout') - self.browser.handle_alert(wait=None) - self.extra.appliance.user = None - - @property - def csrf_token(self): - return self.browser.get_attribute('content', self.CSRF_TOKEN) - - @csrf_token.setter - def csrf_token(self, value): - self.browser.set_attribute('content', value, self.CSRF_TOKEN) - - @property - def unexpected_error(self): - if not self.browser.elements('//h1[contains(., "Unexpected error encountered")]'): - return None - try: - err_el = self.browser.element('//h2[contains(., "Error text:")]/following-sibling::h3') - return self.browser.text(err_el) - except NoSuchElementException: - return None diff --git a/cfme/base/ui.py b/cfme/base/ui.py index 034115c7fb..c297117927 100644 --- a/cfme/base/ui.py +++ b/cfme/base/ui.py @@ -24,7 +24,7 @@ from cfme.base import Zone from cfme.base import ZoneCollection from cfme.base.credential import Credential -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.configure.about import AboutView from cfme.configure.configuration.server_settings import ServerAuthenticationView from cfme.configure.configuration.server_settings import ServerInformationView @@ -459,9 +459,7 @@ class Dashboard(CFMENavigateStep): prerequisite = NavigateToSibling('LoggedIn') def step(self, *args, **kwargs): - self.prerequisite_view.navigation.select( - self.obj.intel_name, "Dashboard" - ) + self.prerequisite_view.navigation.select(self.obj.intel_name, "Dashboard") @navigator.register(Server) @@ -470,9 +468,7 @@ class Chargeback(CFMENavigateStep): prerequisite = NavigateToSibling('LoggedIn') def step(self, *args, **kwargs): - self.prerequisite_view.navigation.select( - self.obj.intel_name, "Chargeback" - ) + self.prerequisite_view.navigation.select(self.obj.intel_name, "Chargeback") class ServerView(ConfigurationView): diff --git a/cfme/cloud/availability_zone.py b/cfme/cloud/availability_zone.py index 990e6cba52..92f544e8b0 100644 --- a/cfme/cloud/availability_zone.py +++ b/cfme/cloud/availability_zone.py @@ -9,7 +9,7 @@ from widgetastic_patternfly import Button from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import CustomButtonEventsMixin from cfme.common import Taggable from cfme.common import TimelinesView diff --git a/cfme/cloud/host_aggregates.py b/cfme/cloud/host_aggregates.py index f44af4bd67..8bd3be610d 100644 --- a/cfme/cloud/host_aggregates.py +++ b/cfme/cloud/host_aggregates.py @@ -5,7 +5,7 @@ from widgetastic_patternfly import BootstrapNav from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.utils.appliance.implementations.ui import CFMENavigateStep diff --git a/cfme/cloud/instance/__init__.py b/cfme/cloud/instance/__init__.py index 69b609a23d..a9809b0676 100644 --- a/cfme/cloud/instance/__init__.py +++ b/cfme/cloud/instance/__init__.py @@ -9,7 +9,7 @@ from widgetastic_patternfly import CheckableBootstrapTreeview from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import TimelinesView from cfme.common.vm import VM from cfme.common.vm import VMCollection diff --git a/cfme/cloud/provider/__init__.py b/cfme/cloud/provider/__init__.py index 4a2d129e78..76c85e7565 100644 --- a/cfme/cloud/provider/__init__.py +++ b/cfme/cloud/provider/__init__.py @@ -8,9 +8,9 @@ from widgetastic_patternfly import BreadCrumb from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage from cfme.cloud.instance.image import Image from cfme.cloud.tenant import ProviderTenantAllView +from cfme.common import BaseLoggedInPage from cfme.common import PolicyProfileAssignable from cfme.common import Taggable from cfme.common import TagPageView diff --git a/cfme/common/__init__.py b/cfme/common/__init__.py index 4d83365c7a..1e206a302b 100644 --- a/cfme/common/__init__.py +++ b/cfme/common/__init__.py @@ -15,9 +15,12 @@ from widgetastic_patternfly import Button from widgetastic_patternfly import CheckableBootstrapTreeview from widgetastic_patternfly import DropdownItemNotFound +from widgetastic_patternfly import FlashMessages +from widgetastic_patternfly import NavDropdown from widgetastic_patternfly import SelectItemNotFound +from widgetastic_patternfly import VerticalNavigation -from cfme.base.login import BaseLoggedInPage +from cfme.exceptions import CFMEException from cfme.exceptions import DestinationNotFound from cfme.exceptions import displayed_not_implemented from cfme.exceptions import ItemNotFound @@ -35,6 +38,126 @@ from widgetastic_manageiq import BaseNonInteractiveEntitiesView from widgetastic_manageiq import ReactSelect from widgetastic_manageiq import ServerTimelinesView +from widgetastic_manageiq import SettingsNavDropdown + + +class BaseLoggedInPage(View): + """This page should be subclassed by any page that models any other page that is available as + logged in. + """ + CSRF_TOKEN = '//meta[@name="csrf-token"]' + flash = FlashMessages('.//div[@id="flash_msg_div"]') + # TODO don't use `help` here, its a built-in + help = NavDropdown(id="help-menu") + configuration_settings = Text('//li[.//a[@title="Configuration"]]') # 5.11+ + settings = SettingsNavDropdown(id="dropdownMenu2") + navigation = VerticalNavigation('#maintab') + breadcrumb = BreadCrumb() + + @property + def is_displayed(self): + return self.logged_in_as_current_user + + def logged_in_as_user(self, user): + if self.logged_out: + return False + + return user.name == self.current_fullname + + def change_group(self, group_name): + """ From the settings menu change to the group specified by 'group_name' + Only available in versions >= 5.9 + + User is required to be currently logged in + """ + if not self.logged_in_as_user: + raise CFMEException("Unable to change group when a user is not logged in") + + if group_name not in self.group_names: + raise CFMEException("{} is not an assigned group for {}".format( + group_name, self.current_username)) + + self.settings.groups.select_item(group_name) + + return True + + @property + def logged_in_as_current_user(self): + return self.logged_in_as_user(self.extra.appliance.user) + + # TODO remove this property, it is erroneous. View properties should be returning data from UI + @property + def current_username(self): + try: + return self.extra.appliance.user.principal + except AttributeError: + return None + + @property + def current_fullname(self): + try: + # When the view isn't displayed self.settings.text is None, resulting in AttributeError + return self.settings.text.strip().split('|', 1)[0].strip() + except AttributeError: + return None + + @property + def current_groupname(self): + current_groups = self.settings.groups.items + + # User is only assigned to one group + if len(current_groups) == 1: + return current_groups[0] + + for group in current_groups: + if self.settings.groups.SELECTED_GROUP_MARKER in group: + return group.replace(self.settings.groups.SELECTED_GROUP_MARKER, '') + else: + # Handle some weird case where we don't detect a current group + raise CFMEException("User is not currently assigned to a group") + + @property + def group_names(self): + """ Return a list of the logged in user's assigned groups. + + Returns: + list containing all groups the logged in user is assigned to + """ + + return [ + group.replace(self.settings.groups.SELECTED_GROUP_MARKER, '') + for group in self.settings.groups.items] + + @property + def logged_in(self): + return self.settings.is_displayed + + @property + def logged_out(self): + return not self.logged_in + + def logout(self): + self.settings.select_item('Logout') + self.browser.handle_alert(wait=None) + self.extra.appliance.user = None + + @property + def csrf_token(self): + return self.browser.get_attribute('content', self.CSRF_TOKEN) + + @csrf_token.setter + def csrf_token(self, value): + self.browser.set_attribute('content', value, self.CSRF_TOKEN) + + @property + def unexpected_error(self): + if not self.browser.elements('//h1[contains(., "Unexpected error encountered")]'): + return None + try: + err_el = self.browser.element('//h2[contains(., "Error text:")]/following-sibling::h3') + return self.browser.text(err_el) + except NoSuchElementException: + return None class ManagePoliciesView(BaseLoggedInPage): diff --git a/cfme/common/host_views.py b/cfme/common/host_views.py index 817f9b9c22..aa96438ddf 100644 --- a/cfme/common/host_views.py +++ b/cfme/common/host_views.py @@ -10,7 +10,7 @@ from widgetastic_patternfly import CheckableBootstrapTreeview from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import TimelinesView from cfme.exceptions import displayed_not_implemented from cfme.utils.log import logger diff --git a/cfme/common/physical_server_views.py b/cfme/common/physical_server_views.py index 5a3770caa9..156dc756d4 100644 --- a/cfme/common/physical_server_views.py +++ b/cfme/common/physical_server_views.py @@ -9,7 +9,7 @@ from widgetastic_patternfly import BreadCrumb from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import TimelinesView from widgetastic_manageiq import BaseEntitiesView from widgetastic_manageiq import BaseNonInteractiveEntitiesView diff --git a/cfme/common/physical_switch_views.py b/cfme/common/physical_switch_views.py index af2e593a35..4a1bfe67ce 100644 --- a/cfme/common/physical_switch_views.py +++ b/cfme/common/physical_switch_views.py @@ -7,7 +7,7 @@ from widgetastic_patternfly import Accordion from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import TimelinesView from widgetastic_manageiq import BaseEntitiesView from widgetastic_manageiq import BreadCrumb diff --git a/cfme/common/provider_views.py b/cfme/common/provider_views.py index f3326b3afb..9c8536fa73 100644 --- a/cfme/common/provider_views.py +++ b/cfme/common/provider_views.py @@ -10,7 +10,7 @@ from widgetastic_patternfly import BreadCrumb from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import TimelinesView from cfme.common.host_views import HostEntitiesView from cfme.utils.version import VersionPicker diff --git a/cfme/common/topology.py b/cfme/common/topology.py index 1021532bf2..969d0d7d00 100644 --- a/cfme/common/topology.py +++ b/cfme/common/topology.py @@ -9,7 +9,7 @@ from widgetastic_patternfly import Button from widgetastic_patternfly import Input -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.utils.appliance.implementations.ui import navigate_to diff --git a/cfme/common/vm.py b/cfme/common/vm.py index 673e7bc6a3..2c014eed9b 100644 --- a/cfme/common/vm.py +++ b/cfme/common/vm.py @@ -12,7 +12,7 @@ from riggerlib import recursive_update from widgetastic.exceptions import NoSuchElementException -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import CustomButtonEventsMixin from cfme.common import PolicyProfileAssignable from cfme.common import Taggable diff --git a/cfme/common/vm_views.py b/cfme/common/vm_views.py index 3e24d5e6ff..e0cbf13079 100644 --- a/cfme/common/vm_views.py +++ b/cfme/common/vm_views.py @@ -14,7 +14,7 @@ from widgetastic_patternfly import Dropdown from widgetastic_patternfly import Input -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.exceptions import displayed_not_implemented from cfme.exceptions import ItemNotFound from cfme.utils.log import logger diff --git a/cfme/configure/configuration/analysis_profile.py b/cfme/configure/configuration/analysis_profile.py index b6b8b7b75a..24f44ef886 100644 --- a/cfme/configure/configuration/analysis_profile.py +++ b/cfme/configure/configuration/analysis_profile.py @@ -11,8 +11,8 @@ from widgetastic_patternfly import Dropdown from widgetastic_patternfly import TextInput -from cfme.base.login import BaseLoggedInPage from cfme.base.ui import ConfigurationView +from cfme.common import BaseLoggedInPage from cfme.exceptions import OptionNotAvailable from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity diff --git a/cfme/configure/settings.py b/cfme/configure/settings.py index 693b702e9c..9be1bae222 100644 --- a/cfme/configure/settings.py +++ b/cfme/configure/settings.py @@ -13,7 +13,7 @@ from cfme.base import BaseCollection from cfme.base import BaseEntity -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance import NavigatableMixin from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigate_to diff --git a/cfme/configure/tasks.py b/cfme/configure/tasks.py index ebe59e8243..e9842fe186 100644 --- a/cfme/configure/tasks.py +++ b/cfme/configure/tasks.py @@ -9,7 +9,7 @@ from widgetastic.widget import View from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.utils.appliance.implementations.ui import CFMENavigateStep diff --git a/cfme/containers/build.py b/cfme/containers/build.py index 23df8c6426..8bd66c40d6 100644 --- a/cfme/containers/build.py +++ b/cfme/containers/build.py @@ -5,7 +5,7 @@ from widgetastic_patternfly import BootstrapNav from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.utils.appliance.implementations.ui import CFMENavigateStep diff --git a/cfme/containers/overview.py b/cfme/containers/overview.py index 4463a06035..0893f0e08f 100644 --- a/cfme/containers/overview.py +++ b/cfme/containers/overview.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from navmazing import NavigateToAttribute -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance import Navigatable from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator diff --git a/cfme/containers/provider/__init__.py b/cfme/containers/provider/__init__.py index 82372d0ba2..4a9cf53a80 100644 --- a/cfme/containers/provider/__init__.py +++ b/cfme/containers/provider/__init__.py @@ -18,7 +18,7 @@ from widgetastic_patternfly import SelectorDropdown from cfme.base.credential import TokenCredential -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import PolicyProfileAssignable from cfme.common import TaggableCollection from cfme.common import TagPageView diff --git a/cfme/control/explorer/__init__.py b/cfme/control/explorer/__init__.py index 03118fbbc9..b0ee4f15af 100644 --- a/cfme/control/explorer/__init__.py +++ b/cfme/control/explorer/__init__.py @@ -5,7 +5,7 @@ from widgetastic_patternfly import Dropdown from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator from widgetastic_manageiq import ManageIQTree diff --git a/cfme/control/explorer/alerts.py b/cfme/control/explorer/alerts.py index 2b86a73f73..d50bfe421c 100644 --- a/cfme/control/explorer/alerts.py +++ b/cfme/control/explorer/alerts.py @@ -14,7 +14,7 @@ from widgetastic_patternfly import Input from widgetastic_patternfly import SelectorDropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.control.explorer import ControlExplorerView from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity diff --git a/cfme/control/import_export.py b/cfme/control/import_export.py index 56db4600b3..0359830d59 100644 --- a/cfme/control/import_export.py +++ b/cfme/control/import_export.py @@ -5,8 +5,8 @@ from widgetastic_patternfly import BootstrapSelect from widgetastic_patternfly import Button -from cfme.base.login import BaseLoggedInPage from cfme.base.ui import Server +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigate_to from cfme.utils.appliance.implementations.ui import navigator diff --git a/cfme/control/log.py b/cfme/control/log.py index c2373b79bf..3d88909289 100644 --- a/cfme/control/log.py +++ b/cfme/control/log.py @@ -4,7 +4,7 @@ from widgetastic_patternfly import Button from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator diff --git a/cfme/control/simulation.py b/cfme/control/simulation.py index f6d402862c..990dad0362 100644 --- a/cfme/control/simulation.py +++ b/cfme/control/simulation.py @@ -7,7 +7,7 @@ from widgetastic_patternfly import Button from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator from widgetastic_manageiq import ManageIQTree diff --git a/cfme/dashboard.py b/cfme/dashboard.py index e3c410dd86..22f6d56df5 100644 --- a/cfme/dashboard.py +++ b/cfme/dashboard.py @@ -16,7 +16,7 @@ from widgetastic_patternfly import Button from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.utils.appliance.implementations.ui import CFMENavigateStep diff --git a/cfme/fixtures/appliance_update.py b/cfme/fixtures/appliance_update.py index 0016c6674a..b19d654cd9 100644 --- a/cfme/fixtures/appliance_update.py +++ b/cfme/fixtures/appliance_update.py @@ -5,7 +5,7 @@ 1) If only ``--update-appliance`` is specified, it will use the YAML url. 2) If you also specify one or more ``--update-url``, it will use them instead. """ -import pytest +from cfme.fixtures.pytest_store import store def pytest_addoption(parser): @@ -21,13 +21,13 @@ def pytest_addoption(parser): def pytest_sessionstart(session): - if pytest.store.parallelizer_role == 'master': + if store.parallelizer_role == 'master': return if not session.config.getoption("update_appliance"): return - pytest.store.write_line("Initiating appliance update ...") + store.write_line("Initiating appliance update ...") urls = session.config.getoption("update_urls") - pytest.store.current_appliance.update_rhel(*urls, reboot=True) - pytest.store.write_line("Appliance update finished, waiting for UI ...") - pytest.store.current_appliance.wait_for_web_ui() - pytest.store.write_line("Appliance update finished ...") + store.current_appliance.update_rhel(*urls, reboot=True) + store.write_line("Appliance update finished, waiting for UI ...") + store.current_appliance.wait_for_web_ui() + store.write_line("Appliance update finished ...") diff --git a/cfme/fixtures/artifactor_plugin.py b/cfme/fixtures/artifactor_plugin.py index 7451bbc763..e8619573e4 100644 --- a/cfme/fixtures/artifactor_plugin.py +++ b/cfme/fixtures/artifactor_plugin.py @@ -119,7 +119,7 @@ def pytest_addoption(parser): help="A run id to assist in logging") -@pytest.mark.tryfirst +@pytest.hookimpl(tryfirst=True) def pytest_configure(config): if config.getoption('--help'): return @@ -171,7 +171,7 @@ def fire_art_test_hook(node, hook, **hook_args): **hook_args) -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_runtest_protocol(item): global session_ver global session_build @@ -199,11 +199,11 @@ def pytest_runtest_protocol(item): fw_version=session_fw_version ) - tier = item.get_marker('tier') + tier = item.get_closest_marker('tier') if tier: tier = tier.args[0] - requirement = item.get_marker('requirement') + requirement = item.get_closest_marker('requirement') if requirement: requirement = requirement.args[0] @@ -217,7 +217,7 @@ def pytest_runtest_protocol(item): # This pre_start_test hook is needed so that filedump is able to make get the test # object set up before the logger starts logging. As the logger fires a nested hook # to the filedumper, and we can't specify order inriggerlib. - meta = item.get_marker('meta') + meta = item.get_closest_marker('meta') if meta and 'blockers' in meta.kwargs: blocker_spec = meta.kwargs['blockers'] blockers = [] @@ -272,7 +272,7 @@ def pytest_runtest_teardown(item, nextitem): def pytest_runtest_logreport(report): if store.slave_manager: return # each node does its own reporting - config = pytest.config # tech debt + config = store.config # tech debt name, location = get_test_idents(report) xfail = hasattr(report, 'wasxfail') @@ -293,7 +293,7 @@ def pytest_runtest_logreport(report): fire_art_hook(config, 'build_report') -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_unconfigure(config): yield shutdown(config) diff --git a/cfme/fixtures/blockers.py b/cfme/fixtures/blockers.py index e7c3005f68..8aa0f132b4 100644 --- a/cfme/fixtures/blockers.py +++ b/cfme/fixtures/blockers.py @@ -65,7 +65,7 @@ def pytest_addoption(parser): help='Specify to list the blockers (takes some time though).') -@pytest.mark.trylast +@pytest.hookimpl(trylast=True) def pytest_collection_modifyitems(session, config, items): if not config.getvalue("list_blockers"): return diff --git a/cfme/fixtures/cli.py b/cfme/fixtures/cli.py index b8083e4b3e..d37daa1488 100644 --- a/cfme/fixtures/cli.py +++ b/cfme/fixtures/cli.py @@ -245,26 +245,26 @@ def get_apps(appliance, old_version, count, preconfigured, pytest_config): @pytest.fixture -def appliance_preupdate(appliance, old_version): +def appliance_preupdate(appliance, old_version, request): """Requests single appliance from sprout.""" with get_apps(appliance, old_version, count=1, preconfigured=True, - pytest_config=pytest.config) as apps: + pytest_config=request.config) as apps: yield apps[0] @pytest.fixture -def multiple_preupdate_appliances(appliance, old_version): +def multiple_preupdate_appliances(appliance, old_version, request): """Requests multiple appliances from sprout.""" with get_apps(appliance, old_version, count=2, preconfigured=False, - pytest_config=pytest.config) as apps: + pytest_config=request.config) as apps: yield apps @pytest.fixture -def ha_multiple_preupdate_appliances(appliance, old_version): +def ha_multiple_preupdate_appliances(appliance, old_version, request): """Requests multiple appliances from sprout.""" with get_apps(appliance, old_version, count=3, preconfigured=False, - pytest_config=pytest.config) as apps: + pytest_config=request.config) as apps: yield apps diff --git a/cfme/fixtures/dev_branch.py b/cfme/fixtures/dev_branch.py index e293dac0b6..ae5346a04a 100644 --- a/cfme/fixtures/dev_branch.py +++ b/cfme/fixtures/dev_branch.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import pytest +from cfme.fixtures.pytest_store import store + def pytest_addoption(parser): group = parser.getgroup('Upstream testing') @@ -17,17 +19,17 @@ def pytest_addoption(parser): def pytest_sessionstart(session): - if pytest.store.parallelizer_role == 'master': + if store.parallelizer_role == 'master': return if session.config.getoption("dev_repo") is None: return - if pytest.store.current_appliance.is_downstream: - pytest.store.write_line("Cannot git update downstream appliances ...") + if store.current_appliance.is_downstream: + store.write_line("Cannot git update downstream appliances ...") pytest.exit('Failed to git update this appliance, because it is downstream') dev_repo = session.config.getoption("dev_repo") dev_branch = session.config.getoption("dev_branch") - pytest.store.write_line( + store.write_line( "Changing the upstream appliance {} to {}#{} ...".format( - pytest.store.current_appliance.hostname, dev_repo, dev_branch)) - pytest.store.current_appliance.use_dev_branch(dev_repo, dev_branch) - pytest.store.write_line("Appliance change finished ...") + store.current_appliance.hostname, dev_repo, dev_branch)) + store.current_appliance.use_dev_branch(dev_repo, dev_branch) + store.write_line("Appliance change finished ...") diff --git a/cfme/fixtures/events.py b/cfme/fixtures/events.py index 547fdabc21..c590277ee2 100644 --- a/cfme/fixtures/events.py +++ b/cfme/fixtures/events.py @@ -66,7 +66,7 @@ def pytest_runtest_call(item): try: yield finally: - if "register_event" in item.funcargnames: + if "register_event" in item.fixturenames: event_listener = item.funcargs["register_event"] soft_assert = item.funcargs["soft_assert"] diff --git a/cfme/fixtures/log.py b/cfme/fixtures/log.py index 6c3a52515c..6e5317d443 100644 --- a/cfme/fixtures/log.py +++ b/cfme/fixtures/log.py @@ -3,6 +3,7 @@ import attr import pytest +from cfme.fixtures.pytest_store import store from cfme.utils import log from cfme.utils.appliance import find_appliance @@ -20,19 +21,19 @@ def pytest_configure(config): config.pluginmanager.register(LogExtraData(config)) -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_runtest_setup(item): path, lineno, domaininfo = item.location - logger().info(log.format_marker(_format_nodeid(item.nodeid), mark="-"), + log.logger.info(log.format_marker(_format_nodeid(item.nodeid), mark="-"), extra={'source_file': path, 'source_lineno': lineno}) yield def pytest_collection_modifyitems(session, config, items): - logger().info(log.format_marker('Starting new test run', mark="=")) + log.logger.info(log.format_marker('Starting new test run', mark="=")) expression = config.getvalue('keyword') or False expr_string = ', will filter with "{}"'.format(expression) if expression else '' - logger().info('Collected {} items{}'.format(len(items), expr_string)) + log.logger.info('Collected {} items{}'.format(len(items), expr_string)) @attr.s(frozen=True) @@ -44,32 +45,32 @@ def managed_known_providers(self): appliance = find_appliance(self.config) return [prov.key for prov in appliance.managed_known_providers] - @pytest.mark.hookwrapper + @pytest.hookimpl(hookwrapper=True) def pytest_runtest_logreport(self, report): # e.g. test_tracking['test_name']['setup'] = 'passed' # test_tracking['test_name']['call'] = 'skipped' # test_tracking['test_name']['teardown'] = 'failed' yield test_tracking[_format_nodeid(report.nodeid, False)][report.when] = report.outcome - if report.when == 'teardown' and pytest.store.parallel_session is None: + if report.when == 'teardown' and store.parallel_session is None: path, lineno, domaininfo = report.location test_status = _test_status(_format_nodeid(report.nodeid, False)) if test_status == "failed": try: - logger().info( + log.logger.info( "Managed providers: {}".format( ", ".join(self.managed_known_providers)) ) except KeyError as ex: if 'ext_management_systems' in ex.msg: - logger().warning("Unable to query ext_management_systems table; DB issue") + log.logger.warning("Unable to query ext_management_systems table; DB issue") else: raise - logger().info(log.format_marker('{} result: {}'.format(_format_nodeid(report.nodeid), + log.logger.info(log.format_marker('{} result: {}'.format(_format_nodeid(report.nodeid), test_status)), extra={'source_file': path, 'source_lineno': lineno}) if report.outcome == "skipped": - logger().info(log.format_marker(report.longreprtext)) + log.logger.info(log.format_marker(report.longreprtext)) def pytest_exception_interact(node, call, report): @@ -79,7 +80,7 @@ def pytest_exception_interact(node, call, report): # This is the same code that powers py.test's output, so we gain py.test's magical ability # to get useful AssertionError output by doing it this way, which makes the voodoo worth it. entry = call.excinfo.traceback.getcrashentry() - logger().error(call.excinfo.getrepr(), + log.logger.error(call.excinfo.getrepr(), extra={'source_file': entry.path, 'source_lineno': entry.lineno + 1}) @@ -92,8 +93,8 @@ def pytest_sessionfinish(session, exitstatus): '{}: {}'.format(k, v) for k, v in c.items()] # Then join it with commas summary = ', '.join(results) - logger().info(log.format_marker('Finished test run', mark='=')) - logger().info(log.format_marker(str(summary), mark='=')) + log.logger.info(log.format_marker('Finished test run', mark='=')) + log.logger.info(log.format_marker(str(summary), mark='=')) def _test_status(test_name): diff --git a/cfme/fixtures/parallelizer/__init__.py b/cfme/fixtures/parallelizer/__init__.py index a893bcd045..767e0a244f 100644 --- a/cfme/fixtures/parallelizer/__init__.py +++ b/cfme/fixtures/parallelizer/__init__.py @@ -69,7 +69,7 @@ def pytest_addhooks(pluginmanager): pluginmanager.add_hookspecs(hooks) -@pytest.mark.trylast +@pytest.hookimpl(trylast=True) def pytest_configure(config): """Configures the parallel session, then fires pytest_parallel_configured.""" if config.getoption('--help'): @@ -85,8 +85,7 @@ def pytest_configure(config): config.pluginmanager.register(session, "parallel_session") store.parallelizer_role = 'master' reporter.write_line( - 'As a parallelizer master kicking off parallel session for these {} appliances'.format( - len(appliances)), + f'Parallelizer master starting session for {len(appliances)} appliances', green=True) config.hook.pytest_parallel_configured(parallel_session=session) else: @@ -106,12 +105,11 @@ def handle_end_session(signal, frame): @attr.s(hash=False) class SlaveDetail(object): - slaveid_generator = ('slave{:02d}'.format(i).encode('ascii') for i in count()) + slaveid_generator = (f'slave{i:02d}'.encode('ascii') for i in count()) appliance = attr.ib() worker_config = attr.ib() - id = attr.ib(default=attr.Factory( - lambda: next(SlaveDetail.slaveid_generator))) + id = attr.ib(default=attr.Factory(lambda: next(SlaveDetail.slaveid_generator))) forbid_restart = attr.ib(default=False, init=False) tests = attr.ib(default=attr.Factory(set), repr=False) process = attr.ib(default=None, repr=False) @@ -133,6 +131,7 @@ def start(self): ], stdout=devnull) at_exit(self.process.kill) + at_exit(devnull.close) def poll(self): if self.process is not None: @@ -170,10 +169,9 @@ def __init__(self, config, appliances): # set up the ipc socket - zmq_endpoint = 'ipc://{}'.format( - config.cache.makedir('parallelize').join(str(os.getpid()))) - ctx = zmq.Context.instance() - self.sock = ctx.socket(zmq.ROUTER) + zmq_endpoint = f'ipc://{config.cache.makedir("parallelize").join(str(os.getpid()))}' + self.zmq_ctx = zmq.Context.instance() + self.sock = self.zmq_ctx.socket(zmq.ROUTER) self.sock.bind(zmq_endpoint) # clean out old slave config if it exists @@ -192,8 +190,9 @@ def __init__(self, config, appliances): self.slaves[slave_data.id] = slave_data for slave in sorted(self.slaves): - self.print_message("using appliance {}".format(self.slaves[slave].appliance.url), - slave, green=True) + self.print_message(f"using appliance {self.slaves[slave].appliance.url}", + slave, + green=True) def _slave_audit(self): # XXX: There is currently no mechanism to add or remove slave_urls, short of @@ -206,15 +205,14 @@ def _slave_audit(self): if returncode: slave.process = None if returncode == -9: - msg = '{} killed due to error, respawning'.format(slave.id) + msg = f'{slave.id} killed due to error, respawning' else: - msg = '{} terminated unexpectedly with status {}, respawning'.format( - slave.id, returncode) + msg = f'{slave.id} terminated unexpectedly with status {returncode}, respawning' if slave.tests: failed_tests, slave.tests = slave.tests, set() num_failed_tests = len(failed_tests) self.sent_tests -= num_failed_tests - msg += ' and redistributing {} tests'.format(num_failed_tests) + msg += f' and redistributing {num_failed_tests} tests' self.failed_slave_test_groups.append(failed_tests) self.print_message(msg, purple=True) @@ -228,8 +226,7 @@ def _slave_audit(self): del self.slaves[slave.id] else: # no hook call here, a future audit will handle the fallout - self.print_message( - "{}'s appliance has died, deactivating slave".format(slave.id)) + self.print_message(f"{slave.id}'s appliance has died, deactivating slave") self.interrupt(slave) else: if slave.process is None: @@ -249,14 +246,13 @@ def send(self, slave, event_data): def recv(self): # poll the zmq socket, populate the recv queue deque with responses - events = zmq.zmq_poll([(self.sock, zmq.POLLIN)], 50) if not events: return None, None, None slaveid, _, event_json = self.sock.recv_multipart(flags=zmq.NOBLOCK) event_data = json.loads(event_json) event_name = event_data.pop('_event_name') - if slaveid not in self.slaves: + if slaveid not in self.slaves: # its byte-string coming from recv self.log.error("message from terminated worker %s %s %s", slaveid, event_name, event_data) return None, None, None @@ -280,12 +276,11 @@ def print_message(self, message, prefix='master', **markup): else: markup = {'cyan': True} stamp = datetime.now().strftime("%Y%m%d %H:%M:%S") - self.terminal.write_ensure_prefix( - '({})[{}] '.format(prefix, stamp), message, **markup) + self.terminal.write_ensure_prefix(f'({prefix})[{stamp}] ', message, **markup) def ack(self, slave, event_name): """Acknowledge a slave's message""" - self.send(slave, 'ack {}'.format(event_name)) + self.send(slave, f'ack {event_name}') def monitor_shutdown(self, slave): # non-daemon so slaves get every opportunity to shut down cleanly @@ -315,9 +310,10 @@ def sleep_and_poll(): yield if polls % poll_report_modulo == 0: remaining_time = int(poll_num_sec - (time() - start_time)) - self.print_message('{} shutting down, will continue polling for {} seconds' - .format(slaveid.decode('ascii'), remaining_time), - blue=True) + self.print_message( + f'{slaveid} shutting down, will polling for {remaining_time} more seconds', + blue=True + ) sleep(poll_sleep_time) # start the poll @@ -327,13 +323,12 @@ def sleep_and_poll(): continue else: if ec == 0: - self.print_message('{} exited'.format(slaveid), green=True) + self.print_message(f'{slaveid} exited', green=True) else: - self.print_message('{} died'.format(slaveid), red=True) + self.print_message(f'{slaveid} died', red=True) break else: - self.print_message('{} failed to shut down gracefully; killed'.format(slaveid), - red=True) + self.print_message(f'{slaveid} failed to shut down gracefully; killed', red=True) process.kill() def interrupt(self, slave, **kwargs): @@ -370,6 +365,7 @@ def send_tests(self, slave): ) return tests + @pytest.hookimpl def pytest_sessionstart(self, session): """pytest sessionstart hook @@ -387,6 +383,7 @@ def pytest_sessionstart(self, session): self.config.pluginmanager.register(self.trdist, "terminaldistreporter") self.session = session + @pytest.hookimpl def pytest_runtestloop(self): """pytest runtest loop @@ -408,8 +405,10 @@ def pytest_runtestloop(self): slave.start() try: - self.print_message("Waiting for {} slave collections".format(len(self.slaves)), - red=True) + self.print_message( + f"Waiting for collection on {len(self.slaves)} pytest instances", + red=True + ) # Turn off the terminal reporter to suppress the builtin logstart printing terminalreporter.disable() @@ -436,7 +435,7 @@ def pytest_runtestloop(self): elif event_name == 'collectionfinish': slave_collection = event_data['node_ids'] # compare slave collection to the master, all test ids must be the same - self.log.debug('diffing {} collection'.format(slave.id)) + self.log.debug(f'diffing {slave.id} collection') diff_err = report_collection_diff( slave.id, self.collection, slave_collection) if diff_err: @@ -444,7 +443,7 @@ def pytest_runtestloop(self): 'collection differs, respawning', slave.id, purple=True) self.print_message(diff_err, purple=True) - self.log.error('{}'.format(diff_err)) + self.log.error(diff_err) self.kill(slave) slave.start() else: @@ -483,8 +482,7 @@ def pytest_runtestloop(self): red=True, bold=True) raise KeyboardInterrupt('Interrupted due to slave failures') except Exception as ex: - self.log.error('Exception in runtest loop:') - self.log.exception(ex) + self.log.exception('Exception in runtest loop:') self.print_message(str(ex)) raise finally: @@ -493,6 +491,10 @@ def pytest_runtestloop(self): # Suppress other runtestloop calls return True + @pytest.hookimpl(trylast=True) + def pytest_sessionfinish(self): + self.zmq_ctx.destroy() + def _test_item_generator(self): for tests in self._modscope_item_generator(): yield tests @@ -509,8 +511,7 @@ def get_fspart(nodeid): for fspath, gen_moditems in groupby(self.collection, key=get_fspart): for tests in self._modscope_id_splitter(gen_moditems): sent_tests += len(tests) - self.log.info('{} tests remaining to send'.format( - collection_len - sent_tests)) + self.log.info(f'{(collection_len - sent_tests)} tests remaining to send') yield list(tests) def _modscope_id_splitter(self, module_items): @@ -528,7 +529,7 @@ def _modscope_id_splitter(self, module_items): for id, tests in parametrized_ids.items(): if tests: - self.log.info('sent tests with param {} {!r}'.format(id, tests)) + self.log.info(f'sent tests with param {id} {tests!r}') yield tests def get(self, slave): @@ -587,7 +588,7 @@ def provs_of_tests(test_group): try: app.delete_all_providers() except Exception as e: - self.print_message('exception during provider removal: {}'.format(e), + self.print_message(f'exception during provider removal: {e}', slave, red=True) slave.provider_allocation = [prov] @@ -634,12 +635,14 @@ def __init__(self, config, terminal): self.tr = terminal self.outcomes = {} + @pytest.hookimpl def runtest_logstart(self, slaveid, nodeid, location): test = self.tr._locationline(nodeid, *location) - prefix = '({}) {}'.format(slaveid, test) + prefix = f'({slaveid}) {test}' self.tr.write_ensure_prefix(prefix, 'running', blue=True) self.config.hook.pytest_runtest_logstart(nodeid=nodeid, location=location) + @pytest.hookimpl def runtest_logreport(self, slaveid, report): # Run all the normal logreport hooks self.config.hook.pytest_runtest_logreport(report=report) @@ -651,7 +654,7 @@ def runtest_logreport(self, slaveid, report): self.tr.stats.setdefault(outcome, []).append(report) test = self.tr._locationline(report.nodeid, *report.location) - prefix = '({}) {}'.format(slaveid, test) + prefix = f'({slaveid}) {test}' try: # for some reason, pytest_report_teststatus returns a word, markup tuple # when the word would be 'XPASS', so unpack it here if that's the case diff --git a/cfme/fixtures/parallelizer/remote.py b/cfme/fixtures/parallelizer/remote.py index a08beac6ce..c4405e5b5f 100644 --- a/cfme/fixtures/parallelizer/remote.py +++ b/cfme/fixtures/parallelizer/remote.py @@ -1,6 +1,7 @@ import json import signal +import pytest import zmq from py.path import local @@ -27,7 +28,7 @@ def __init__(self, config, slaveid, zmq_endpoint): ctx = zmq.Context.instance() self.sock = ctx.socket(zmq.REQ) self.sock.set_hwm(1) - self.sock.setsockopt_string(zmq.IDENTITY, '{}'.format(self.slaveid)) + self.sock.setsockopt_string(zmq.IDENTITY, f'{self.slaveid}') self.sock.connect(zmq_endpoint) self.messages = {} @@ -36,14 +37,14 @@ def __init__(self, config, slaveid, zmq_endpoint): def send_event(self, name, **kwargs): kwargs['_event_name'] = name - self.log.debug("sending {} {!r}".format(name, kwargs)) + self.log.debug(f"sending {name} {kwargs!r}") self.sock.send_json(kwargs) recv = self.sock.recv_json() if recv == 'die': self.log.info('Slave instructed to die by master; shutting down') raise SystemExit() else: - self.log.debug('received "{!r}" from master'.format(recv)) + self.log.debug(f'received "{recv!r}" from master') if recv != 'ack': return recv @@ -51,6 +52,7 @@ def message(self, message, **kwargs): """Send a message to the master, which should get printed to the console""" self.send_event('message', message=message, markup=kwargs) # message! + @pytest.hookimpl def pytest_collection_finish(self, session): """pytest collection hook @@ -63,6 +65,7 @@ def pytest_collection_finish(self, session): terminalreporter.disable() self.send_event("collectionfinish", node_ids=list(self.collection.keys())) + @pytest.hookimpl(trylast=True) def pytest_runtest_logstart(self, nodeid, location): """pytest runtest logstart hook @@ -71,6 +74,7 @@ def pytest_runtest_logstart(self, nodeid, location): """ self.send_event("runtest_logstart", nodeid=nodeid, location=location) + @pytest.hookimpl(trylast=True) def pytest_runtest_logreport(self, report): """pytest runtest logreport hook @@ -84,23 +88,23 @@ def pytest_runtest_logreport(self, report): if test_status == "failed": appliance = find_appliance(self) try: - self.log.info( - "Managed providers: {}".format( - ", ".join([ - prov.key for prov in - appliance.managed_known_providers])) - ) + managed_providers = ", ".join([prov.key for prov in + appliance.managed_known_providers]) + self.log.info(f"Managed providers: {managed_providers}") except KeyError as ex: if 'ext_management_systems' in ex.msg: self.log.warning("Unable to query ext_management_systems table; DB issue") else: raise - self.log.info(log.format_marker('{} result: {}'.format(_format_nodeid(report.nodeid), - test_status)), - extra={'source_file': path, 'source_lineno': lineno}) + self.log.info( + log.format_marker(f'{_format_nodeid(report.nodeid)} result: {test_status}'), + extra={'source_file': path, 'source_lineno': lineno} + ) + if report.outcome == "skipped": self.log.info(log.format_marker(report.longreprtext)) + @pytest.hookimpl(tryfirst=True) def pytest_internalerror(self, excrepr): """pytest internal error hook @@ -108,12 +112,13 @@ def pytest_internalerror(self, excrepr): - reports short traceback to the py.test console """ - msg = 'INTERNALERROR> {}'.format(str(excrepr)) + msg = f'INTERNALERROR> {excrepr}' self.log.error(msg) # Only send the last line (exc type/message) to keep the pytest log clean - short_tb = 'INTERNALERROR> {}'.format(msg.strip().splitlines()[-1]) + short_tb = f'INTERNALERROR> {msg.strip().splitlines()[-1]}' self.send_event("internalerror", message=short_tb) + @pytest.hookimpl def pytest_runtestloop(self, session): """pytest runtest loop @@ -124,7 +129,7 @@ def pytest_runtestloop(self, session): for item, nextitem in self._test_generator(): if self.config.option.collectonly: - self.message('{}'.format(item.nodeid)) + self.message(f'{item.nodeid}') pass else: self.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) @@ -132,6 +137,7 @@ def pytest_runtestloop(self, session): break return True + @pytest.hookimpl(trylast=True) def pytest_sessionfinish(self): self.shutdown() @@ -142,6 +148,7 @@ def handle_quit(self): def shutdown(self): self.message('shutting down') self.send_event('shutdown') + self.sock.close() self.quit_signaled = True def _test_generator(self): @@ -191,9 +198,13 @@ def _init_config(slave_options, slave_args): import pytest # NOQA from _pytest.config import get_config config = get_config() + config.args = slave_args + config._preparse(config.args, addopts=False) + config.option.__dict__.update(slave_options) + # The master handles the result log, slaves shouldn't also write to it config.option.resultlog = None # Unset appliances to prevent the slaves from starting distributes tests :) @@ -209,9 +220,9 @@ def _init_config(slave_options, slave_args): parser = argparse.ArgumentParser() parser.add_argument('--worker', help='The worker id of this process') parser.add_argument('--appliance', help='The json data about the used appliance') - parser.add_argument('--ts', help='The timestap to use for collections') + parser.add_argument('--ts', help='The timestamp to use for collections') - parser.add_argument('--config', help='The timestap to use for collections') + parser.add_argument('--config', help='The config options for pytest, in json') args = parser.parse_args() # TODO: clean the logic up here @@ -247,6 +258,7 @@ def _init_config(slave_options, slave_args): slave_args = config.pop('args') slave_options = config.pop('options') + ip_address = appliance.hostname appliance_data = config["appliance_data"] if ip_address in appliance_data: diff --git a/cfme/fixtures/portset.py b/cfme/fixtures/portset.py index 96728334c7..cb42e7c6f1 100644 --- a/cfme/fixtures/portset.py +++ b/cfme/fixtures/portset.py @@ -19,7 +19,7 @@ def pytest_addoption(parser): help="Override appliance's SSH port.") -@pytest.mark.tryfirst +@pytest.hookimpl(tryfirst=True) def pytest_configure(config): if config.getoption('--help'): return diff --git a/cfme/fixtures/provider.py b/cfme/fixtures/provider.py index a2c37e9c00..88b1b45dc7 100644 --- a/cfme/fixtures/provider.py +++ b/cfme/fixtures/provider.py @@ -39,7 +39,6 @@ import random import sys from collections import defaultdict -from collections import Mapping import pytest from _pytest.compat import getimfunc @@ -50,7 +49,7 @@ from cfme.common.provider import BaseProvider from cfme.fixtures.artifactor_plugin import fire_art_test_hook from cfme.fixtures.pytest_store import store -from cfme.fixtures.templateloader import TEMPLATES +from cfme.markers.env_markers.provider import ProviderEnvironmentMarker from cfme.utils.appliance import ApplianceException from cfme.utils.log import logger from cfme.utils.providers import list_providers @@ -288,7 +287,7 @@ def setup_provider_modscope(request, provider): @pytest.fixture(scope='class') def setup_provider_clsscope(request, provider): - """Module-scoped fixture to set up a provider""" + """Class-scoped fixture to set up a provider""" return setup_or_skip(request, provider) @@ -299,218 +298,6 @@ def setup_provider_funcscope(request, provider): # ----------------------------------------------- -@pytest.fixture(scope="function") -def template(template_location, provider): - if template_location is not None: - o = provider.data - try: - for field in template_location: - o = o[field] - except (IndexError, KeyError): - logger.info("Cannot apply %r to %r in the template specification, ignoring.", field, o) - else: - if not isinstance(o, str): - raise ValueError("{!r} is not a string! (for template)".format(o)) - if not TEMPLATES: - # There is nothing in TEMPLATES, that means no trackerbot URL and no data pulled. - # This should normally not constitute an issue so continue. - return o - templates = TEMPLATES.get(provider.key) - if templates is not None: - if o in templates: - return o - logger.info("Wanted template %s on %s but it is not there!", o, provider.key) - pytest.skip('Template not available') - - -def _get_template(provider, template_type_name): - """Get the template name for the given template type - YAML is expected to have structure with a templates section in the provider: - provider: - templates: - small_template: - name: - creds: - big_template: - name: - creds: - Args: - provider (obj): Provider object to lookup template on - template_type_name (str): Template type to lookup (small_template, big_template, etc) - Returns: - (dict) template dictionary from the yaml, with name and creds key:value pairs - """ - try: - template_type = provider.data.templates.get(template_type_name) - except (AttributeError, KeyError): - logger.error("Wanted template %s on %s but it is not there!", template, provider.key) - pytest.skip('No {} for provider {}'.format(template_type_name, provider.key)) - if not isinstance(template_type, Mapping): - pytest.skip('Template mapping is incorrect, {} on provider {}' - .format(template_type_name, provider.key)) - return template_type - - -@pytest.fixture(scope="function") -def small_template(provider): - return _get_template(provider, 'small_template') - - -@pytest.fixture(scope="module") -def small_template_modscope(provider): - return _get_template(provider, 'small_template') - - -@pytest.fixture(scope="function") -def full_template(provider): - return _get_template(provider, 'full_template') - - -@pytest.fixture(scope="module") -def full_template_modscope(provider): - return _get_template(provider, 'full_template') - - -@pytest.fixture(scope="function") -def big_template(provider): - return _get_template(provider, 'big_template') - - -@pytest.fixture(scope="module") -def big_template_modscope(provider): - return _get_template(provider, 'big_template') - - -@pytest.fixture(scope="function") -def provisioning(provider): - try: - return provider.data['provisioning'] - except KeyError: - logger.warning('Tests using the provisioning fixture ' - 'should include required_fields in their ProviderFilter marker') - pytest.skip('Missing "provisioning" field in provider data') - - -@pytest.fixture(scope="function") -def console_template(provider): - return _get_template(provider, 'console_template') - - -@pytest.fixture(scope="module") -def console_template_modscope(provider): - return _get_template(provider, 'console_template') - - -@pytest.fixture(scope="function") -def ubuntu16_template(provider): - return _get_template(provider, 'ubuntu16_template') - - -@pytest.fixture(scope="module") -def ubuntu16_template_modscope(provider): - return _get_template(provider, 'ubuntu16_template') - - -@pytest.fixture(scope="function") -def rhel69_template(provider): - return _get_template(provider, 'rhel69_template') - - -@pytest.fixture(scope="module") -def rhel69_template_modscope(provider): - return _get_template(provider, 'rhel69_template') - - -@pytest.fixture(scope="function") -def rhel74_template(provider): - return _get_template(provider, 'rhel74_template') - - -@pytest.fixture(scope="module") -def rhel74_template_modscope(provider): - return _get_template(provider, 'rhel74_template') - - -@pytest.fixture(scope="function") -def win7_template(provider): - return _get_template(provider, 'win7_template') - - -@pytest.fixture(scope="module") -def win7_template_modscope(provider): - return _get_template(provider, 'win7_template') - - -@pytest.fixture(scope="function") -def win10_template(provider): - return _get_template(provider, 'win10_template') - - -@pytest.fixture(scope="module") -def win10_template_modscope(provider): - return _get_template(provider, 'win10_template') - - -@pytest.fixture(scope="function") -def win2012_template(provider): - return _get_template(provider, 'win2012_template') - - -@pytest.fixture(scope="module") -def win2012_template_modscope(provider): - return _get_template(provider, 'win2012_template') - - -@pytest.fixture(scope="function") -def win2016_template(provider): - return _get_template(provider, 'win2016_template') - - -@pytest.fixture(scope="module") -def win2016_template_modscope(provider): - return _get_template(provider, 'win2016_template') - - -@pytest.fixture(scope="function") -def dual_network_template(provider): - return _get_template(provider, 'dual_network_template') - - -@pytest.fixture(scope="module") -def dual_network_template_modscope(provider): - return _get_template(provider, 'dual_network_template') - - -@pytest.fixture(scope="function") -def dual_disk_template(provider): - return _get_template(provider, 'dual_disk_template') - - -@pytest.fixture(scope="module") -def dual_disk_template_modscope(provider): - return _get_template(provider, 'dual_disk_template') - - -@pytest.fixture(scope="function") -def dportgroup_template(provider): - return _get_template(provider, 'dportgroup_template') - - -@pytest.fixture(scope="module") -def dportgroup_template_modscope(provider): - return _get_template(provider, 'dportgroup_template') - - -@pytest.fixture(scope="function") -def rhel7_minimal(provider): - return _get_template(provider, 'rhel7_minimal') - - -@pytest.fixture(scope="module") -def rhel7_minimal_modscope(provider): - return _get_template(provider, 'rhel7_minimal') - - def _walk_to_obj_parent(obj): old = None while True: @@ -524,7 +311,7 @@ def _walk_to_obj_parent(obj): return obj -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_fixture_setup(fixturedef, request): # since we use DataProvider at collection time and BaseProvider in fixtures and tests, # we need to instantiate BaseProvider and replace DataProvider obj with it right before first @@ -534,36 +321,36 @@ def pytest_fixture_setup(fixturedef, request): # As the object may not be the root object and may have a parent, we need to walk to that # the object to see if we can find the attribute on it or any of its parents - if hasattr(_walk_to_obj_parent(request).function, 'provider'): - marks = _walk_to_obj_parent(request).function.provider._marks - - for mark in marks: - if mark.kwargs.get('fixture_name', 'provider') == fixturedef.argname: - kwargs = {} - for argname in fixturedef.argnames: - fixdef = request._get_active_fixturedef(argname) - result, arg_cache_key, exc = fixdef.cached_result - request._check_scope(argname, request.scope, fixdef.scope) - kwargs[argname] = result - - fixturefunc = fixturedef.func - if request.instance is not None: - fixturefunc = getimfunc(fixturedef.func) - if fixturefunc != fixturedef.func: - fixturefunc = fixturefunc.__get__(request.instance) - my_cache_key = request.param_index - try: - provider_data = call_fixture_func(fixturefunc, request, kwargs) - except TEST_OUTCOME: - fixturedef.cached_result = (None, my_cache_key, sys.exc_info()) - raise - from cfme.utils.providers import get_crud - provider = get_crud(provider_data.key) - fixturedef.cached_result = (provider, my_cache_key, None) - request.param = provider - yield provider - break - else: - yield + parent = _walk_to_obj_parent(request) + # node has all the markers from full scope + # default it to empty dict so loop below shorts and yields at the end + item_marks = ProviderEnvironmentMarker.get_closest_kwarg_markers(parent.node) or {} + + for fixture_name, mark in item_marks.items(): + if fixture_name == fixturedef.argname: + kwargs = {} + for argname in fixturedef.argnames: + fixdef = request._get_active_fixturedef(argname) + result, arg_cache_key, exc = fixdef.cached_result + request._check_scope(argname, request.scope, fixdef.scope) + kwargs[argname] = result + + fixturefunc = fixturedef.func + if request.instance is not None: + fixturefunc = getimfunc(fixturedef.func) + if fixturefunc != fixturedef.func: + fixturefunc = fixturefunc.__get__(request.instance) + my_cache_key = request.param_index + try: + provider_data = call_fixture_func(fixturefunc, request, kwargs) + except TEST_OUTCOME: + fixturedef.cached_result = (None, my_cache_key, sys.exc_info()) + raise + from cfme.utils.providers import get_crud + provider = get_crud(provider_data.key) + fixturedef.cached_result = (provider, my_cache_key, None) + request.param = provider + yield provider + break else: yield diff --git a/cfme/fixtures/pytest_store.py b/cfme/fixtures/pytest_store.py index 6ee365e9cb..12f1b5980a 100644 --- a/cfme/fixtures/pytest_store.py +++ b/cfme/fixtures/pytest_store.py @@ -148,9 +148,9 @@ def write_line(self, line, **kwargs): store = Store() -def pytest_namespace(): - # Expose the pytest store as pytest.store - return {'store': store} +# def pytest_configure(config): +# # Expose the pytest store as pytest.store +# pytest.store = store def pytest_plugin_registered(manager): diff --git a/cfme/fixtures/rbac.py b/cfme/fixtures/rbac.py index e8466f3af1..1f500dce6d 100644 --- a/cfme/fixtures/rbac.py +++ b/cfme/fixtures/rbac.py @@ -153,7 +153,7 @@ def really_logout(): ensure_browser_open() -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_pyfunc_call(pyfuncitem): """Inspects and consumes certain exceptions @@ -169,7 +169,7 @@ def pytest_pyfunc_call(pyfuncitem): # Login as the "new" user to run the test under if 'rbac_role' in pyfuncitem.fixturenames: - user = pyfuncitem._request.getfuncargvalue('rbac_role') + user = pyfuncitem._request.getfixturevalue('rbac_role') really_logout() logger.info("setting user to {}".format(user)) user_obj = current_appliance.collections.users.instantiate( @@ -221,7 +221,7 @@ def pytest_pyfunc_call(pyfuncitem): raise Exception("RBAC: Test should have passed!") -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_generate_tests(metafunc): yield if 'rbac_role' in metafunc.fixturenames: diff --git a/cfme/fixtures/sauce.py b/cfme/fixtures/sauce.py index bfd00d10eb..b0bccfece6 100644 --- a/cfme/fixtures/sauce.py +++ b/cfme/fixtures/sauce.py @@ -1,7 +1,7 @@ import pytest -@pytest.mark.trylast +@pytest.hookimpl(trylast=True) def pytest_runtest_teardown(item, nextitem): if item.config.getoption('sauce'): from cfme.utils.browser import ensure_browser_open, quit, browser diff --git a/cfme/fixtures/service_fixtures.py b/cfme/fixtures/service_fixtures.py index 8d8798e35c..a98e8dc897 100644 --- a/cfme/fixtures/service_fixtures.py +++ b/cfme/fixtures/service_fixtures.py @@ -10,7 +10,7 @@ from cfme.cloud.provider.ec2 import EC2Provider from cfme.cloud.provider.gce import GCEProvider from cfme.cloud.provider.openstack import OpenStackProvider -from cfme.fixtures.provider import console_template +from cfme.fixtures.templates import _get_template from cfme.infrastructure.provider import InfraProvider from cfme.rest.gen_data import dialog as _dialog from cfme.rest.gen_data import service_catalog_obj as _catalog @@ -82,7 +82,7 @@ def create_catalog_item(appliance, provider, provisioning, dialog, catalog, provision_type, template, host, datastore, iso_file, vlan = map(provisioning.get, ('provision_type', 'template', 'host', 'datastore', 'iso_file', 'vlan')) if console_test: - template = console_template(provider).name + template = _get_template(provider, 'console_template').name logger.info("Console template name : {}".format(template)) item_name = dialog.label if provider.one_of(InfraProvider): diff --git a/cfme/fixtures/skip_not_implemented.py b/cfme/fixtures/skip_not_implemented.py index 6d2a40a8e0..6a8ae8d5f4 100644 --- a/cfme/fixtures/skip_not_implemented.py +++ b/cfme/fixtures/skip_not_implemented.py @@ -1,7 +1,7 @@ import pytest -@pytest.mark.hookwrapper(tryfirst=True) +@pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_setup(item): """Pytest hook ensuring that failures caused by NotImplementedError show up as skips instead""" outcome = yield @@ -11,7 +11,7 @@ def pytest_runtest_setup(item): pytest.skip(e) -@pytest.mark.hookwrapper(tryfirst=True) +@pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_call(item): """Pytest hook ensuring that failures caused by NotImplementedError show up as skips instead""" outcome = yield diff --git a/cfme/fixtures/smtp.py b/cfme/fixtures/smtp.py index 1633c669fe..a2502ffa6a 100644 --- a/cfme/fixtures/smtp.py +++ b/cfme/fixtures/smtp.py @@ -96,7 +96,7 @@ def _finalize(): return client -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(item): try: yield diff --git a/cfme/fixtures/soft_assert.py b/cfme/fixtures/soft_assert.py index 3814eacc44..43f441f2d1 100644 --- a/cfme/fixtures/soft_assert.py +++ b/cfme/fixtures/soft_assert.py @@ -49,14 +49,14 @@ def base64_from_text(text): return base64.b64encode(text) -@pytest.mark.hookwrapper(tryfirst=True) +@pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_protocol(item, nextitem): if 'soft_assert' in item.fixturenames: _thread_locals.caught_asserts = [] yield -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(item): """pytest hook to handle :py:func:`soft_assert` fixture usage""" yield diff --git a/cfme/fixtures/ssh_client.py b/cfme/fixtures/ssh_client.py index cabd6161f1..25b549da59 100644 --- a/cfme/fixtures/ssh_client.py +++ b/cfme/fixtures/ssh_client.py @@ -6,7 +6,7 @@ from cfme.utils.log import logger -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_sessionfinish(session, exitstatus): """Loop through the appliance stack and close ssh connections""" diff --git a/cfme/fixtures/tccheck.py b/cfme/fixtures/tccheck.py index af568106bf..e910e4e78b 100644 --- a/cfme/fixtures/tccheck.py +++ b/cfme/fixtures/tccheck.py @@ -35,7 +35,7 @@ def load_available_requirements(): def check_tier(item): strings = [] - tier = item.get_marker('tier') + tier = item.get_closest_marker('tier') if tier is None: strings.append('[TCV-E] MISSING TIER: {}'.format(item.nodeid)) else: @@ -51,7 +51,7 @@ def check_tier(item): def check_requirement(item, available_requirements): strings = [] - requirement = item.get_marker('requirement') + requirement = item.get_closest_marker('requirement') if requirement is None: strings.append('[TCV-E] MISSING REQUIREMENT: {}'.format(item.nodeid)) else: diff --git a/cfme/fixtures/templateloader.py b/cfme/fixtures/templateloader.py index df47b1a554..fcfedc07ef 100644 --- a/cfme/fixtures/templateloader.py +++ b/cfme/fixtures/templateloader.py @@ -11,7 +11,7 @@ TEMPLATES = {} -@pytest.mark.tryfirst +@pytest.hookimpl(tryfirst=True) def pytest_addoption(parser): # Create the cfme option group for use in other plugins parser.addoption( diff --git a/cfme/fixtures/templates.py b/cfme/fixtures/templates.py new file mode 100644 index 0000000000..f825317d30 --- /dev/null +++ b/cfme/fixtures/templates.py @@ -0,0 +1,221 @@ +from collections import Mapping + +import pytest + +from cfme.fixtures.templateloader import TEMPLATES +from cfme.utils.log import logger + + +# TODO restructure these to indirectly parametrize the name, and provide name constants here +# These are getting improperly imported and used as functions + +@pytest.fixture(scope="function") +def template(template_location, provider): + if template_location is not None: + o = provider.data + try: + for field in template_location: + o = o[field] + except (IndexError, KeyError): + logger.info("Cannot apply %r to %r in the template specification, ignoring.", field, o) + else: + if not isinstance(o, str): + raise ValueError("{!r} is not a string! (for template)".format(o)) + if not TEMPLATES: + # There is nothing in TEMPLATES, that means no trackerbot URL and no data pulled. + # This should normally not constitute an issue so continue. + return o + templates = TEMPLATES.get(provider.key) + if templates is not None: + if o in templates: + return o + logger.info("Wanted template %s on %s but it is not there!", o, provider.key) + pytest.skip('Template not available') + + +def _get_template(provider, template_type_name): + """Get the template name for the given template type + YAML is expected to have structure with a templates section in the provider: + provider: + templates: + small_template: + name: + creds: + big_template: + name: + creds: + Args: + provider (obj): Provider object to lookup template on + template_type_name (str): Template type to lookup (small_template, big_template, etc) + Returns: + (dict) template dictionary from the yaml, with name and creds key:value pairs + """ + try: + template_type = provider.data.templates.get(template_type_name) + except (AttributeError, KeyError): + logger.error("Wanted template %s on %s but it is not there!", template, provider.key) + pytest.skip('No {} for provider {}'.format(template_type_name, provider.key)) + if not isinstance(template_type, Mapping): + pytest.skip('Template mapping is incorrect, {} on provider {}' + .format(template_type_name, provider.key)) + return template_type + + +@pytest.fixture(scope="function") +def small_template(provider): + return _get_template(provider, 'small_template') + + +@pytest.fixture(scope="module") +def small_template_modscope(provider): + return _get_template(provider, 'small_template') + + +@pytest.fixture(scope="function") +def full_template(provider): + return _get_template(provider, 'full_template') + + +@pytest.fixture(scope="module") +def full_template_modscope(provider): + return _get_template(provider, 'full_template') + + +@pytest.fixture(scope="function") +def big_template(provider): + return _get_template(provider, 'big_template') + + +@pytest.fixture(scope="module") +def big_template_modscope(provider): + return _get_template(provider, 'big_template') + + +@pytest.fixture(scope="function") +def provisioning(provider): + try: + return provider.data['provisioning'] + except KeyError: + logger.warning('Tests using the provisioning fixture ' + 'should include required_fields in their ProviderFilter marker') + pytest.skip('Missing "provisioning" field in provider data') + + +@pytest.fixture(scope="function") +def console_template(provider): + return _get_template(provider, 'console_template') + + +@pytest.fixture(scope="module") +def console_template_modscope(provider): + return _get_template(provider, 'console_template') + + +@pytest.fixture(scope="function") +def ubuntu16_template(provider): + return _get_template(provider, 'ubuntu16_template') + + +@pytest.fixture(scope="module") +def ubuntu16_template_modscope(provider): + return _get_template(provider, 'ubuntu16_template') + + +@pytest.fixture(scope="function") +def rhel69_template(provider): + return _get_template(provider, 'rhel69_template') + + +@pytest.fixture(scope="module") +def rhel69_template_modscope(provider): + return _get_template(provider, 'rhel69_template') + + +@pytest.fixture(scope="function") +def rhel74_template(provider): + return _get_template(provider, 'rhel74_template') + + +@pytest.fixture(scope="module") +def rhel74_template_modscope(provider): + return _get_template(provider, 'rhel74_template') + + +@pytest.fixture(scope="function") +def win7_template(provider): + return _get_template(provider, 'win7_template') + + +@pytest.fixture(scope="module") +def win7_template_modscope(provider): + return _get_template(provider, 'win7_template') + + +@pytest.fixture(scope="function") +def win10_template(provider): + return _get_template(provider, 'win10_template') + + +@pytest.fixture(scope="module") +def win10_template_modscope(provider): + return _get_template(provider, 'win10_template') + + +@pytest.fixture(scope="function") +def win2012_template(provider): + return _get_template(provider, 'win2012_template') + + +@pytest.fixture(scope="module") +def win2012_template_modscope(provider): + return _get_template(provider, 'win2012_template') + + +@pytest.fixture(scope="function") +def win2016_template(provider): + return _get_template(provider, 'win2016_template') + + +@pytest.fixture(scope="module") +def win2016_template_modscope(provider): + return _get_template(provider, 'win2016_template') + + +@pytest.fixture(scope="function") +def dual_network_template(provider): + return _get_template(provider, 'dual_network_template') + + +@pytest.fixture(scope="module") +def dual_network_template_modscope(provider): + return _get_template(provider, 'dual_network_template') + + +@pytest.fixture(scope="function") +def dual_disk_template(provider): + return _get_template(provider, 'dual_disk_template') + + +@pytest.fixture(scope="module") +def dual_disk_template_modscope(provider): + return _get_template(provider, 'dual_disk_template') + + +@pytest.fixture(scope="function") +def dportgroup_template(provider): + return _get_template(provider, 'dportgroup_template') + + +@pytest.fixture(scope="module") +def dportgroup_template_modscope(provider): + return _get_template(provider, 'dportgroup_template') + + +@pytest.fixture(scope="function") +def rhel7_minimal(provider): + return _get_template(provider, 'rhel7_minimal') + + +@pytest.fixture(scope="module") +def rhel7_minimal_modscope(provider): + return _get_template(provider, 'rhel7_minimal') diff --git a/cfme/fixtures/ui_coverage.py b/cfme/fixtures/ui_coverage.py index a1907cec86..8ea33efdfb 100644 --- a/cfme/fixtures/ui_coverage.py +++ b/cfme/fixtures/ui_coverage.py @@ -264,7 +264,7 @@ def pytest_sessionstart(self, session): conf.runtime['.ui-coverage']['collection_appliance'] = collection_appliance_address conf.save('.ui-coverage') - @pytest.mark.hookwrapper + @pytest.hookimpl(hookwrapper=True) def pytest_collection_finish(self): yield # Install coverage after collection finishes diff --git a/cfme/fixtures/v2v_fixtures.py b/cfme/fixtures/v2v_fixtures.py index 95b299a3d6..86f31806f6 100644 --- a/cfme/fixtures/v2v_fixtures.py +++ b/cfme/fixtures/v2v_fixtures.py @@ -8,7 +8,6 @@ from widgetastic.utils import partial_match from cfme.cloud.provider.openstack import OpenStackProvider -from cfme.fixtures.provider import rhel7_minimal from cfme.fixtures.provider import setup_or_skip from cfme.infrastructure.host import Host from cfme.infrastructure.provider.rhevm import RHEVMProvider @@ -418,10 +417,12 @@ def get_vm(request, appliance, source_provider, template, datastore='nfs'): collection = source_provider.appliance.provider_based_collection(source_provider) vm_name = random_vm_name("v2v-auto") vm_obj = collection.instantiate( - vm_name, source_provider, template_name=template(source_provider)["name"] + vm_name, source_provider, template_name=template["name"] ) power_on_vm = True - if template.__name__ == "win10_template": + if 'win10' in template.name: + # TODO Get the VM to the correct power state within the fixture/test, not here + # the fixture or test # Need to leave this off, otherwise migration fails # because when migration process tries to power off the VM if it is powered off # and for win10, it hibernates and that state of filesystem is unsupported @@ -483,7 +484,7 @@ def infra_mapping_default_data(source_provider, provider): @pytest.fixture(scope="function") -def mapping_data_vm_obj_mini(request, appliance, source_provider, provider): +def mapping_data_vm_obj_mini(request, appliance, source_provider, provider, rhel7_minimal): """Fixture to return minimal mapping data and vm object for migration plan""" infra_mapping_data = infra_mapping_default_data(source_provider, provider) vm_obj = get_vm(request, appliance, source_provider, template=rhel7_minimal) diff --git a/cfme/fixtures/video.py b/cfme/fixtures/video.py index 74517991ef..4f29ffcc52 100644 --- a/cfme/fixtures/video.py +++ b/cfme/fixtures/video.py @@ -36,7 +36,7 @@ def get_path_and_file_name(node): return node.parent.name, vid_name -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_runtest_setup(item): global recorder if vid_options and vid_options['enabled']: @@ -62,13 +62,13 @@ def stop_recording(): recorder = None -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_runtest_teardown(item, nextitem): yield stop_recording() -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_unconfigure(config): yield stop_recording() diff --git a/cfme/fixtures/xunit_tools.py b/cfme/fixtures/xunit_tools.py index 7dc3cf02b1..c5e129336a 100644 --- a/cfme/fixtures/xunit_tools.py +++ b/cfme/fixtures/xunit_tools.py @@ -128,13 +128,13 @@ def get_testcase_data(name, tests, processed_test, item, legacy=False): except Exception: description = "" try: - requirement = item.get_marker('requirement').args[0] + requirement = item.get_closest_marker('requirement').args[0] requirement_id = cfme_data['requirements'][requirement] work_items.append({'id': requirement_id, 'role': 'verifies'}) except Exception: pass try: - tier = item.get_marker('tier').args[0] + tier = item.get_closest_marker('tier').args[0] tier_id = caselevels[str(tier)] custom_fields['caselevel'] = tier_id except Exception: @@ -142,7 +142,7 @@ def get_testcase_data(name, tests, processed_test, item, legacy=False): param_list = list(extract_fixtures_values(item).keys()) if not legacy else None - manual = item.get_marker('manual') + manual = item.get_closest_marker('manual') if not manual: # The master here should probably link the latest "commit" eventually automation_script = 'http://github.com/{0}/{1}/blob/master/{2}#L{3}'.format( @@ -318,7 +318,7 @@ def gen_duplicates_log(items): f.write('{}\n'.format(test)) -@pytest.mark.trylast +@pytest.hookimpl(trylast=True) def pytest_collection_modifyitems(config, items): """Generates the XML files using collected items.""" if not (config.getoption('generate_xmls') or config.getoption('generate_legacy_xmls')): diff --git a/cfme/generic_objects/definition/definition_views.py b/cfme/generic_objects/definition/definition_views.py index 390259f9e1..ef3d2b79d2 100644 --- a/cfme/generic_objects/definition/definition_views.py +++ b/cfme/generic_objects/definition/definition_views.py @@ -10,7 +10,7 @@ from widgetastic_patternfly import Dropdown from widgetastic_patternfly import Input -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from widgetastic_manageiq import AutomateRadioGroup from widgetastic_manageiq import BaseEntitiesView from widgetastic_manageiq import BootstrapSwitch diff --git a/cfme/generic_objects/instance/ui.py b/cfme/generic_objects/instance/ui.py index 460f364bd2..779e064138 100644 --- a/cfme/generic_objects/instance/ui.py +++ b/cfme/generic_objects/instance/ui.py @@ -8,7 +8,7 @@ from widgetastic_patternfly import CandidateNotFound from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import Taggable from cfme.common import TagPageView from cfme.generic_objects.instance import GenericObjectInstance diff --git a/cfme/infrastructure/cluster.py b/cfme/infrastructure/cluster.py index 0f2e3c8411..88f68e3c61 100644 --- a/cfme/infrastructure/cluster.py +++ b/cfme/infrastructure/cluster.py @@ -10,7 +10,7 @@ from widgetastic_patternfly import Button from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import CustomButtonEventsMixin from cfme.common import Taggable from cfme.common import TimelinesView diff --git a/cfme/infrastructure/config_management.py b/cfme/infrastructure/config_management.py index 2fd1a951ab..a747cb2266 100644 --- a/cfme/infrastructure/config_management.py +++ b/cfme/infrastructure/config_management.py @@ -12,13 +12,13 @@ from widgetastic_patternfly import Dropdown from cfme.base.credential import Credential as BaseCredential -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import Taggable from cfme.common import TagPageView from cfme.exceptions import displayed_not_implemented from cfme.utils import conf from cfme.utils import ParamClassName -from cfme.utils.appliance import Navigatable +from cfme.utils.appliance import NavigatableMixin from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigate_to from cfme.utils.appliance.implementations.ui import navigator @@ -251,7 +251,7 @@ class ConfigManagementEditView(ConfigManagementView): is_displayed = displayed_not_implemented -class ConfigManager(Updateable, Pretty, Navigatable): +class ConfigManager(Updateable, Pretty, NavigatableMixin): """ This is base class for Configuration manager objects (Red Hat Satellite, Foreman, Ansible Tower) @@ -271,8 +271,8 @@ class ConfigManager(Updateable, Pretty, Navigatable): type = None refresh_flash_msg = 'Refresh Provider initiated for 1 provider' - def __init__(self, name=None, url=None, ssl=None, credentials=None, key=None, appliance=None): - Navigatable.__init__(self, appliance=appliance) + def __init__(self, appliance, name=None, url=None, ssl=None, credentials=None, key=None): + self.appliance = appliance self.name = name self.url = url self.ssl = ssl @@ -510,7 +510,7 @@ def config_profiles(self): name = row.description.text if 'unassigned' in name.lower(): continue - config_profiles.append(ConfigProfile(name=name, manager=self)) + config_profiles.append(ConfigProfile(appliance=self.appliance, name=name, manager=self)) return config_profiles @property @@ -524,11 +524,12 @@ def yaml_data(self): return conf.cfme_data.configuration_managers[self.key] @classmethod - def load_from_yaml(cls, key): + def load_from_yaml(cls, key, appliance): """Returns 'ConfigManager' object loaded from yamls, based on its key""" data = conf.cfme_data.configuration_managers[key] creds = conf.credentials[data['credentials']] return cls( + appliance=appliance, name=data['name'], url=data['url'], ssl=data['ssl'], @@ -544,27 +545,32 @@ def quad_name(self): return '{} Configuration Manager'.format(self.name) -def get_config_manager_from_config(cfg_mgr_key): +def get_config_manager_from_config(cfg_mgr_key, appliance=None, mgr_type=None): cfg_mgr = conf.cfme_data.get('configuration_managers', {})[cfg_mgr_key] + if mgr_type and cfg_mgr['type'] != mgr_type: + logger.info(f'Config managers loading type mismatch: {cfg_mgr} ' + f'key does not match desired type: [{mgr_type}]') + return None if cfg_mgr['type'] == 'satellite': - return Satellite.load_from_yaml(cfg_mgr_key) + return Satellite.load_from_yaml(cfg_mgr_key, appliance) elif cfg_mgr['type'] == 'ansible': - return AnsibleTower.load_from_yaml(cfg_mgr_key) + return AnsibleTower.load_from_yaml(cfg_mgr_key, appliance) else: raise Exception("Unknown configuration manager key") -class ConfigProfile(Pretty, Navigatable): +class ConfigProfile(Pretty, NavigatableMixin): """Configuration profile object (foreman-side hostgroup) Args: + appliance: appliance object name: Name of the profile manager: ConfigManager object which this profile is bound to """ pretty_attrs = ['name', 'manager'] - def __init__(self, name, manager, appliance=None): - Navigatable.__init__(self, appliance=appliance) + def __init__(self, appliance, name, manager): + self.appliance = appliance self.name = name self.manager = manager @@ -579,17 +585,17 @@ def systems(self): view.entities.configured_systems.click() if view.entities.configured_systems.elements.is_displayed: - return [ConfigSystem(row.hostname.text, self) + return [ConfigSystem(appliance=self.appliance, name=row.hostname.text, profile=self) for row in view.entities.configured_systems.elements] return list() -class ConfigSystem(Pretty, Navigatable, Taggable): +class ConfigSystem(Pretty, NavigatableMixin, Taggable): """The tags pages of the config system""" pretty_attrs = ['name', 'manager_key'] - def __init__(self, name, profile, appliance=None): - Navigatable.__init__(self, appliance=appliance) + def __init__(self, appliance, name, profile): + self.appliance = appliance self.name = name self.profile = profile @@ -642,13 +648,13 @@ class Satellite(ConfigManager): LATEST: 'Foreman' }) - def __init__(self, name=None, url=None, ssl=None, credentials=None, key=None): - super(Satellite, self).__init__(name=name, url=url, ssl=ssl, credentials=credentials, + def __init__(self, appliance, name=None, url=None, ssl=None, credentials=None, key=None): + super(Satellite, self).__init__(appliance=appliance, + name=name, + url=url, + ssl=ssl, + credentials=credentials, key=key) - self.name = name - self.url = url - self.ssl = ssl - self.credentials = credentials self.key = key or name @@ -686,13 +692,13 @@ class AnsibleTower(ConfigManager): type = 'Ansible Tower' - def __init__(self, name=None, url=None, ssl=None, credentials=None, key=None): - super(AnsibleTower, self).__init__(name=name, url=url, ssl=ssl, credentials=credentials, + def __init__(self, appliance, name=None, url=None, ssl=None, credentials=None, key=None): + super(AnsibleTower, self).__init__(appliance=appliance, + name=name, + url=url, + ssl=ssl, + credentials=credentials, key=key) - self.name = name - self.url = url - self.ssl = ssl - self.credentials = credentials self.key = key or name @property diff --git a/cfme/infrastructure/datastore.py b/cfme/infrastructure/datastore.py index d873b5fa7d..e418442eeb 100644 --- a/cfme/infrastructure/datastore.py +++ b/cfme/infrastructure/datastore.py @@ -9,7 +9,7 @@ from widgetastic_patternfly import Accordion from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import CustomButtonEventsMixin from cfme.common import Taggable from cfme.common.candu_views import DatastoreInfraUtilizationView diff --git a/cfme/infrastructure/pxe.py b/cfme/infrastructure/pxe.py index 9582a5117e..0eea01f94e 100644 --- a/cfme/infrastructure/pxe.py +++ b/cfme/infrastructure/pxe.py @@ -15,7 +15,7 @@ from cfme.base import BaseCollection from cfme.base import BaseEntity -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.exceptions import displayed_not_implemented from cfme.utils import conf from cfme.utils import ParamClassName diff --git a/cfme/infrastructure/virtual_machines.py b/cfme/infrastructure/virtual_machines.py index 182c8853d2..4628643680 100644 --- a/cfme/infrastructure/virtual_machines.py +++ b/cfme/infrastructure/virtual_machines.py @@ -26,7 +26,7 @@ from widgetastic_patternfly import Dropdown from widgetastic_patternfly import Input as WInput -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import TimelinesView from cfme.common.vm import Template from cfme.common.vm import TemplateCollection diff --git a/cfme/intelligence/chargeback/__init__.py b/cfme/intelligence/chargeback/__init__.py index f27b052ea2..c354b1ee85 100644 --- a/cfme/intelligence/chargeback/__init__.py +++ b/cfme/intelligence/chargeback/__init__.py @@ -1,12 +1,8 @@ # -*- coding: utf-8 -*- -from navmazing import NavigateToSibling from widgetastic.widget import View from widgetastic_patternfly import Accordion -from cfme.base import Server -from cfme.base.login import BaseLoggedInPage -from cfme.utils.appliance.implementations.ui import CFMENavigateStep -from cfme.utils.appliance.implementations.ui import navigator +from cfme.common import BaseLoggedInPage from widgetastic_manageiq import ManageIQTree @@ -35,14 +31,3 @@ class rates(Accordion): # noqa @View.nested class assignments(Accordion): # noqa tree = ManageIQTree() - - -@navigator.register(Server) -class IntelChargeback(CFMENavigateStep): - VIEW = ChargebackView - prerequisite = NavigateToSibling("LoggedIn") - - def step(self, *args, **kwargs): - self.prerequisite_view.navigation.select( - self.obj.intel_name, "Chargeback" - ) diff --git a/cfme/intelligence/chargeback/assignments.py b/cfme/intelligence/chargeback/assignments.py index 9326e8ff30..6f4706c494 100644 --- a/cfme/intelligence/chargeback/assignments.py +++ b/cfme/intelligence/chargeback/assignments.py @@ -120,7 +120,7 @@ class StorageAssign(Assign): @navigator.register(Assign, 'All') class AssignAll(CFMENavigateStep): - prerequisite = NavigateToAttribute('appliance.server', 'IntelChargeback') + prerequisite = NavigateToAttribute('appliance.server', 'Chargeback') VIEW = AssignmentsAllView def step(self, *args, **kwargs): diff --git a/cfme/intelligence/chargeback/rates.py b/cfme/intelligence/chargeback/rates.py index c14246d34c..779c003986 100644 --- a/cfme/intelligence/chargeback/rates.py +++ b/cfme/intelligence/chargeback/rates.py @@ -309,7 +309,7 @@ def create(self, description, currency=None, fields=None): @navigator.register(ComputeRateCollection, 'All') class ComputeRateAll(CFMENavigateStep): VIEW = RatesView - prerequisite = NavigateToAttribute('appliance.server', 'IntelChargeback') + prerequisite = NavigateToAttribute('appliance.server', 'Chargeback') def step(self, *args, **kwargs): self.view.rates.tree.click_path( @@ -364,7 +364,7 @@ def step(self, *args, **kwargs): @navigator.register(StorageRateCollection, 'All') class StorageRateAll(CFMENavigateStep): VIEW = RatesView - prerequisite = NavigateToAttribute('appliance.server', 'IntelChargeback') + prerequisite = NavigateToAttribute('appliance.server', 'Chargeback') def step(self, *args, **kwargs): self.view.rates.tree.click_path( diff --git a/cfme/intelligence/reports/__init__.py b/cfme/intelligence/reports/__init__.py index 2c3e8a498b..d749ae3675 100644 --- a/cfme/intelligence/reports/__init__.py +++ b/cfme/intelligence/reports/__init__.py @@ -7,7 +7,7 @@ from widgetastic_patternfly import Dropdown from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator from cfme.utils.blockers import BZ diff --git a/cfme/intelligence/rss.py b/cfme/intelligence/rss.py index 856cd28fce..d55a1d3510 100644 --- a/cfme/intelligence/rss.py +++ b/cfme/intelligence/rss.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from widgetastic_manageiq import Table diff --git a/cfme/intelligence/timelines.py b/cfme/intelligence/timelines.py index 9c17dae25a..2b97afec39 100644 --- a/cfme/intelligence/timelines.py +++ b/cfme/intelligence/timelines.py @@ -1,7 +1,7 @@ from widgetastic.widget import View from widgetastic_patternfly import Accordion -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from widgetastic_manageiq import ManageIQTree from widgetastic_manageiq import TimelinesChart diff --git a/cfme/markers/__init__.py b/cfme/markers/__init__.py index a0d099ed55..1f8dff5f85 100644 --- a/cfme/markers/__init__.py +++ b/cfme/markers/__init__.py @@ -1,17 +1 @@ -pytest_plugins = [ - "cfme.markers.composite", - "cfme.markers.crud", - "cfme.markers.fixtureconf", - "cfme.markers.meta", - "cfme.markers.polarion", - "cfme.markers.requires", - "cfme.markers.rhv", - "cfme.markers.sauce", - "cfme.markers.skipper", - "cfme.markers.smoke", - "cfme.markers.stream_excluder", - "cfme.markers.marker_filters", - "cfme.markers.uses", - "cfme.markers.uncollect", - "cfme.markers.isolation", -] +# look at conftest for plugin loading via entrypoints diff --git a/cfme/markers/destructive.py b/cfme/markers/destructive.py new file mode 100644 index 0000000000..db4114af87 --- /dev/null +++ b/cfme/markers/destructive.py @@ -0,0 +1,9 @@ +# register the non_destructive marker +# for use on tests that are non-destructive to the appliance, and can be run concurrently + + +def pytest_configure(config): + config.addinivalue_line( + 'markers', + 'non_destructive: mark tests that are non-destructive to the appliance' + ) diff --git a/cfme/markers/env.py b/cfme/markers/env.py index 99d1e84050..f381f365b3 100644 --- a/cfme/markers/env.py +++ b/cfme/markers/env.py @@ -16,6 +16,12 @@ class EnvironmentMarker(object): """Base Environment Marker""" PARAM_BY_DEFAULT = False + NAME = None + CHOICES = None + + @classmethod + def marker_description(cls): + return f'{cls.NAME}: mark a test for environment: {cls.CHOICES}' def process_env_mark(self, metafunc): if hasattr(metafunc.function, self.NAME): @@ -56,6 +62,13 @@ class PodifiedEnvironmentMarker(EnvironmentMarker): CHOICES = ['pod', 'vm'] +def pytest_configure(config): + config.addinivalue_line('markers', 'provider: Mark a test function/class/module for a provider') + config.addinivalue_line('markers', 'uses_testgen: Marker placed on tests that use testgen') + for envmark in [BrowserEnvironmentMarker, TCPEnvironmentMarker, PodifiedEnvironmentMarker]: + config.addinivalue_line('markers', envmark.marker_description()) + + def pytest_generate_tests(metafunc): from cfme.markers.env_markers.provider import ProviderEnvironmentMarker markers = [ diff --git a/cfme/markers/env_markers/provider.py b/cfme/markers/env_markers/provider.py index 2e8dd18640..ef7f76c2bc 100644 --- a/cfme/markers/env_markers/provider.py +++ b/cfme/markers/env_markers/provider.py @@ -22,6 +22,8 @@ ONE_PER_CATEGORY = 'one_per_category' ONE_PER_TYPE = 'one_per_type' +PROVIDER_MARKER_FIXTURE_NAME = 'provider' + class DPFilter(ProviderFilter): def __call__(self, provider): @@ -248,7 +250,7 @@ def providers(metafunc, filters=None, selector=ONE_PER_VERSION, fixture_name='pr flags_filter = ProviderFilter(required_flags=test_flags) filters = filters + [flags_filter] - # available_providers are the ones "available" from the yamls after all of the global and + # available_providers are the ones "available" from the yamls after all of the aal and # local filters have been applied. It will be a list of crud objects. available_providers = list_providers(filters) @@ -392,8 +394,13 @@ def add_prov(prov): def providers_by_class( - metafunc, classes, required_fields=None, selector=ONE_PER_VERSION, fixture_name='provider', - required_flags=None): + metafunc, + classes, + required_fields=None, + selector=ONE_PER_VERSION, + fixture_name=PROVIDER_MARKER_FIXTURE_NAME, + required_flags=None +): """ Gets providers by their class Args: @@ -417,48 +424,78 @@ def pytest_generate_tests(metafunc): class ProviderEnvironmentMarker(EnvironmentMarker): - NAME = 'provider' + NAME = PROVIDER_MARKER_FIXTURE_NAME + + @classmethod + def get_closest_kwarg_markers(cls, test_item, kwarg_name='fixture_name'): + """use iter_markers, and apply a marker kwarg filter, returning the first marker matching + Like pytest.nodes implementation, its relying on the first item returned by iter_marker + to be the 'lowest' level mark + + Args: + test_item: a test definition, or node, that has iter_markers + kwarg_name: the kwarg to organize markers by for closeness + + Returns: + dictionary of marks keyed by fixture name + """ + provider_marks = list(test_item.iter_markers(name=cls.NAME)) + if not provider_marks: + # no matching marks, nothing to parametrize + return + marks_by_kwarg = defaultdict(list) + for mark in provider_marks: + marks_by_kwarg[mark.kwargs.get(kwarg_name, cls.NAME)].append(mark) + + # pop the first item in the list for the closest marker + closest_marks = {fix: marks.pop(0) if marks else None + for fix, marks in marks_by_kwarg.items()} + return closest_marks def process_env_mark(self, metafunc): - if hasattr(metafunc.function, self.NAME): - mark_dict = defaultdict(list) - for mark in getattr(metafunc.function, self.NAME): - # Find all provider-ish markers - fixture_name = mark.kwargs.get('fixture_name', 'provider') - mark_dict[fixture_name].append(mark) - - for name, marks in mark_dict.items(): - mark = None - for mark in marks: - if mark.kwargs.get('override', False): - break - else: - if len(mark_dict[name]) >= 2: - raise Exception( - "You have an override provider without " - "specifying the override flag [{}]".format(metafunc.function.__name__) - ) - - args = mark.args - kwargs = mark.kwargs.copy() - if 'override' in kwargs: - kwargs.pop('override') - scope = kwargs.pop('scope', 'function') - indirect = kwargs.pop('indirect', False) - filter_unused = kwargs.pop('filter_unused', True) - selector = kwargs.pop('selector', ONE_PER_VERSION) - gen_func = kwargs.pop('gen_func', providers_by_class) - - # If parametrize doesn't get you what you need, steal this and modify as needed - kwargs.update({'selector': selector}) - argnames, argvalues, idlist = gen_func(metafunc, *args, **kwargs) - # Filter out argnames that aren't requested on the metafunc test item, - # so not all tests need all fixtures to run, and tests not using gen_func's - # fixtures aren't parametrized. - if filter_unused: - argnames, argvalues = fixture_filter(metafunc, argnames, argvalues) - # See if we have to parametrize at all after filtering - parametrize( - metafunc, argnames, argvalues, indirect=indirect, - ids=idlist, scope=scope, selector=selector - ) + """ Process the provider env marks + Notes: + provider markers can be applied at multiple layers (module, class, function) + provider markers automatically override at lower layers (function overrides all) + provider markers can supply their own fixture_name, to support multiple providers + Args: + metafunc: pytest metafunc object + + Returns: + Parametrizes metafunc object directly, returns nothing + """ + + # organize by fixture_name kwarg to the marker + # iter_markers returns most local mark first, maybe don't need override + marks_by_fixture = self.get_closest_kwarg_markers(metafunc.definition) + if marks_by_fixture is None: + return + + # process each mark, defaulting fixture_name + for fixture_name, mark in marks_by_fixture.items(): + + # mark is either the lowest marker (automatic override), or has custom fixture_name + logger.debug(f'Parametrizing provider env mark {mark}') + args = mark.args + kwargs = mark.kwargs.copy() + if kwargs.pop('override', False): + logger.warning('provider marker included override kwarg, this is unnecessary') + scope = kwargs.pop('scope', 'function') + indirect = kwargs.pop('indirect', False) + filter_unused = kwargs.pop('filter_unused', True) + selector = kwargs.pop('selector', ONE_PER_VERSION) + gen_func = kwargs.pop('gen_func', providers_by_class) + + # If parametrize doesn't get you what you need, steal this and modify as needed + kwargs.update({'selector': selector}) + argnames, argvalues, idlist = gen_func(metafunc, *args, **kwargs) + # Filter out argnames that aren't requested on the metafunc test item, + # so not all tests need all fixtures to run, and tests not using gen_func's + # fixtures aren't parametrized. + if filter_unused: + argnames, argvalues = fixture_filter(metafunc, argnames, argvalues) + # See if we have to parametrize at all after filtering + parametrize( + metafunc, argnames, argvalues, indirect=indirect, + ids=idlist, scope=scope, selector=selector + ) diff --git a/cfme/markers/fixtureconf.py b/cfme/markers/fixtureconf.py index 7671db9bef..4c21f3bdfa 100644 --- a/cfme/markers/fixtureconf.py +++ b/cfme/markers/fixtureconf.py @@ -13,7 +13,7 @@ def pytest_configure(config): def pytest_runtest_setup(item): - fixtureconf_mark = item.keywords.get('fixtureconf') + fixtureconf_mark = item.get_closest_marker('fixtureconf') args = getattr(fixtureconf_mark, 'args', tuple()) kwargs = getattr(fixtureconf_mark, 'kwargs', dict()) fixtureconf = dict() diff --git a/cfme/markers/isolation.py b/cfme/markers/isolation.py index 2ee68fb618..8688abb1b4 100644 --- a/cfme/markers/isolation.py +++ b/cfme/markers/isolation.py @@ -10,17 +10,17 @@ def pytest_configure(config): config.addinivalue_line('markers', 'browser_isolation: Mark a test case for browser isolation') -@pytest.mark.hookwrapper(tryfirst=True) +@pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_setup(item): - if item.get_marker('browser_isolation'): + if item.get_closest_marker('browser_isolation'): logger.info('Browser isolation for marker in setup') browser_implementation_quits(item) yield -@pytest.mark.hookwrapper(trylast=True) +@pytest.hookimpl(hookwrapper=True, trylast=True) def pytest_runtest_teardown(item, nextitem): yield - if item.get_marker("browser_isolation"): + if item.get_closest_marker("browser_isolation"): logger.info('Browser isolation for marker in teardown') browser_implementation_quits(item) diff --git a/cfme/markers/manual.py b/cfme/markers/manual.py index b7093acdcb..d729ca7fed 100644 --- a/cfme/markers/manual.py +++ b/cfme/markers/manual.py @@ -16,7 +16,7 @@ def pytest_addoption(parser): help="Collect also manual tests (only for --collect-only)") -@pytest.mark.tryfirst +@pytest.hookimpl(tryfirst=True) def pytest_collection_modifyitems(config, items): if config.getvalue('include_manual'): return @@ -24,7 +24,7 @@ def pytest_collection_modifyitems(config, items): keep, discard = [], [] for item in items: - if bool(item.get_marker("manual")) == is_manual: + if bool(item.get_closest_marker("manual")) == is_manual: keep.append(item) else: discard.append(item) diff --git a/cfme/markers/marker_filters.py b/cfme/markers/marker_filters.py index 642fe7e7e0..506aee1ee5 100644 --- a/cfme/markers/marker_filters.py +++ b/cfme/markers/marker_filters.py @@ -17,6 +17,7 @@ def pytest_configure(config): 'tier: mark a test case with a tier', 'requirement: mark a test case with a requirement', 'customer_scenario: mark a test case as a customer story', + 'rfe: mark a test case as an RFE' ] for marker in markers_to_add: config.addinivalue_line('markers', marker) @@ -53,15 +54,14 @@ def pytest_collection_modifyitems(session, config, items): # for each filter, check if its active and that the item has the marker # Then check if the marker content matches the passed filter # Discard items without the matching value - if (tiers and - not getattr(item.get_marker('tier'), 'args', [False])[0] in tiers): + if not getattr(item.get_closest_marker('tier'), 'args', [False])[0] in (tiers or []): discard_tier.append(item) continue - if (requirements and - not getattr(item.get_marker('requirement'), 'args', [False])[0] in requirements): + if (not getattr(item.get_closest_marker('requirement'), 'args', [False])[0] + in (requirements or [])): discard_requirement.append(item) continue - if customer and item.get_marker('customer_scenario') is None: + if customer and item.get_closest_marker('customer_scenario') is None: discard_customer.append(item) continue keep.append(item) diff --git a/cfme/markers/meta.py b/cfme/markers/meta.py index d53943668b..de0556659f 100644 --- a/cfme/markers/meta.py +++ b/cfme/markers/meta.py @@ -68,7 +68,7 @@ def pytest_addoption(parser): help='Comma-separated list of metaplugins to disable') -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_pycollect_makeitem(collector, name, obj): # Put the meta mark on objects as soon as pytest begins to collect them if isinstance(obj, FunctionType) and not hasattr(obj, 'meta'): @@ -76,7 +76,7 @@ def pytest_pycollect_makeitem(collector, name, obj): yield -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_collection_modifyitems(session, config, items): from cfme.utils.log import logger for item in items: @@ -88,10 +88,9 @@ def pytest_collection_modifyitems(session, config, items): ) item._metadata = AttrDict() - meta = item.get_marker("meta") - if meta is None: - continue - metas = reversed([x.kwargs for x in meta]) # Extract the kwargs, reverse the order + # Extract the kwargs, reverse the order + metas = reversed([mark.kwargs + for _, mark in item.iter_markers_with_node('meta') or []]) for meta in metas: item._metadata.update(meta) yield @@ -173,7 +172,7 @@ def pytest_runtest_teardown(item): run_plugins(item, plugin.TEARDOWN) -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(item): run_plugins(item, plugin.BEFORE_RUN) try: diff --git a/cfme/markers/perf.py b/cfme/markers/perf.py new file mode 100644 index 0000000000..5ed6d31f87 --- /dev/null +++ b/cfme/markers/perf.py @@ -0,0 +1,10 @@ +# auto-mark items in the perf directory with perf marker +import pytest + + +@pytest.hookimpl(tryfirst=True) +def pytest_collection_modifyitems(items): + # mark all perf tests here so we don't have to maintain the mark in those modules + for item in items: + if item.nodeid.startswith('cfme/tests/perf'): + item.add_marker(pytest.mark.perf) diff --git a/cfme/markers/polarion.py b/cfme/markers/polarion.py index d32a43506e..7ec89ce229 100644 --- a/cfme/markers/polarion.py +++ b/cfme/markers/polarion.py @@ -12,11 +12,11 @@ def pytest_configure(config): def extract_polarion_ids(item): """Extracts Polarion TC IDs from the test item. Returns None if no marker present.""" - polarion = item.get_marker('polarion') + polarion = item.get_closest_marker('polarion') return list(map(str, getattr(polarion, 'args', []))) -@pytest.mark.tryfirst +@pytest.hookimpl(tryfirst=True) def pytest_collection_modifyitems(config, items): xml = getattr(config, '_xml', None) if xml is None: @@ -34,7 +34,7 @@ class ReportPolarionToJunitPlugin(object): xml = attr.ib() node_map = attr.ib() - @pytest.mark.tryfirst + @pytest.hookimpl(tryfirst=True) def pytest_runtest_logreport(self, report): """Adds the supplied test case id to the xunit file as a property""" if report.when != 'setup': diff --git a/cfme/markers/regression.py b/cfme/markers/regression.py new file mode 100644 index 0000000000..8ab915eb43 --- /dev/null +++ b/cfme/markers/regression.py @@ -0,0 +1,9 @@ +# register the regression marker +# for use on tests that dare written for regressions + + +def pytest_configure(config): + config.addinivalue_line( + 'markers', + 'regression: mark tests written for regressions' + ) diff --git a/cfme/markers/rhel_tests.py b/cfme/markers/rhel_tests.py new file mode 100644 index 0000000000..8db5eed3b1 --- /dev/null +++ b/cfme/markers/rhel_tests.py @@ -0,0 +1,9 @@ +# register the rhel_testing marker +# for use on tests that deal directly with the OS that MIQ/CFME is running in + + +def pytest_configure(config): + config.addinivalue_line( + 'markers', + 'rhel_testing: mark tests that deal with the base OS directly' + ) diff --git a/cfme/markers/rhv.py b/cfme/markers/rhv.py index a5141d3448..5b934c2ec0 100644 --- a/cfme/markers/rhv.py +++ b/cfme/markers/rhv.py @@ -20,7 +20,7 @@ def test_something(): pytest -p pytester markers/rhv.py """ -import pytest +from cfme.fixtures.pytest_store import store RHV_CFME_TIERS = (1, 2, 3) @@ -62,7 +62,7 @@ def pytest_runtest_logreport(report): """Add RHV tier (e.g. "rhv1" to XML report as a property.""" if report.when != 'setup': return - xml = getattr(pytest.config, '_xml', None) + xml = getattr(store.config, '_xml', None) if xml is None: return reporter = xml.node_reporter(report) diff --git a/cfme/markers/skipper.py b/cfme/markers/skipper.py index 7dd5491943..5447d807f9 100644 --- a/cfme/markers/skipper.py +++ b/cfme/markers/skipper.py @@ -4,15 +4,13 @@ :py:attr:`skip_marks`. """ -import pytest - - #: List of (mark, commandline flag) tuples. When the given mark is used on a test, it will #: be skipped unless the commandline flag is used. If the mark is already found in py.test's #: parsed mark expression, no changes will be made for that mark. skip_marks = [ ('long_running', '--long-running'), - ('perf', '--perf') + ('perf', '--perf'), + ('long_running', '--long-running-env') ] _mark_doc = ('{mark}: Skip tests with the {mark} mark by default, unless {cmdline} commandline ' @@ -28,6 +26,7 @@ def pytest_addoption(parser): def pytest_configure(config): + # warning: dragons below if config.getoption('--help'): return @@ -62,10 +61,3 @@ def pytest_configure(config): config.option.markexpr = '({}) and ({})'.format(skip_mark_expr, config.option.markexpr) else: config.option.markexpr = skip_mark_expr - - -def pytest_collection_modifyitems(items): - # mark all perf tests here so we don't have to maintain the mark in those modules - for item in items: - if item.nodeid.startswith('cfme/tests/perf'): - item.add_marker(pytest.mark.perf) diff --git a/cfme/markers/smoke.py b/cfme/markers/smoke.py index b532ccd725..41842ee54c 100644 --- a/cfme/markers/smoke.py +++ b/cfme/markers/smoke.py @@ -30,14 +30,14 @@ def pytest_addoption(parser): help="halt the test run if smoke tests fail") -@pytest.mark.trylast +@pytest.hookimpl(trylast=True) def pytest_configure(config): smoke_tests = SmokeTests(reporter(config)) config.pluginmanager.register(smoke_tests, 'smoke_tests') config.addinivalue_line('markers', __doc__.splitlines()[0]) -@pytest.mark.hookwrapper +@pytest.hookimpl(hookwrapper=True) def pytest_collection_modifyitems(session, config, items): # XXX: This also handles moving long_running tests to the front of the test module # There are a few different ways to handle this batter, but rather than building in logic @@ -107,5 +107,6 @@ def pytest_runtest_logreport(self, report): def pytest_runtest_teardown(self, item, nextitem): # This condition should only be met on the last smoke test, since they were # all moved to the top of the test run. - if item.get_marker('smoke') and not (nextitem and nextitem.get_marker('smoke')): + if item.get_closest_marker('smoke') and not (nextitem and + nextitem.get_closest_marker('smoke')): self.complete = True diff --git a/cfme/markers/stream_excluder.py b/cfme/markers/stream_excluder.py index 3fae070216..aef4da5152 100644 --- a/cfme/markers/stream_excluder.py +++ b/cfme/markers/stream_excluder.py @@ -35,7 +35,7 @@ def pytest_configure(config): def pytest_itemcollected(item): holder = item.config.pluginmanager.getplugin('appliance-holder') streams_id = get_streams_id(holder.held_appliance) - marker = item.get_marker("ignore_stream") + marker = item.get_closest_marker("ignore_stream") if marker is None: return if hasattr(item, "callspec"): diff --git a/cfme/markers/uncollect.py b/cfme/markers/uncollect.py index 82593d26dc..3f8bc2c904 100644 --- a/cfme/markers/uncollect.py +++ b/cfme/markers/uncollect.py @@ -60,6 +60,17 @@ def test_delete_all_snapshots(test_vm, provider_key, provider_type): MARKDECORATOR_TYPE = type(pytest.mark.skip) +def pytest_configure(config): + config.addinivalue_line( + 'markers', + 'uncollectif: Uncollect a test based on a lambda operating on the test fixture values' + ) + config.addinivalue_line( + 'markers', + 'uncollect: Uncollect a test with a direct call' + ) + + # work around https://github.com/pytest-dev/pytest/issues/2400 def get_uncollect_function(marker_or_markdecorator): if isinstance(marker_or_markdecorator, MARKDECORATOR_TYPE): @@ -77,10 +88,7 @@ def uncollectif(item): from cfme.utils.appliance import find_appliance from cfme.utils.pytest_shortcuts import extract_fixtures_values - markers = item.get_marker('uncollectif') - if not markers: - return False, None - for mark in markers: + for _, mark in item.iter_markers_with_node('uncollectif') or []: log_msg = 'Trying uncollecting {}: {}'.format( item.name, mark.kwargs.get('reason', 'No reason given')) @@ -106,7 +114,7 @@ def uncollectif(item): return True, None args = [values[arg] for arg in arg_names] except KeyError: - missing_argnames = list(set(arg_names) - set(item._request.funcargnames)) + missing_argnames = list(set(arg_names) - set(item._request.fixturenames)) func_name = item.name if missing_argnames: raise Exception("You asked for a fixture which wasn't in the function {} " @@ -120,8 +128,8 @@ def uncollectif(item): return retval, mark.kwargs.get('reason', "No reason given") else: return False, None - else: + # no uncollect markers return False, None @@ -135,7 +143,7 @@ def pytest_collection_modifyitems(session, config, items): with log_path.join('uncollected.log').open('w') as f: for item in items: # First filter out all items who have the uncollect mark - uncollect_marker = item.get_marker('uncollect') + uncollect_marker = item.get_closest_marker('uncollect') if uncollect_marker: uncollect_reason = uncollect_marker.kwargs.get('reason', "No reason given") f.write("{} - {}\n".format(item.name, uncollect_reason)) diff --git a/cfme/markers/uses.py b/cfme/markers/uses.py index dd532dfcbf..2ae453cf45 100644 --- a/cfme/markers/uses.py +++ b/cfme/markers/uses.py @@ -21,7 +21,7 @@ # List of fixture marks to create and use for test marking # these are exposed as globals and individually documented -USES_FIXTURENAMES = set() +FIXTURENAME_MARKERS = dict() ## # Create the fixtures that will trigger test marking @@ -30,8 +30,8 @@ def _markfixture(func): if func.__doc__ is None: - func.__doc__ = "Fixture which marks a test with the ``{}`` mark".format(func.__name__) - USES_FIXTURENAMES.add(func.__name__) + func.__doc__ = f"{func.__name__}: Fixture which applies '{func.__name__}' mark to test" + FIXTURENAME_MARKERS.update({func.__name__: func.__doc__}) return pytest.fixture(scope="session")(func) @@ -57,26 +57,32 @@ def is_appliance(): @_markfixture def uses_db(is_appliance): - """fixture that marks tests with a ``uses_db`` and a ``is_appliance`` mark""" + pass @_markfixture def uses_ssh(is_appliance): - """fixture that marks tests with a ``uses_ssh`` and a ``is_appliance`` mark""" + pass @_markfixture def uses_cloud_providers(uses_providers): - """Fixture which marks a test with the ``uses_cloud_providers`` and ``uses_providers`` marks""" pass @_markfixture def uses_infra_providers(uses_providers): - """Fixture which marks a test with the ``uses_infra_providers`` and ``uses_providers`` marks""" pass +def pytest_configure(config): + for marker_doc in FIXTURENAME_MARKERS.values(): + config.addinivalue_line( + 'markers', + marker_doc.splitlines()[0] + ) + + ### # Now hook the item collector to apply all the correct marks ### @@ -88,7 +94,8 @@ def pytest_itemcollected(item): """ try: # Intersect 'uses_' fixture set with the fixtures being used by a test - mark_fixtures = USES_FIXTURENAMES.intersection(item.fixturenames) + uses_fixturenames = set(list(FIXTURENAME_MARKERS.keys())) + mark_fixtures = uses_fixturenames.intersection(item.fixturenames) except AttributeError: # Test doesn't have fixturenames, make no changes return diff --git a/cfme/networks/views.py b/cfme/networks/views.py index a48a0deae7..3c97456600 100644 --- a/cfme/networks/views.py +++ b/cfme/networks/views.py @@ -9,7 +9,7 @@ from widgetastic_patternfly import Dropdown from widgetastic_patternfly import TextInput -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common.provider_views import ProviderAddView from cfme.common.provider_views import ProviderEditView from cfme.exceptions import displayed_not_implemented diff --git a/cfme/optimize/__init__.py b/cfme/optimize/__init__.py index c90b7d32de..c7920930ae 100644 --- a/cfme/optimize/__init__.py +++ b/cfme/optimize/__init__.py @@ -2,7 +2,7 @@ from widgetastic.widget import View from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator from widgetastic_manageiq import Accordion diff --git a/cfme/physical/physical_chassis.py b/cfme/physical/physical_chassis.py index bc9ce4c8c5..f92d5fa1e1 100644 --- a/cfme/physical/physical_chassis.py +++ b/cfme/physical/physical_chassis.py @@ -9,7 +9,7 @@ from widgetastic_patternfly import Dropdown from wrapanapi.entities import PhysicalContainer -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import PolicyProfileAssignable from cfme.common import Taggable from cfme.exceptions import HostStatsNotContains diff --git a/cfme/physical/physical_rack.py b/cfme/physical/physical_rack.py index 7d54f609d3..1629b21063 100644 --- a/cfme/physical/physical_rack.py +++ b/cfme/physical/physical_rack.py @@ -8,7 +8,7 @@ from widgetastic_patternfly import Accordion from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import Taggable from cfme.exceptions import ProviderHasNoProperty from cfme.exceptions import RackStatsDoesNotContain diff --git a/cfme/physical/physical_storage.py b/cfme/physical/physical_storage.py index 5974ff039b..0280ddf170 100644 --- a/cfme/physical/physical_storage.py +++ b/cfme/physical/physical_storage.py @@ -10,7 +10,7 @@ from widgetastic_patternfly import BreadCrumb from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import PolicyProfileAssignable from cfme.common import Taggable from cfme.exceptions import ItemNotFound diff --git a/cfme/physical/provider/__init__.py b/cfme/physical/provider/__init__.py index 75e795568b..5c3b0931d9 100644 --- a/cfme/physical/provider/__init__.py +++ b/cfme/physical/provider/__init__.py @@ -4,8 +4,8 @@ from widgetastic.exceptions import NoSuchElementException from widgetastic.utils import Fillable -from cfme.base.login import BaseLoggedInPage from cfme.base.ui import Server +from cfme.common import BaseLoggedInPage from cfme.common.provider import BaseProvider from cfme.common.provider import provider_types from cfme.common.provider_views import PhysicalProviderAddView diff --git a/cfme/services/catalogs/__init__.py b/cfme/services/catalogs/__init__.py index f59823ec42..4f95005d93 100644 --- a/cfme/services/catalogs/__init__.py +++ b/cfme/services/catalogs/__init__.py @@ -4,7 +4,7 @@ from widgetastic_patternfly import Dropdown from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator from widgetastic_manageiq import Accordion diff --git a/cfme/services/myservice/ui.py b/cfme/services/myservice/ui.py index b4b35ee642..4d55d5cf98 100644 --- a/cfme/services/myservice/ui.py +++ b/cfme/services/myservice/ui.py @@ -11,7 +11,7 @@ from widgetastic_patternfly import Dropdown from widgetastic_patternfly import Input -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import TagPageView from cfme.common.vm_views import VMDetailsEntities from cfme.exceptions import displayed_not_implemented diff --git a/cfme/services/requests.py b/cfme/services/requests.py index 432128e035..871ff1de1e 100644 --- a/cfme/services/requests.py +++ b/cfme/services/requests.py @@ -12,7 +12,7 @@ from widgetastic_patternfly import BreadCrumb from widgetastic_patternfly import Input -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common.vm_views import BasicProvisionFormView from cfme.common.vm_views import ProvisionView from cfme.exceptions import displayed_not_implemented diff --git a/cfme/services/service_catalogs/ui.py b/cfme/services/service_catalogs/ui.py index 7e25071239..1aa3a30e85 100644 --- a/cfme/services/service_catalogs/ui.py +++ b/cfme/services/service_catalogs/ui.py @@ -6,7 +6,7 @@ from widgetastic_patternfly import Dropdown from cfme.base import Server -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.services.requests import RequestsView from cfme.services.service_catalogs import BaseOrderForm from cfme.services.service_catalogs import ServiceCatalogs diff --git a/cfme/services/workloads.py b/cfme/services/workloads.py index 9be1ec9f5d..83936d7651 100644 --- a/cfme/services/workloads.py +++ b/cfme/services/workloads.py @@ -7,7 +7,7 @@ from widgetastic_patternfly import Button from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance import Navigatable from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator diff --git a/cfme/storage/volume_type.py b/cfme/storage/volume_type.py index 4dd3af7197..26ba48dd9d 100644 --- a/cfme/storage/volume_type.py +++ b/cfme/storage/volume_type.py @@ -7,7 +7,7 @@ from widgetastic_patternfly import Button from widgetastic_patternfly import Dropdown -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import Taggable from cfme.common import TaggableCollection from cfme.exceptions import ItemNotFound diff --git a/cfme/test_framework/appliance.py b/cfme/test_framework/appliance.py index 646e89a17c..52b2455694 100644 --- a/cfme/test_framework/appliance.py +++ b/cfme/test_framework/appliance.py @@ -4,6 +4,7 @@ import pytest from cfme.fixtures import terminalreporter +from cfme.fixtures.pytest_store import store from cfme.utils import conf from cfme.utils.appliance import DummyAppliance from cfme.utils.appliance import load_appliances_from_config @@ -94,6 +95,6 @@ def appliance(self): def pytest_sessionstart(self): if isinstance(self.held_appliance, DummyAppliance) or self.held_appliance.is_dev: return - if pytest.store.parallelizer_role != 'slave': + if store.parallelizer_role != 'slave': with log_path.join('appliance_version').open('w') as appliance_version: appliance_version.write(self.held_appliance.version.vstring) diff --git a/cfme/test_framework/browser_isolation.py b/cfme/test_framework/browser_isolation.py index 5868332062..18d2b4efd0 100644 --- a/cfme/test_framework/browser_isolation.py +++ b/cfme/test_framework/browser_isolation.py @@ -32,7 +32,7 @@ def browser_implementation_quits(item): logger.debug('Browser isolation specified, but no appliance browsers available to quit on') -@pytest.mark.hookwrapper(trylast=True) +@pytest.hookimpl(hookwrapper=True, trylast=True) def pytest_runtest_teardown(item, nextitem): yield if item.config.getoption("browser_isolation"): diff --git a/cfme/test_framework/pytest_plugin.py b/cfme/test_framework/pytest_plugin.py index 47b15af32f..b6875e7b0b 100644 --- a/cfme/test_framework/pytest_plugin.py +++ b/cfme/test_framework/pytest_plugin.py @@ -1,21 +1,20 @@ """ cfme main plugin -this loads all of the elemental cfme plugins and prepares configuration +This provides the option group and disables pytest logging plugin + +Also provides uncollection stats during testrun/collection """ import pytest -@pytest.mark.tryfirst +@pytest.hookimpl(tryfirst=True) def pytest_addoption(parser): # Create the cfme option group for use in other plugins parser.getgroup('cfme', 'cfme: options related to cfme/miq appliances') def pytest_configure(config): - # disable pytest warnings plugin in order to keep our own warning logging - # we might want to remove this one - config.pluginmanager.set_blocked('warnings') # also disable the pytest logging system since its triggering issues with our own config.pluginmanager.set_blocked('logging-plugin') @@ -31,88 +30,3 @@ def pytest_collection_finish(session): store.terminalreporter.write( " {} tests left after all uncollections\n".format(len(session.items)), bold=True) - - -pytest_plugins = ( - 'cfme.markers', - 'cfme.fixtures.pytest_store', - 'cfme.test_framework.sprout.plugin', - 'cfme.test_framework.appliance_police', - 'cfme.test_framework.appliance', - 'cfme.test_framework.appliance_log_collector', - 'cfme.test_framework.browser_isolation', - 'cfme.fixtures.portset', - - 'cfme.markers.manual', - 'cfme.markers.polarion', # before artifactor - 'cfme.markers.env', - 'cfme.fixtures.artifactor_plugin', - 'cfme.fixtures.parallelizer', - - 'cfme.fixtures.prov_filter', - - 'cfme.fixtures.appliance', - 'cfme.fixtures.ansible_tower', - 'cfme.fixtures.embedded_ansible', - 'cfme.fixtures.single_appliance_sprout', - 'cfme.fixtures.dev_branch', - 'cfme.fixtures.events', - 'cfme.fixtures.appliance_update', - 'cfme.fixtures.blockers', - 'cfme.fixtures.browser', - 'cfme.fixtures.bzs', - 'cfme.fixtures.cfme_data', - 'cfme.fixtures.datafile', - 'cfme.fixtures.depot', - 'cfme.fixtures.disable_forgery_protection', - 'cfme.fixtures.fixtureconf', - 'cfme.fixtures.log', - 'cfme.fixtures.maximized', - 'cfme.fixtures.merkyl', - 'cfme.fixtures.nelson', - 'cfme.fixtures.page_screenshots', - 'cfme.fixtures.perf', - 'cfme.fixtures.provider', - 'cfme.fixtures.physical_switch', - 'cfme.fixtures.qa_contact', - 'cfme.fixtures.randomness', - 'cfme.fixtures.rbac', - 'cfme.fixtures.sauce', - 'cfme.fixtures.screenshots', - 'cfme.fixtures.skip_not_implemented', - 'cfme.fixtures.soft_assert', - 'cfme.fixtures.ssh_client', - 'cfme.fixtures.templateloader', - 'cfme.fixtures.terminalreporter', - 'cfme.fixtures.ui_coverage', - 'cfme.fixtures.update_tests', - 'cfme.fixtures.version_info', - 'cfme.fixtures.video', - 'cfme.fixtures.virtual_machine', - 'cfme.fixtures.widgets', - 'cfme.fixtures.xunit_tools', - 'cfme.fixtures.ansible_fixtures', - 'cfme.fixtures.base', - 'cfme.fixtures.cli', - 'cfme.fixtures.authentication', - 'cfme.fixtures.rdb', - 'cfme.fixtures.service_fixtures', - 'cfme.fixtures.smtp', - 'cfme.fixtures.tag', - 'cfme.fixtures.vm', - 'cfme.fixtures.vm_console', - 'cfme.fixtures.vporizer', - 'cfme.fixtures.model_collections', - 'cfme.fixtures.has_persistent_volume', - 'cfme.fixtures.tccheck', - 'cfme.fixtures.pxe', - 'cfme.fixtures.candu', - 'cfme.fixtures.v2v_fixtures', - 'cfme.fixtures.networks', - 'cfme.fixtures.nuage', - 'cfme.fixtures.automate', - 'cfme.fixtures.multi_region', - 'cfme.fixtures.generic_object', - - 'cfme.metaplugins', -) diff --git a/cfme/tests/ansible/test_embedded_ansible_services.py b/cfme/tests/ansible/test_embedded_ansible_services.py index d0151420bd..89d63c49ac 100644 --- a/cfme/tests/ansible/test_embedded_ansible_services.py +++ b/cfme/tests/ansible/test_embedded_ansible_services.py @@ -778,7 +778,7 @@ def _revert(): @pytest.mark.tier(3) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) @pytest.mark.usefixtures("setup_provider") @pytest.mark.meta(automates=[BZ(1448918)]) def test_ansible_service_linked_vm( diff --git a/cfme/tests/automate/custom_button/test_buttons.py b/cfme/tests/automate/custom_button/test_buttons.py index a0bc041c04..db5f9fcd15 100644 --- a/cfme/tests/automate/custom_button/test_buttons.py +++ b/cfme/tests/automate/custom_button/test_buttons.py @@ -351,7 +351,7 @@ def test_open_url_availability(appliance): view.cancel_button.click() -@pytest.mark.provider([VMwareProvider], override=True, scope="function", selector=ONE_PER_TYPE) +@pytest.mark.provider([VMwareProvider], scope="function", selector=ONE_PER_TYPE) def test_custom_button_quotes(appliance, provider, setup_provider, dialog, request): """ Test custom button and group allows quotes or not @@ -407,7 +407,7 @@ def test_custom_button_quotes(appliance, provider, setup_provider, dialog, reque @pytest.mark.meta( blockers=[BZ(1535215, forced_streams=["5.10"], unblock=lambda button_tag: button_tag != "Evm")] ) -@pytest.mark.provider([VMwareProvider], override=True, scope="function", selector=ONE_PER_TYPE) +@pytest.mark.provider([VMwareProvider], scope="function", selector=ONE_PER_TYPE) @pytest.mark.parametrize("button_tag", ["Evm", "Build"]) def test_custom_button_simulation(request, appliance, provider, setup_provider, button_tag): """ Test whether custom button works with simulation option @@ -466,7 +466,7 @@ def test_custom_button_simulation(request, appliance, provider, setup_provider, @pytest.mark.parametrize("button_tag", ["Evm", "Build"]) -@pytest.mark.provider([VMwareProvider], override=True, scope="module", selector=ONE_PER_TYPE) +@pytest.mark.provider([VMwareProvider], scope="module", selector=ONE_PER_TYPE) def test_custom_button_order_sort(appliance, request, provider, setup_provider, button_tag): """ Test custom button order reflection on destination # ToDo: Now, we are testing this against single object per group tag. If need extends for all. @@ -614,7 +614,7 @@ def test_custom_button_language(): @pytest.mark.tier(2) @pytest.mark.meta(automates=[1651099]) -@pytest.mark.provider([VMwareProvider], override=True, selector=ONE_PER_TYPE) +@pytest.mark.provider([VMwareProvider], selector=ONE_PER_TYPE) @pytest.mark.parametrize("obj_type", ["PROVIDER"], ids=["Provider"], scope="module") def test_attribute_override(appliance, request, provider, setup_provider, obj_type, button_group): """ Test custom button attribute override @@ -677,7 +677,7 @@ def test_attribute_override(appliance, request, provider, setup_provider, obj_ty @pytest.mark.meta(blockers=[BZ(1719282, unblock=lambda button_type: button_type != "User")]) @pytest.mark.parametrize("button_type", ["User", "Provider"]) -@pytest.mark.provider([VMwareProvider], override=True, scope="module", selector=ONE_PER_TYPE) +@pytest.mark.provider([VMwareProvider], scope="module", selector=ONE_PER_TYPE) def test_simulated_object_copy_on_button(appliance, provider, setup_provider, button_type): """ Test copy of simulated object over custom button diff --git a/cfme/tests/automate/custom_button/test_service_objects.py b/cfme/tests/automate/custom_button/test_service_objects.py index 671dd70981..c9f3f4897c 100644 --- a/cfme/tests/automate/custom_button/test_service_objects.py +++ b/cfme/tests/automate/custom_button/test_service_objects.py @@ -786,7 +786,7 @@ def unassigned_btn_setup(request, appliance, provider, gen_rest_service): yield obj, gp, destinations -@pytest.mark.provider([VMwareProvider], override=True, scope="module", selector=ONE) +@pytest.mark.provider([VMwareProvider], scope="module", selector=ONE) def test_custom_button_unassigned_behavior_objs( appliance, setup_provider, unassigned_btn_setup, request ): diff --git a/cfme/tests/automate/generic_objects/buttons/test_generic_class_custom_button.py b/cfme/tests/automate/generic_objects/buttons/test_generic_class_custom_button.py index aa7659a8b3..f4d40021ed 100644 --- a/cfme/tests/automate/generic_objects/buttons/test_generic_class_custom_button.py +++ b/cfme/tests/automate/generic_objects/buttons/test_generic_class_custom_button.py @@ -2,7 +2,7 @@ import pytest from cfme import test_requirements -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.generic_objects.definition.definition_views import GenericObjectDefinitionAllView from cfme.generic_objects.definition.definition_views import GenericObjectDefinitionDetailsView from cfme.utils.appliance import ViaUI diff --git a/cfme/tests/automate/generic_objects/test_definitions.py b/cfme/tests/automate/generic_objects/test_definitions.py index e0eb9f5f7c..8d579735b3 100644 --- a/cfme/tests/automate/generic_objects/test_definitions.py +++ b/cfme/tests/automate/generic_objects/test_definitions.py @@ -4,7 +4,7 @@ import yaml from cfme import test_requirements -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.utils.appliance import ViaREST from cfme.utils.appliance import ViaUI from cfme.utils.appliance.implementations.ui import navigate_to diff --git a/cfme/tests/automate/test_vmware_methods.py b/cfme/tests/automate/test_vmware_methods.py index dea233c55d..9ab0561d6b 100644 --- a/cfme/tests/automate/test_vmware_methods.py +++ b/cfme/tests/automate/test_vmware_methods.py @@ -8,7 +8,7 @@ from widgetastic_patternfly import Dropdown from cfme import test_requirements -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.infrastructure.provider.virtualcenter import VMwareProvider from cfme.utils.generators import random_vm_name from cfme.utils.log import logger diff --git a/cfme/tests/cli/test_appliance_update.py b/cfme/tests/cli/test_appliance_update.py index 9fd12ded5e..deec2741ff 100644 --- a/cfme/tests/cli/test_appliance_update.py +++ b/cfme/tests/cli/test_appliance_update.py @@ -72,7 +72,7 @@ def old_version(request): @pytest.fixture(scope="function", ) -def appliance_preupdate(old_version, appliance): +def appliance_preupdate(old_version, appliance, request): series = appliance.version.series() update_url = "update_url_{}".format(series.replace('.', '')) @@ -81,17 +81,19 @@ def appliance_preupdate(old_version, appliance): repo file for update""" usable = [] - sp = SproutClient.from_config(sprout_user_key=pytest.config.option.sprout_user_key or None) - available_versions = set(sp.call_method('available_cfme_versions')) + sprout = SproutClient.from_config( + sprout_user_key=request.config.getoption('sprout_user_key', default=None) or None + ) + available_versions = set(sprout.call_method('available_cfme_versions')) for a in available_versions: if a.startswith(old_version): usable.append(Version(a)) usable.sort(reverse=True) try: - apps, pool_id = sp.provision_appliances(count=1, - preconfigured=True, - lease_time=180, - version=str(usable[0])) + apps, pool_id = sprout.provision_appliances(count=1, + preconfigured=True, + lease_time=180, + version=str(usable[0])) except AuthException: msg = ('Sprout credentials key or yaml maps missing or invalid,' 'unable to provision appliance version %s for preupdate'.format(str(usable[0]))) @@ -107,7 +109,7 @@ def appliance_preupdate(old_version, appliance): apps[0].ssh_client.run_command('cat /etc/yum.repos.d/update.repo').output) yield apps[0] apps[0].ssh_client.close() - sp.destroy_pool(pool_id) + sprout.destroy_pool(pool_id) def do_yum_update(appliance): diff --git a/cfme/tests/cloud/test_cloud_timelines.py b/cfme/tests/cloud/test_cloud_timelines.py index a39533db2e..b4cd57314f 100644 --- a/cfme/tests/cloud/test_cloud_timelines.py +++ b/cfme/tests/cloud/test_cloud_timelines.py @@ -368,7 +368,7 @@ def test_cloud_timeline_diagnostic(new_instance, mark_vm_as_appliance, soft_asse inst_event.catch_in_timelines(soft_assert, targets) -@pytest.mark.provider([EC2Provider], override=True, scope='function') +@pytest.mark.provider([EC2Provider], scope='function') def test_cloud_timeline_rename_event(new_instance, soft_assert, azone): """ Metadata: diff --git a/cfme/tests/cloud/test_instance_power_control.py b/cfme/tests/cloud/test_instance_power_control.py index 1134b7602a..077250c7d0 100644 --- a/cfme/tests/cloud/test_instance_power_control.py +++ b/cfme/tests/cloud/test_instance_power_control.py @@ -4,12 +4,12 @@ from wrapanapi import VmState from cfme import test_requirements -from cfme.base.login import BaseLoggedInPage from cfme.cloud.provider import CloudProvider from cfme.cloud.provider.azure import AzureProvider from cfme.cloud.provider.ec2 import EC2Provider from cfme.cloud.provider.gce import GCEProvider from cfme.cloud.provider.openstack import OpenStackProvider +from cfme.common import BaseLoggedInPage from cfme.utils.appliance.implementations.ui import navigate_to from cfme.utils.blockers import BZ from cfme.utils.generators import random_vm_name @@ -19,14 +19,12 @@ from cfme.utils.wait import TimedOutError from cfme.utils.wait import wait_for -FILTER_FIELDS = dict(required_fields=['test_power_control']) - - pytestmark = [ pytest.mark.tier(2), pytest.mark.long_running, test_requirements.power, - pytest.mark.provider([CloudProvider], scope='function', **FILTER_FIELDS), + pytest.mark.provider([CloudProvider], scope='function', + required_fields=['test_power_control']), pytest.mark.usefixtures('setup_provider'), ] @@ -359,8 +357,7 @@ def _get_view_with_icons_checked(): wait_for_instance_state(soft_assert, testing_instance2, state="started") -@pytest.mark.provider([OpenStackProvider], - scope='function', override=True, **FILTER_FIELDS) +@pytest.mark.uncollectif(lambda provider: not provider.one_of(OpenStackProvider)) def test_hard_reboot(appliance, provider, testing_instance, ensure_vm_running, soft_assert): """ Tests instance hard reboot @@ -383,8 +380,7 @@ def test_hard_reboot(appliance, provider, testing_instance, ensure_vm_running, s wait_for_instance_state(soft_assert, testing_instance, state="started") -@pytest.mark.provider([AzureProvider], - scope='function', override=True, **FILTER_FIELDS) +@pytest.mark.uncollectif(lambda provider: not provider.one_of(AzureProvider)) def test_hard_reboot_unsupported(appliance, testing_instance): """ Tests that hard reboot throws an 'unsupported' error message on an Azure instance @@ -407,8 +403,7 @@ def test_hard_reboot_unsupported(appliance, testing_instance): appliance.browser.create_view(BaseLoggedInPage).flash.assert_message(message) -@pytest.mark.provider([AzureProvider, OpenStackProvider], - scope='function', override=True, **FILTER_FIELDS) +@pytest.mark.uncollectif(lambda provider: not provider.one_of(AzureProvider, OpenStackProvider)) def test_suspend(appliance, provider, testing_instance, ensure_vm_running, soft_assert): """ Tests instance suspend @@ -430,8 +425,7 @@ def test_suspend(appliance, provider, testing_instance, ensure_vm_running, soft_ wait_for_instance_state(soft_assert, testing_instance, state="suspended") -@pytest.mark.provider([OpenStackProvider], - scope='function', override=True, **FILTER_FIELDS) +@pytest.mark.uncollectif(lambda provider: not provider.one_of(OpenStackProvider)) def test_unpause(appliance, provider, testing_instance, ensure_vm_paused, soft_assert): """ Tests instance unpause @@ -451,8 +445,7 @@ def test_unpause(appliance, provider, testing_instance, ensure_vm_paused, soft_a wait_for_instance_state(soft_assert, testing_instance, state="started") -@pytest.mark.provider([AzureProvider, OpenStackProvider], - scope='function', override=True, **FILTER_FIELDS) +@pytest.mark.uncollectif(lambda provider: not provider.one_of(AzureProvider, OpenStackProvider)) def test_resume(appliance, provider, testing_instance, ensure_vm_suspended, soft_assert): """ Tests instance resume @@ -623,8 +616,7 @@ def test_soft_reboot(self, provider, testing_instance, # check if the power state change is reflected on UI and provider wait_for_instance_state(soft_assert, testing_instance, state="started") - @pytest.mark.provider([OpenStackProvider], - scope='function', override=True, **FILTER_FIELDS) + @pytest.mark.uncollectif(lambda provider: not provider.one_of(OpenStackProvider)) @pytest.mark.parametrize("from_detail", [True, False], ids=["from_detail", "from_collection"]) def test_hard_reboot(self, provider, testing_instance, soft_assert, ensure_vm_running, appliance, from_detail): @@ -654,8 +646,7 @@ def test_hard_reboot(self, provider, testing_instance, # check if the power state change is reflected on UI and provider wait_for_instance_state(soft_assert, testing_instance, state="started") - @pytest.mark.provider([AzureProvider, OpenStackProvider], - scope='function', override=True, **FILTER_FIELDS) + @pytest.mark.uncollectif(lambda provider: not provider.one_of(AzureProvider, OpenStackProvider)) @pytest.mark.parametrize("from_detail", [True, False], ids=["from_detail", "from_collection"]) def test_suspend_resume(self, provider, testing_instance, soft_assert, ensure_vm_running, appliance, from_detail): @@ -701,8 +692,7 @@ def test_suspend_resume(self, provider, testing_instance, # check if the power state change is reflected on UI and provider wait_for_instance_state(soft_assert, testing_instance, state="started") - @pytest.mark.provider([OpenStackProvider], - scope='function', override=True, **FILTER_FIELDS) + @pytest.mark.uncollectif(lambda provider: not provider.one_of(OpenStackProvider)) @pytest.mark.parametrize("from_detail", [True, False], ids=["from_detail", "from_collection"]) def test_pause_unpause(self, provider, testing_instance, soft_assert, ensure_vm_running, appliance, from_detail): diff --git a/cfme/tests/cloud/test_keypairs.py b/cfme/tests/cloud/test_keypairs.py index 255ba71e2a..76aa78a938 100644 --- a/cfme/tests/cloud/test_keypairs.py +++ b/cfme/tests/cloud/test_keypairs.py @@ -155,7 +155,7 @@ def test_keypair_create_invalid_key_validation(provider, appliance): @test_requirements.tag -@pytest.mark.provider([OpenStackProvider], override=True, scope="module", selector=ONE_PER_TYPE) +@pytest.mark.provider([OpenStackProvider], scope="module", selector=ONE_PER_TYPE) def test_keypair_add_and_remove_tag(keypair): """ This will test whether it will add and remove tag for newly created Keypair or not and then deletes it. diff --git a/cfme/tests/cloud/test_providers.py b/cfme/tests/cloud/test_providers.py index 36d0ddf8a7..27526abd96 100644 --- a/cfme/tests/cloud/test_providers.py +++ b/cfme/tests/cloud/test_providers.py @@ -500,7 +500,7 @@ def test_api_port_max_character_validation_cloud(appliance): @pytest.mark.tier(2) @test_requirements.azure -@pytest.mark.provider([AzureProvider], scope="function", override=True) +@pytest.mark.provider([AzureProvider], scope="function") def test_azure_subscription_required(request, provider): """ Tests that provider can't be added w/o subscription @@ -661,7 +661,7 @@ def test_openstack_provider_has_dashboard(appliance, openstack_provider): @test_requirements.ec2 @pytest.mark.tier(3) -@pytest.mark.provider([EC2Provider], scope="function", override=True) +@pytest.mark.provider([EC2Provider], scope="function") def test_select_key_pair_none_while_provisioning(appliance, request, has_no_cloud_providers, provider): """ @@ -700,7 +700,7 @@ def test_select_key_pair_none_while_provisioning(appliance, request, has_no_clou @pytest.mark.tier(3) @test_requirements.azure -@pytest.mark.provider([AzureProvider], override=True) +@pytest.mark.provider([AzureProvider]) def test_azure_instance_password_requirements(appliance, request, has_no_cloud_providers, setup_provider): """ @@ -859,7 +859,7 @@ def test_security_groups_query(self, provider, appliance, setup_provider): @test_requirements.tag -@pytest.mark.provider([CloudProvider], override=True, selector=ONE) +@pytest.mark.provider([CloudProvider], selector=ONE) def test_tagvis_provision_fields(setup_provider, request, appliance, user_restricted, tag, soft_assert): """Test for network environment fields for restricted user @@ -890,7 +890,7 @@ def test_tagvis_provision_fields(setup_provider, request, appliance, user_restri @test_requirements.general_ui @pytest.mark.tier(3) -@pytest.mark.provider([OpenStackProvider], override=True) +@pytest.mark.provider([OpenStackProvider]) def test_domain_id_validation(request, provider): """ Test validating Keystone V3 needs domain_id @@ -922,7 +922,7 @@ def test_domain_id_validation(request, provider): @test_requirements.azure @pytest.mark.meta(automates=[1315945]) -@pytest.mark.provider([AzureProvider], override=True, selector=ONE) +@pytest.mark.provider([AzureProvider], selector=ONE) def test_vpc_env_selection(setup_provider, request, provider, appliance, provisioning): """ Test selection of components in environment page of cloud instances diff --git a/cfme/tests/cloud/test_quota_tagging.py b/cfme/tests/cloud/test_quota_tagging.py index 82d5d63a9a..232e59159f 100644 --- a/cfme/tests/cloud/test_quota_tagging.py +++ b/cfme/tests/cloud/test_quota_tagging.py @@ -168,7 +168,7 @@ def test_quota_tagging_cloud_via_lifecycle(request, appliance, provider, prov_da recursive_update(prov_data, { 'request': {'email': 'test_{}@example.com'.format(fauxfactory.gen_alphanumeric())}}) prov_data.update({'template_name': template_name}) - appliance.collections.cloud_instances.create(vm_name, provider, prov_data, override=True) + appliance.collections.cloud_instances.create(vm_name, provider, prov_data) # nav to requests page to check quota validation request_description = 'Provision from [{template}] to [{vm}]'.format(template=template_name, vm=vm_name) @@ -233,7 +233,7 @@ def test_cloud_quota_by_lifecycle(request, appliance, provider, set_entity_quota recursive_update(prov_data, { 'request': {'email': 'test_{}@example.com'.format(fauxfactory.gen_alphanumeric())}}) prov_data.update({'template_name': template_name}) - appliance.collections.cloud_instances.create(vm_name, provider, prov_data, override=True) + appliance.collections.cloud_instances.create(vm_name, provider, prov_data) # nav to requests page to check quota validation request_description = 'Provision from [{template}] to [{vm}]'.format(template=template_name, vm=vm_name) diff --git a/cfme/tests/cloud/test_tag_mapping.py b/cfme/tests/cloud/test_tag_mapping.py index 37abcf6699..a1a23b91de 100644 --- a/cfme/tests/cloud/test_tag_mapping.py +++ b/cfme/tests/cloud/test_tag_mapping.py @@ -71,7 +71,7 @@ def tag_components(): 'tag_value_{}'.format(fauxfactory.gen_alphanumeric())) -@pytest.mark.provider([AzureProvider], selector=ONE_PER_TYPE, scope='function', override=True) +@pytest.mark.provider([AzureProvider], selector=ONE_PER_TYPE, scope='function') def test_tag_mapping_azure_instances(tagged_vm, map_tags): """" Polarion: diff --git a/cfme/tests/cloud_infra_common/test_cloud_init_provisioning.py b/cfme/tests/cloud_infra_common/test_cloud_init_provisioning.py index f891e4e034..042fca5ab6 100644 --- a/cfme/tests/cloud_infra_common/test_cloud_init_provisioning.py +++ b/cfme/tests/cloud_infra_common/test_cloud_init_provisioning.py @@ -120,8 +120,8 @@ def test_provision_cloud_init(appliance, request, setup_provider, provider, prov @pytest.mark.rhv3 -@pytest.mark.provider([RHEVMProvider], override=True) @test_requirements.provision +@pytest.mark.provider([RHEVMProvider]) def test_provision_cloud_init_payload(appliance, request, setup_provider, provider, provisioning, vm_name): """ diff --git a/cfme/tests/cloud_infra_common/test_custom_attributes_rest.py b/cfme/tests/cloud_infra_common/test_custom_attributes_rest.py index 74824b9508..d36627a2e9 100644 --- a/cfme/tests/cloud_infra_common/test_custom_attributes_rest.py +++ b/cfme/tests/cloud_infra_common/test_custom_attributes_rest.py @@ -201,8 +201,8 @@ def test_delete_from_collection(self, request, collection_name, get_resource): collection = resource.custom_attributes delete_resources_from_collection(attributes, collection=collection, not_found=True) - @pytest.mark.unollectif(lambda provider, collection_name: - _uncollect(provider, collection_name)) + @pytest.mark.uncollectif(lambda provider, collection_name: + _uncollect(provider, collection_name)) @pytest.mark.parametrize("collection_name", COLLECTIONS) def test_delete_single_from_collection(self, request, collection_name, get_resource): """Test deleting single custom attribute from collection using REST API. diff --git a/cfme/tests/cloud_infra_common/test_power_control_manual.py b/cfme/tests/cloud_infra_common/test_power_control_manual.py index 85a7c7ddb5..8b765ca996 100644 --- a/cfme/tests/cloud_infra_common/test_power_control_manual.py +++ b/cfme/tests/cloud_infra_common/test_power_control_manual.py @@ -224,7 +224,7 @@ def test_power_controls_on_vm_in_stack_cloud(): @pytest.mark.tier(2) @pytest.mark.parametrize('context', [ViaREST, ViaUI]) @pytest.mark.provider([CloudProvider, InfraProvider], required_fields=['templates'], - selector=ONE_PER_TYPE, override=True) + selector=ONE_PER_TYPE) @test_requirements.multi_region @test_requirements.power def test_power_operations_from_global_region(provider, context): diff --git a/cfme/tests/cloud_infra_common/test_tag_visibility.py b/cfme/tests/cloud_infra_common/test_tag_visibility.py index 79a5451d5d..b16f7d507a 100644 --- a/cfme/tests/cloud_infra_common/test_tag_visibility.py +++ b/cfme/tests/cloud_infra_common/test_tag_visibility.py @@ -138,7 +138,7 @@ def _check_vm_visibility(group, vm, vis_expect): return _check_vm_visibility -@pytest.mark.provider([InfraProvider], override=True, selector=ONE, scope='module') +@pytest.mark.provider([InfraProvider], selector=ONE, scope='module') def test_tag_expression_and_condition( request, vms_for_tagging, location_tag, service_level_tag, group_with_tag_expression, check_vm_visibility @@ -169,7 +169,7 @@ def test_tag_expression_and_condition( check_vm_visibility(group, first_vm, True) -@pytest.mark.provider([InfraProvider], override=True, selector=ONE, scope='module') +@pytest.mark.provider([InfraProvider], selector=ONE, scope='module') def test_tag_expression_or_condition( request, vms_for_tagging, location_tag, service_level_tag, group_with_tag_expression, check_vm_visibility @@ -200,7 +200,7 @@ def test_tag_expression_or_condition( check_vm_visibility(group, second_vm, True) -@pytest.mark.provider([InfraProvider], override=True, selector=ONE, scope='module') +@pytest.mark.provider([InfraProvider], selector=ONE, scope='module') def test_tag_expression_not_condition( request, vms_for_tagging, location_tag, group_with_tag_expression, check_vm_visibility @@ -227,7 +227,7 @@ def test_tag_expression_not_condition( check_vm_visibility(group, second_vm, True) -@pytest.mark.provider([InfraProvider], override=True, selector=ONE, scope='module') +@pytest.mark.provider([InfraProvider], selector=ONE, scope='module') def test_tag_expression_not_and_condition( request, vms_for_tagging, location_tag, service_level_tag, group_with_tag_expression, @@ -265,7 +265,7 @@ def test_tag_expression_not_and_condition( check_vm_visibility(group, second_vm, True) -@pytest.mark.provider([InfraProvider], override=True, selector=ONE, scope='module') +@pytest.mark.provider([InfraProvider], selector=ONE, scope='module') def test_tag_expression_not_or_condition( request, vms_for_tagging, location_tag, service_level_tag, group_with_tag_expression, check_vm_visibility @@ -298,7 +298,7 @@ def test_tag_expression_not_or_condition( @pytest.mark.tier(2) -@pytest.mark.provider([InfraProvider], override=True, selector=ONE, scope='module') +@pytest.mark.provider([InfraProvider], selector=ONE, scope='module') def test_tag_expression_and_with_or_with_not( request, vms_for_tagging, location_tag, service_level_tag, third_tag, group_with_tag_expression, check_vm_visibility @@ -339,7 +339,7 @@ def test_tag_expression_and_with_or_with_not( @pytest.mark.tier(2) -@pytest.mark.provider([InfraProvider], override=True, selector=ONE, scope='module') +@pytest.mark.provider([InfraProvider], selector=ONE, scope='module') def test_tag_expression_and_with_or( request, vms_for_tagging, location_tag, service_level_tag, third_tag, group_with_tag_expression, check_vm_visibility diff --git a/cfme/tests/cloud_infra_common/test_vm_ownership.py b/cfme/tests/cloud_infra_common/test_vm_ownership.py index 1d2f829444..932a95d80f 100644 --- a/cfme/tests/cloud_infra_common/test_vm_ownership.py +++ b/cfme/tests/cloud_infra_common/test_vm_ownership.py @@ -237,7 +237,7 @@ def test_group_ownership_on_user_or_group_role( assert not check_vm_exists(vm_crud), "vm exists! but shouldn't exist" -@pytest.mark.provider([VMwareProvider], override=True, scope="module") +@pytest.mark.provider([VMwareProvider], scope="module") @pytest.mark.meta(blockers=[BZ(1622952, forced_streams=['5.10'])]) def test_template_set_ownership(appliance, request, provider, setup_provider, vm_crud): """ Sets ownership to an infra template. diff --git a/cfme/tests/configure/test_log_depot_operation.py b/cfme/tests/configure/test_log_depot_operation.py index a742242a22..f2b560946f 100644 --- a/cfme/tests/configure/test_log_depot_operation.py +++ b/cfme/tests/configure/test_log_depot_operation.py @@ -201,7 +201,6 @@ def service_request(appliance, ansible_catalog_item): @pytest.mark.tier(3) -@pytest.mark.nondestructive @pytest.mark.meta(automates=[1652116, 1656318, 1706989]) def test_collect_log_depot(log_depot, appliance, service_request, configured_depot, request): """ Boilerplate test to verify functionality of this concept diff --git a/cfme/tests/configure/test_tag.py b/cfme/tests/configure/test_tag.py index 449447c623..18397858c2 100644 --- a/cfme/tests/configure/test_tag.py +++ b/cfme/tests/configure/test_tag.py @@ -359,7 +359,7 @@ def test_create_tag_with_wrong_arguments(self, appliance): @pytest.mark.tier(3) @pytest.mark.provider( - [CloudProvider, InfraProvider], selector=ONE_PER_CATEGORY, override=True + [CloudProvider, InfraProvider], selector=ONE_PER_CATEGORY ) @pytest.mark.parametrize("collection_name", INFRA_COLLECTION + CLOUD_COLLECTION) @pytest.mark.uncollectif( diff --git a/cfme/tests/configure/test_workers.py b/cfme/tests/configure/test_workers.py index d771b30aab..c7ceedaef4 100644 --- a/cfme/tests/configure/test_workers.py +++ b/cfme/tests/configure/test_workers.py @@ -26,7 +26,6 @@ @pytest.mark.tier(3) @pytest.mark.sauce -@pytest.mark.nondestructive def test_restart_workers(appliance): """ Polarion: diff --git a/cfme/tests/conftest.py b/cfme/tests/conftest.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cfme/tests/containers/test_configurable_menus.py b/cfme/tests/containers/test_configurable_menus.py index 46c733b506..dcf2752adf 100644 --- a/cfme/tests/containers/test_configurable_menus.py +++ b/cfme/tests/containers/test_configurable_menus.py @@ -2,7 +2,7 @@ import pytest from cfme import test_requirements -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.containers.provider import ContainersProvider from cfme.utils.appliance.implementations.ui import navigate_to diff --git a/cfme/tests/control/test_alerts.py b/cfme/tests/control/test_alerts.py index 766b05f499..e3cc868aad 100644 --- a/cfme/tests/control/test_alerts.py +++ b/cfme/tests/control/test_alerts.py @@ -27,12 +27,14 @@ pf1 = ProviderFilter(classes=[InfraProvider]) pf2 = ProviderFilter(classes=[SCVMMProvider, RHEVMProvider], inverted=True) + CANDU_PROVIDER_TYPES = [VMwareProvider] # note: RHV provider is not supported for alerts via the Cloudforms support matrix pytestmark = [ pytest.mark.long_running, pytest.mark.meta(server_roles=["+automate", "+smartproxy", "+notifier"]), + pytest.mark.provider(CANDU_PROVIDER_TYPES, scope="module"), pytest.mark.usefixtures("setup_provider_modscope"), pytest.mark.tier(3), test_requirements.alert @@ -211,7 +213,6 @@ def test_alert_vm_turned_on_more_than_twice_in_past_15_minutes( wait_for_alert(smtp_test, alert, delay=16 * 60) -@pytest.mark.provider(CANDU_PROVIDER_TYPES, scope="module") def test_alert_rtp(request, appliance, full_template_vm, smtp_test, provider, setup_candu, wait_candu, setup_for_alerts): """ Tests a custom alert that uses C&U data to trigger an alert. Since the threshold is set to @@ -249,7 +250,6 @@ def test_alert_rtp(request, appliance, full_template_vm, smtp_test, provider, "text": full_template_vm.name, "from_address": email}) -@pytest.mark.provider(CANDU_PROVIDER_TYPES, scope="module") def test_alert_timeline_cpu(request, appliance, full_template_vm, set_performance_capture_threshold, provider, ssh, setup_candu, wait_candu, setup_for_alerts): @@ -300,7 +300,6 @@ def test_alert_timeline_cpu(request, appliance, full_template_vm, pytest.fail("The event has not been found on the timeline. Event list: {}".format(events)) -@pytest.mark.provider(CANDU_PROVIDER_TYPES, scope="module") def test_alert_snmp(request, appliance, provider, setup_snmp, setup_candu, full_template_vm, wait_candu, setup_for_alerts): """ Tests a custom alert that uses C&U data to trigger an alert. Since the threshold is set to @@ -354,9 +353,8 @@ def _snmp_arrived(): wait_for(_snmp_arrived, timeout="30m", delay=60, message="SNMP trap arrived.") -@pytest.mark.provider(CANDU_PROVIDER_TYPES, scope="module") def test_alert_hardware_reconfigured(request, appliance, configure_fleecing, smtp_test, - full_template_vm, setup_for_alerts): + full_template_vm, setup_for_alerts, provider): """Tests alert based on "Hardware Reconfigured" evaluation. According https://bugzilla.redhat.com/show_bug.cgi?id=1396544 Hardware Reconfigured alerts diff --git a/cfme/tests/infrastructure/test_config_management.py b/cfme/tests/infrastructure/test_config_management.py index 384180b520..758d7e0e1f 100644 --- a/cfme/tests/infrastructure/test_config_management.py +++ b/cfme/tests/infrastructure/test_config_management.py @@ -9,7 +9,6 @@ pytest_generate_tests = generate(gen_func=config_managers) -pytestmark = [pytest.mark.meta(blockers=[1491704])] TEMPLATE_TYPE = { "job": "Job Template (Ansible Tower)", @@ -18,8 +17,9 @@ @pytest.fixture -def config_manager(config_manager_obj): +def config_manager(config_manager_obj, appliance): """ Fixture that provides a random config manager and sets it up""" + config_manager_obj.appliance = appliance config_manager_obj.create() yield config_manager_obj config_manager_obj.delete() diff --git a/cfme/tests/infrastructure/test_config_management_rest.py b/cfme/tests/infrastructure/test_config_management_rest.py index 21d8469bef..74c07656c8 100644 --- a/cfme/tests/infrastructure/test_config_management_rest.py +++ b/cfme/tests/infrastructure/test_config_management_rest.py @@ -15,15 +15,16 @@ # config_managers generates list of managers from cfme_data gen_func=config_managers, # Filter the config manager types for Tower - managers_type='Ansible Tower', + managers_type='ansible', scope='module' ) pytestmark = [test_requirements.rest] @pytest.fixture(scope='module') -def config_manager(config_manager_obj): +def config_manager(config_manager_obj, appliance): """Fixture that provides a random config manager and sets it up.""" + config_manager_obj.appliance = appliance if config_manager_obj.type != 'Ansible Tower': pytest.skip('A non "Ansible Tower" configuration manager provider was selected for test,' 'Please verify cfme_data.yaml content.') diff --git a/cfme/tests/infrastructure/test_datastore_analysis.py b/cfme/tests/infrastructure/test_datastore_analysis.py index b65525069a..6d55a3b207 100644 --- a/cfme/tests/infrastructure/test_datastore_analysis.py +++ b/cfme/tests/infrastructure/test_datastore_analysis.py @@ -8,6 +8,7 @@ from cfme.infrastructure.provider.virtualcenter import VMwareProvider from cfme.utils import testgen from cfme.utils.appliance.implementations.ui import navigate_to +from cfme.utils.log import logger from cfme.utils.wait import wait_for pytestmark = [test_requirements.smartstate] @@ -27,45 +28,54 @@ def pytest_generate_tests(metafunc): argnames, argvalues, idlist = testgen.providers_by_class( - metafunc, [RHEVMProvider, VMwareProvider], required_fields=['datastores']) + metafunc, + [RHEVMProvider, VMwareProvider], + required_fields=['datastores'] + ) argnames.append('datastore_type') + argnames.append('datastore_name') new_idlist = [] new_argvalues = [] for index, argvalue_tuple in enumerate(argvalues): args = dict(list(zip(argnames, argvalue_tuple))) - datastores = args['provider'].data.datastores - for ds in datastores: - if not ds.get('test_fleece', False): - continue - assert ds['type'] in DATASTORE_TYPES, ( - 'datastore type must be set to [{}] for smartstate analysis tests'.format( - '|'.join(DATASTORE_TYPES))) - new_argvalues.append([args["provider"], ds['type']]) - test_id = '{}-{}'.format(idlist[index], ds['type']) - new_idlist.append(test_id) + provider_arg = args["provider"] + datastores = provider_arg.data.get("datastores", {}) + # don't collect any datastores without test_fleece set + # only collect datastores with type matching accepted list + testable_datastores = [ + (ds.get("type"), ds.get("name")) + for ds in datastores + if ds.get('test_fleece', False) and ds.get("type") in DATASTORE_TYPES + ] + for ds_type, ds_name in testable_datastores: + new_argvalues.append([provider_arg, ds_type, ds_name]) + new_idlist.append(f"{idlist[index]}-{ds_type}") + else: + logger.warning(f"No testable datastores found for SSA on {provider_arg}") testgen.parametrize(metafunc, argnames, new_argvalues, ids=new_idlist, scope="module") @pytest.fixture(scope='module') -def datastore(appliance, provider, datastore_type): - datastores = provider.data.get('datastores') - for ds in datastores: - if ds.type == datastore_type: - return appliance.collections.datastores.instantiate( - name=ds.name, provider=provider, type=ds.type) +def datastore(appliance, provider, datastore_type, datastore_name): + return appliance.collections.datastores.instantiate(name=datastore_name, + provider=provider, + type=datastore_type) @pytest.fixture(scope='module') def datastores_hosts_setup(provider, datastore): hosts = datastore.hosts.all() - assert hosts, "No hosts attached to this datastore found" for host in hosts: - host_data = [data for data in provider.data['hosts'] if data['name'] == host.name] + host_data = [data + for data in provider.data.get("hosts", {}) + if data.get("name") == host.name] if not host_data: - pytest.skip("No host data") + pytest.skip(f"No host data for provider {provider} and datastore {datastore}") host.update_credentials_rest(credentials=host_data[0]['credentials']) + else: + pytest.skip(f"No hosts attached to the datastore selected for testing: {datastore}") yield for host in hosts: host.remove_credentials_rest() diff --git a/cfme/tests/infrastructure/test_host.py b/cfme/tests/infrastructure/test_host.py index 3d617f219c..2f940882e5 100644 --- a/cfme/tests/infrastructure/test_host.py +++ b/cfme/tests/infrastructure/test_host.py @@ -215,7 +215,7 @@ def test_multiple_host_bad_creds(setup_provider, provider): @test_requirements.tag -@pytest.mark.provider([InfraProvider], override=True, selector=ONE, scope='module') +@pytest.mark.provider([InfraProvider], selector=ONE, scope='module') def test_tag_host_after_provider_delete(provider, appliance, setup_provider, request): """Test if host can be tagged after delete diff --git a/cfme/tests/infrastructure/test_providers.py b/cfme/tests/infrastructure/test_providers.py index 61a9d8fdcf..6f96509a9f 100644 --- a/cfme/tests/infrastructure/test_providers.py +++ b/cfme/tests/infrastructure/test_providers.py @@ -339,7 +339,7 @@ def test_provider_rhv_create_delete_tls(request, provider, verify_tls): @pytest.mark.rhv3 @test_requirements.rhev @pytest.mark.meta(automates=[1691109, 1731237]) -@pytest.mark.provider([RHEVMProvider], selector=ONE_PER_VERSION, override=True, scope="function") +@pytest.mark.provider([RHEVMProvider], selector=ONE_PER_VERSION, scope="function") def test_rhv_guest_devices_count(appliance, setup_provider, provider): """ Polarion: diff --git a/cfme/tests/infrastructure/test_provisioning_dialog.py b/cfme/tests/infrastructure/test_provisioning_dialog.py index 9f36ee4f9e..8660fab291 100644 --- a/cfme/tests/infrastructure/test_provisioning_dialog.py +++ b/cfme/tests/infrastructure/test_provisioning_dialog.py @@ -10,7 +10,7 @@ from widgetastic_patternfly import CheckableBootstrapTreeview as CbTree from cfme import test_requirements -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.infrastructure.provider import InfraProvider from cfme.infrastructure.provider.rhevm import RHEVMProvider from cfme.infrastructure.provider.scvmm import SCVMMProvider @@ -432,7 +432,7 @@ def test_provision_vm_with_2_nics(provisioner, provisioning, prov_data, vm_name) assert len(nics) == 2, 'The VM should have 2 NICs attached.' -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_vmware_default_placement(provisioner, prov_data, provider, setup_provider, vm_name): """ Tests whether vm placed in Datacenter root after the provisioning. diff --git a/cfme/tests/infrastructure/test_snapshot.py b/cfme/tests/infrastructure/test_snapshot.py index 13853df427..f2575b5a9c 100644 --- a/cfme/tests/infrastructure/test_snapshot.py +++ b/cfme/tests/infrastructure/test_snapshot.py @@ -130,7 +130,7 @@ def test_snapshot_crud(small_test_vm, provider): @pytest.mark.rhv3 @test_requirements.rhev -@pytest.mark.provider([RHEVMProvider], override=True) +@pytest.mark.provider([RHEVMProvider]) @pytest.mark.meta(automates=[BZ(1443411)]) def test_delete_active_vm_snapshot(small_test_vm): """ @@ -153,7 +153,7 @@ def test_delete_active_vm_snapshot(small_test_vm): @pytest.mark.rhv3 @test_requirements.rhev -@pytest.mark.provider([RHEVMProvider], override=True) +@pytest.mark.provider([RHEVMProvider]) def test_create_without_description(small_test_vm): """ Test that we get an error message when we try to create a snapshot with @@ -179,7 +179,7 @@ def test_create_without_description(small_test_vm): view.flash.assert_message('Description is required') -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_delete_all_snapshots(small_test_vm, provider): """Tests snapshot removal @@ -292,7 +292,7 @@ def test_verify_revert_snapshot(full_test_vm, provider, soft_assert, register_ev verify_revert_snapshot(full_test_vm, provider, soft_assert, register_event, request) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_revert_active_snapshot(full_test_vm, provider, soft_assert, register_event, request): """Tests revert active snapshot @@ -338,7 +338,7 @@ def test_revert_to_active_vm(small_test_vm, provider): @pytest.mark.rhv3 -@pytest.mark.provider([RHEVMProvider], override=True) +@pytest.mark.provider([RHEVMProvider]) @pytest.mark.meta(automates=[BZ(1375544)]) def test_revert_on_running_vm(small_test_vm): """ @@ -373,7 +373,7 @@ def setup_snapshot_env(test_vm, memory): @pytest.mark.parametrize("parent_vm", ["on_with_memory", "on_without_memory", "off"]) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_verify_vm_state_revert_snapshot(provider, parent_vm, small_test_vm): """ test vm state after revert snapshot with parent vm: @@ -401,7 +401,7 @@ def test_verify_vm_state_revert_snapshot(provider, parent_vm, small_test_vm): assert bool(small_test_vm.mgmt.is_running) == memory -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_operations_suspended_vm(small_test_vm, soft_assert): """Tests snapshot operations on suspended vm @@ -444,7 +444,7 @@ def test_operations_suspended_vm(small_test_vm, soft_assert): snapshot2.delete() -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_operations_powered_off_vm(small_test_vm): """ Polarion: @@ -496,7 +496,7 @@ def test_snapshot_history_btn(small_test_vm, provider): assert snapshot_view.is_displayed -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_create_snapshot_via_ae(appliance, request, domain, small_test_vm): """This test checks whether the vm.create_snapshot works in AE. @@ -723,7 +723,7 @@ def test_snapshot_tree_view_functionality(): @pytest.mark.manual -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) @test_requirements.snapshot @pytest.mark.tier(1) @pytest.mark.meta(coverage=[1395116]) @@ -762,7 +762,7 @@ def test_snapshot_link_after_deleting_snapshot(): @pytest.mark.manual -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) @test_requirements.snapshot @test_requirements.ssui @pytest.mark.tier(2) @@ -795,7 +795,7 @@ def test_sui_snapshot_timeline_time_of_creation(): @pytest.mark.manual -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) @test_requirements.snapshot @test_requirements.ssui @pytest.mark.tier(2) @@ -830,7 +830,7 @@ def test_sui_test_snapshot_count(): @pytest.mark.manual -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) @test_requirements.snapshot @test_requirements.ssui @pytest.mark.tier(2) @@ -868,7 +868,7 @@ def test_snapshot_timeline_crud(): @pytest.mark.manual -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) @test_requirements.snapshot @pytest.mark.meta(coverage=[1419872]) def test_creating_second_snapshot_on_suspended_vm(): diff --git a/cfme/tests/infrastructure/test_tenant_quota.py b/cfme/tests/infrastructure/test_tenant_quota.py index d1b9ee471d..3bf67de818 100644 --- a/cfme/tests/infrastructure/test_tenant_quota.py +++ b/cfme/tests/infrastructure/test_tenant_quota.py @@ -328,7 +328,7 @@ def test_setting_child_quota_more_than_parent(appliance, tenants_setup, parent_q @pytest.mark.long_running -@pytest.mark.provider([VMwareProvider], override=True, scope="module", +@pytest.mark.provider([VMwareProvider], scope="module", required_fields=[['templates', 'small_template']], selector=ONE_PER_TYPE) @pytest.mark.parametrize( ['set_roottenant_quota', 'custom_prov_data'], diff --git a/cfme/tests/infrastructure/test_timelines.py b/cfme/tests/infrastructure/test_timelines.py index 138349c7a2..28fdc16f34 100644 --- a/cfme/tests/infrastructure/test_timelines.py +++ b/cfme/tests/infrastructure/test_timelines.py @@ -446,7 +446,7 @@ def test_infra_timeline_diagnostic(new_vm, soft_assert, mark_vm_as_appliance): @pytest.mark.meta(blockers=[BZ(1622952)]) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_infra_timeline_clone_event(new_vm, soft_assert): """Test that the event clone is visible on the management event timeline of the Vm, Vm's cluster, VM's host, VM's provider. @@ -494,7 +494,7 @@ def test_infra_timeline_migrate_event(new_vm, soft_assert): vm_event.catch_in_timelines(soft_assert, targets) -@pytest.mark.provider([VMwareProvider], override=True, scope='function') +@pytest.mark.provider([VMwareProvider], scope='function') def test_infra_timeline_rename_event(new_vm, soft_assert): """Test that the event rename is visible on the management event timeline of the Vm, Vm's cluster, VM's host, VM's provider. diff --git a/cfme/tests/infrastructure/test_vm_ownership.py b/cfme/tests/infrastructure/test_vm_ownership.py index 27d25fe2ca..58fe0fa557 100644 --- a/cfme/tests/infrastructure/test_vm_ownership.py +++ b/cfme/tests/infrastructure/test_vm_ownership.py @@ -126,7 +126,7 @@ def small_vm(provider, small_template): @test_requirements.power -@pytest.mark.provider([VMwareProvider], override=True, scope="function", selector=ONE_PER_TYPE) +@pytest.mark.provider([VMwareProvider], scope="function", selector=ONE_PER_TYPE) def test_rename_vm(small_vm): """Test for rename the VM. diff --git a/cfme/tests/infrastructure/test_vm_power_control.py b/cfme/tests/infrastructure/test_vm_power_control.py index b5324cb031..3a260416b8 100644 --- a/cfme/tests/infrastructure/test_vm_power_control.py +++ b/cfme/tests/infrastructure/test_vm_power_control.py @@ -6,7 +6,7 @@ from cfme import test_requirements from cfme.base.credential import Credential -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.infrastructure.provider import InfraProvider from cfme.infrastructure.provider.rhevm import RHEVMProvider from cfme.infrastructure.provider.scvmm import SCVMMProvider @@ -533,7 +533,7 @@ def test_vm_power_options_from_off(provider, soft_assert, testing_vm, ensure_vm_ check_power_options(provider, soft_assert, testing_vm, testing_vm.STATE_OFF) -@pytest.mark.provider([VMwareProvider, RHEVMProvider], override=True, scope='function') +@pytest.mark.provider([VMwareProvider, RHEVMProvider], scope='function') @pytest.mark.meta(automates=[1571830, 1650506]) def test_guest_os_reset(appliance, provider, testing_vm_tools, ensure_vm_running, soft_assert): """Tests vm guest os reset @@ -572,7 +572,7 @@ def test_guest_os_reset(appliance, provider, testing_vm_tools, ensure_vm_running @pytest.mark.meta(automates=[1723485, 1571895, 1650506]) -@pytest.mark.provider([VMwareProvider, RHEVMProvider], override=True) +@pytest.mark.provider([VMwareProvider, RHEVMProvider]) @pytest.mark.meta(blockers=[BZ(1723485, forced_streams=["5.11"], unblock=lambda provider: not (provider.one_of(RHEVMProvider) and not provider.version < 4.3))]) @@ -628,7 +628,7 @@ def new_user(request, appliance): @pytest.mark.tier(1) @pytest.mark.meta(automates=[1687597]) -@pytest.mark.provider([VMwareProvider], selector=ONE_PER_TYPE, override=True) +@pytest.mark.provider([VMwareProvider], selector=ONE_PER_TYPE) def test_retire_vm_with_vm_user_role(new_user, appliance, testing_vm): """ Bugzilla: diff --git a/cfme/tests/infrastructure/test_vm_reconfigure.py b/cfme/tests/infrastructure/test_vm_reconfigure.py index b63774669f..ea8dec13fd 100644 --- a/cfme/tests/infrastructure/test_vm_reconfigure.py +++ b/cfme/tests/infrastructure/test_vm_reconfigure.py @@ -251,13 +251,14 @@ def test_vm_reconfig_add_remove_hw_hot( reconfigure_vm(full_vm, orig_config) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) @pytest.mark.parametrize('disk_type', ['thin', 'thick']) -@pytest.mark.parametrize( - 'disk_mode', ['persistent', 'independent_persistent', 'independent_nonpersistent']) -@pytest.mark.uncollectif( - lambda disk_mode, vm_state: disk_mode == 'independent_nonpersistent' and vm_state == 'hot', - reason='The disk resize operation not supported for hot-independent_nonpersistent') +@pytest.mark.parametrize('disk_mode', ['persistent', + 'independent_persistent', + 'independent_nonpersistent']) +@pytest.mark.uncollectif(lambda disk_mode, vm_state: + disk_mode == 'independent_nonpersistent' and vm_state == 'hot', + reason='Disk resize not supported for hot vm independent_nonpersistent') def test_vm_reconfig_resize_disk(appliance, full_vm, vm_state, disk_type, disk_mode): """ Resize the disk while VM is running and not running Polarion: @@ -273,9 +274,9 @@ def test_vm_reconfig_resize_disk(appliance, full_vm, vm_state, disk_type, disk_m { "disk_size_in_mb": 20, "sync": True, - "persistent": False if disk_mode == "independent_nonpersistent" else True, - "thin_provisioned": True if disk_type == "thin" else False, - "dependent": False if "independent" in disk_mode else True, + "persistent": disk_mode != "independent_nonpersistent", + "thin_provisioned": disk_type == "thin", + "dependent": not "independent"in disk_mode, "bootable": False, } ] @@ -293,7 +294,10 @@ def test_vm_reconfig_resize_disk(appliance, full_vm, vm_state, disk_type, disk_m # there will always be 2 disks after the disk has been added disks_present = [disk.filename for disk in full_vm.configuration.disks] # get the newly added disk - disk_added = list(set(disks_present) - set(initial_disks))[0] + try: + disk_added = list(set(disks_present) - set(initial_disks))[0] + except IndexError: + pytest.fail('Added disk not found in diff between initial and present disks') # resize the disk disk_size = 500 @@ -312,10 +316,11 @@ def test_vm_reconfig_resize_disk(appliance, full_vm, vm_state, disk_type, disk_m @pytest.mark.customer_scenario @pytest.mark.meta(automates=[1631448, 1696841]) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) @pytest.mark.parametrize('disk_type', ['thin', 'thick']) -@pytest.mark.parametrize( - 'disk_mode', ['persistent', 'independent_persistent', 'independent_nonpersistent']) +@pytest.mark.parametrize('disk_mode', ['persistent', + 'independent_persistent', + 'independent_nonpersistent']) def test_vm_reconfig_resize_disk_snapshot(request, disk_type, disk_mode, full_vm, memory=False): """ @@ -361,7 +366,7 @@ def test_vm_reconfig_resize_disk_snapshot(request, disk_type, disk_mode, full_vm @pytest.mark.manual @pytest.mark.tier(1) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) @pytest.mark.parametrize( 'adapters_type', ['DPortGroup', 'VmNetwork', 'MgmtNetwork', 'VmKernel']) def test_vm_reconfig_add_remove_network_adapters(adapters_type): @@ -385,7 +390,7 @@ def test_vm_reconfig_add_remove_network_adapters(adapters_type): @pytest.mark.manual @pytest.mark.tier(2) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_reconfigure_vm_vmware_mem_multiple(): """ Polarion: @@ -418,7 +423,7 @@ def test_reconfigure_vm_vmware_mem_multiple(): @pytest.mark.manual @pytest.mark.tier(2) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_reconfigure_vm_vmware_sockets_multiple(): """ Test changing the cpu sockets of multiple vms at the same time. @@ -443,7 +448,7 @@ def test_reconfigure_vm_vmware_sockets_multiple(): @pytest.mark.manual @pytest.mark.tier(2) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_reconfigure_vm_vmware_cores_multiple(): """ Test changing the cpu cores of multiple vms at the same time. @@ -477,7 +482,7 @@ def test_reconfigure_vm_vmware_cores_multiple(): @pytest.mark.manual @pytest.mark.tier(3) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_reconfigure_add_disk_cold(): """ Test adding 16th disk to test how a new scsi controller is handled. @@ -502,7 +507,7 @@ def test_reconfigure_add_disk_cold(): @pytest.mark.manual @pytest.mark.tier(2) -@pytest.mark.provider([VMwareProvider], override=True) +@pytest.mark.provider([VMwareProvider]) def test_reconfigure_add_disk_cold_controller_sas(): """ @@ -623,7 +628,7 @@ def test_vm_disk_reconfig_via_rest(appliance, full_vm): @pytest.mark.tier(2) @pytest.mark.parametrize('context', [ViaREST, ViaUI]) @pytest.mark.provider([VMwareProvider, RHEVMProvider], - required_fields=['templates'], selector=ONE_PER_TYPE, override=True) + required_fields=['templates'], selector=ONE_PER_TYPE) @test_requirements.multi_region @test_requirements.reconfigure def test_vm_reconfigure_from_global_region(context): diff --git a/cfme/tests/infrastructure/test_vmware_provider.py b/cfme/tests/infrastructure/test_vmware_provider.py index 21a5d8f94c..82201ab298 100644 --- a/cfme/tests/infrastructure/test_vmware_provider.py +++ b/cfme/tests/infrastructure/test_vmware_provider.py @@ -31,7 +31,7 @@ @pytest.mark.tier(3) -@pytest.mark.provider([VMwareProvider], selector=ONE, override=True) +@pytest.mark.provider([VMwareProvider], selector=ONE) def test_vmware_provider_filters(appliance, provider, soft_assert): """ N-3 filters for esx provider. @@ -63,7 +63,7 @@ def test_vmware_provider_filters(appliance, provider, soft_assert): @pytest.mark.tier(3) @pytest.mark.long_running @pytest.mark.ignore_stream("upstream") -@pytest.mark.provider([VMwareProvider], selector=ONE, override=True) +@pytest.mark.provider([VMwareProvider], selector=ONE) def test_appliance_scsi_control_vmware(request, appliance): """ Appliance cfme-vsphere-paravirtual-*.ova has SCSI controller as Para @@ -105,7 +105,7 @@ def _cleanup(): @pytest.mark.tier(1) -@pytest.mark.provider([VMwareProvider], selector=ONE, override=True) +@pytest.mark.provider([VMwareProvider], selector=ONE) def test_vmware_vds_ui_display(soft_assert, appliance, provider): """ Virtual Distributed Switch port groups are displayed for VMs assigned @@ -141,7 +141,7 @@ def test_vmware_vds_ui_display(soft_assert, appliance, provider): @pytest.mark.meta(blockers=[BZ(1650441, forced_streams=['5.10', '5.11'])]) @pytest.mark.provider([VMwareProvider], required_fields=filter_fields + [(['cap_and_util', 'capandu_vm'], 'cu-24x7')], - selector=ONE, override=True) + selector=ONE) def test_vmware_reconfigure_vm_controller_type(appliance, provider): """ Edit any VM which is provisioned for vSphere and select "Reconfigure this VM" option. @@ -179,7 +179,7 @@ def test_vmware_reconfigure_vm_controller_type(appliance, provider): @pytest.mark.tier(1) -@pytest.mark.provider([VMwareProvider], selector=ONE, override=True) +@pytest.mark.provider([VMwareProvider], selector=ONE) def test_vmware_vds_ui_tagging(appliance, provider, soft_assert): """ Virtual Distributed Switch port groups are displayed for VMs assigned @@ -244,9 +244,9 @@ def test_vmware_inaccessible_datastore(): @pytest.mark.tier(1) @pytest.mark.provider([VMwareProvider], selector=ONE, - required_fields=filter_fields + [(['cap_and_util', 'capandu_vm'], 'cu-24x7')], - override=True) + required_fields=filter_fields + [(['cap_and_util', 'capandu_vm'], 'cu-24x7')]) @pytest.mark.meta(automates=[1689369]) +@pytest.mark.meta(blockers=[BZ(1689369, forced_streams=['5.10', '5.11'])]) def test_vmware_cdrom_dropdown_not_blank(appliance, provider): """ Test CD/DVD Drives dropdown lists ISO files, dropdown is not blank @@ -382,7 +382,7 @@ def test_vmware_provisioned_vm_host_relationship(request, appliance, provider): @pytest.mark.tier(1) -@pytest.mark.provider([VMwareProvider], selector=ONE, override=True) +@pytest.mark.provider([VMwareProvider], selector=ONE) def test_esxi_reboot_not_orphan_vms(appliance, provider): """ By mimicking ESXi reboot effect on VMs in CFME, make sure they are not getting marked orphaned. @@ -430,7 +430,7 @@ def test_esxi_reboot_not_orphan_vms(appliance, provider): @pytest.mark.tier(1) -@pytest.mark.provider([VMwareProvider], selector=ONE, override=True) +@pytest.mark.provider([VMwareProvider], selector=ONE) @pytest.mark.meta(automates=[1688900]) def test_switches_class_present_ems(appliance, provider): """ @@ -475,7 +475,7 @@ def test_switches_class_present_ems(appliance, provider): @pytest.mark.tier(1) -@pytest.mark.provider([VMwareProvider], selector=ONE, override=True) +@pytest.mark.provider([VMwareProvider], selector=ONE) @pytest.mark.meta(automates=[1719399]) def test_rebuilt_vcenter_duplicate_hosts(appliance, provider): """ diff --git a/cfme/tests/networks/test_sdn_balancers.py b/cfme/tests/networks/test_sdn_balancers.py index 853b040855..f6f3c8d169 100644 --- a/cfme/tests/networks/test_sdn_balancers.py +++ b/cfme/tests/networks/test_sdn_balancers.py @@ -72,7 +72,7 @@ def test_sdn_balancers_detail(provider, network_prov_with_load_balancers): # only one provider is needed for that test, used Azure as it has balancers -@pytest.mark.provider([AzureProvider], scope='module', override=True) +@pytest.mark.provider([AzureProvider], scope='module') @test_requirements.tag @pytest.mark.parametrize('visibility', [True, False], ids=['visible', 'notVisible']) def test_sdn_balancers_tagvis(check_item_visibility, visibility, network_prov_with_load_balancers): diff --git a/cfme/tests/networks/test_sdn_inventory_collection.py b/cfme/tests/networks/test_sdn_inventory_collection.py index e5b4c8a960..b8bad2c88b 100644 --- a/cfme/tests/networks/test_sdn_inventory_collection.py +++ b/cfme/tests/networks/test_sdn_inventory_collection.py @@ -40,7 +40,7 @@ def test_sdn_api_inventory_networks(provider, appliance): 'cfme list: {cfme}'.format(networks=prov_networks, cfme=cfme_networks) -@pytest.mark.provider([AzureProvider, EC2Provider], override=True, scope='function') +@pytest.mark.provider([AzureProvider, EC2Provider], scope='function') def test_sdn_api_inventory_routers(provider, appliance): """Pulls the list of routers from the Provider API and from the appliance. Compare the 2 results. If Similar, then test is successful @@ -89,7 +89,7 @@ def test_sdn_api_inventory_subnets(provider, appliance): 'different from cfme list: {cfme}'.format(sub=prov_subnets, cfme=cfme_subnets) -@pytest.mark.provider([EC2Provider, AzureProvider], override=True, scope='function') +@pytest.mark.provider([EC2Provider, AzureProvider], scope='function') def test_sdn_api_inventory_security_groups(provider, appliance): """Pulls the list of security groups from the Provider API and from the appliance. Compare the 2 results. If Similar, then test is successful @@ -110,7 +110,7 @@ def test_sdn_api_inventory_security_groups(provider, appliance): @pytest.mark.ignore_stream('5.11') # Load Balancers are deprecated in 5.11 -@pytest.mark.provider([EC2Provider, AzureProvider], override=True, scope='function') +@pytest.mark.provider([EC2Provider, AzureProvider], scope='function') def test_sdn_api_inventory_loadbalancers(provider, appliance): """Pulls the list of loadbalancers from the Provider API and from the appliance. Compare the 2 results. If Similar, then test is successful @@ -144,8 +144,8 @@ def secgroup_with_rule(provider): provider.mgmt.remove_netsec_group(secgroup_name, res_group) -@pytest.mark.provider([AzureProvider], override=True, scope='function') @pytest.mark.meta(automates=[1520196]) +@pytest.mark.provider([AzureProvider], scope='function') def test_sdn_nsg_firewall_rules(provider, appliance, secgroup_with_rule): """ Pulls the list of firewall ports from Provider API and from appliance. Compare the 2 results. If same, then test is successful. diff --git a/cfme/tests/pod/test_appliance_crud.py b/cfme/tests/pod/test_appliance_crud.py index a20468701a..b7562f0797 100644 --- a/cfme/tests/pod/test_appliance_crud.py +++ b/cfme/tests/pod/test_appliance_crud.py @@ -6,11 +6,11 @@ import fauxfactory import pytest import yaml -from pytest import config from wrapanapi.systems.container.rhopenshift import ApiException from cfme.containers.provider.openshift import OpenshiftProvider from cfme.fixtures.appliance import sprout_appliances +from cfme.fixtures.pytest_store import store from cfme.test_framework.appliance import PLUGIN_KEY from cfme.utils import conf from cfme.utils import ssh @@ -199,7 +199,7 @@ def temp_pod_appliance(appliance, provider, appliance_data, pytestconfig): stack.push(appliance) # framework will try work with default appliance if browser restarts w/o this # workaround - holder = config.pluginmanager.get_plugin(PLUGIN_KEY) + holder = store.config.pluginmanager.get_plugin(PLUGIN_KEY) holder.held_appliance = appliance yield appliance stack.pop() @@ -235,7 +235,7 @@ def is_api_available(appliance): # workaround appliance.is_pod = True stack.push(appliance) - holder = config.pluginmanager.get_plugin(PLUGIN_KEY) + holder = store.config.pluginmanager.get_plugin(PLUGIN_KEY) holder.held_appliance = appliance # workaround, appliance looks ready but api may return errors wait_for(is_api_available, func_args=[appliance], num_sec=30) @@ -303,7 +303,7 @@ def temp_pod_ansible_appliance(provider, appliance_data, template_tags): with IPAppliance(**params) as appliance: # framework will try work with default appliance if browser restarts w/o this # workaround - holder = config.pluginmanager.get_plugin(PLUGIN_KEY) + holder = store.config.pluginmanager.get_plugin(PLUGIN_KEY) holder.held_appliance = appliance yield appliance finally: diff --git a/cfme/tests/services/test_ansible_workflow_servicecatalogs.py b/cfme/tests/services/test_ansible_workflow_servicecatalogs.py index 05cc781825..48aef96f7b 100644 --- a/cfme/tests/services/test_ansible_workflow_servicecatalogs.py +++ b/cfme/tests/services/test_ansible_workflow_servicecatalogs.py @@ -37,8 +37,9 @@ def pytest_generate_tests(metafunc): @pytest.fixture(scope="module") -def tower_manager(config_manager_obj): +def tower_manager(config_manager_obj, appliance): """ Fixture that sets up Ansible Tower provider""" + config_manager_obj.appliance = appliance if config_manager_obj.type == "Ansible Tower": config_manager_obj.create(validate=True) yield config_manager_obj diff --git a/cfme/tests/services/test_catalog_item.py b/cfme/tests/services/test_catalog_item.py index 42a46f96ab..ecd7a745b3 100644 --- a/cfme/tests/services/test_catalog_item.py +++ b/cfme/tests/services/test_catalog_item.py @@ -5,7 +5,7 @@ import cfme.tests.configure.test_access_control as tac from cfme import test_requirements -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.services.catalogs.catalog_items import AddCatalogItemView from cfme.services.catalogs.catalog_items import AllCatalogItemView from cfme.services.catalogs.catalog_items import DetailsCatalogItemView diff --git a/cfme/tests/services/test_config_provider_servicecatalogs.py b/cfme/tests/services/test_config_provider_servicecatalogs.py index 4efb7ab7fe..0863071cac 100644 --- a/cfme/tests/services/test_config_provider_servicecatalogs.py +++ b/cfme/tests/services/test_config_provider_servicecatalogs.py @@ -39,8 +39,9 @@ def pytest_generate_tests(metafunc): @pytest.fixture(scope="module") -def config_manager(config_manager_obj): +def config_manager(config_manager_obj, appliance): """ Fixture that provides a random config manager and sets it up""" + config_manager_obj.appliance = appliance if config_manager_obj.type == "Ansible Tower": config_manager_obj.create(validate=True) else: diff --git a/cfme/tests/test_modules_importable.py b/cfme/tests/test_modules_importable.py index c183614590..205e79f65e 100644 --- a/cfme/tests/test_modules_importable.py +++ b/cfme/tests/test_modules_importable.py @@ -14,14 +14,6 @@ 'cfme/utils/ports.py', # module object 'cfme/utils/dockerbot/check_prs.py', # unprotected script 'cfme/utils/conf.py', # config object that replaces the module - 'cfme/intelligence/rss.py', # import loops - 'cfme/intelligence/timelines.py', - 'cfme/intelligence/chargeback/rates.py', - 'cfme/intelligence/chargeback/assignments.py', - 'cfme/intelligence/chargeback/__init__.py', - 'cfme/fixtures/widgets.py', - 'cfme/dashboard.py', - 'cfme/configure/tasks.py', ]) @@ -34,8 +26,11 @@ def test_import_own_module(module_path): casecomponent: Appliance initialEstimate: 1/4h """ + if module_path in KNOWN_FAILURES: - pytest.skip("{} is a known failed path".format(ROOT.dirpath().bestrelpath(module_path))) + pytest.skip(f"{ROOT.dirpath().bestrelpath(module_path)} is a known failed path") subprocess.check_call( [sys.executable, '-c', - 'import sys, py;py.path.local(sys.argv[1]).pyimport()', str(module_path)], timeout=60) + 'import sys, py;py.path.local(sys.argv[1]).pyimport()', + str(module_path)] + ) diff --git a/cfme/tests/v2v/test_migration_throttling.py b/cfme/tests/v2v/test_migration_throttling.py index 0d983340e1..f1883e60d9 100644 --- a/cfme/tests/v2v/test_migration_throttling.py +++ b/cfme/tests/v2v/test_migration_throttling.py @@ -4,9 +4,9 @@ from cfme import test_requirements from cfme.cloud.provider.openstack import OpenStackProvider -from cfme.fixtures.provider import rhel69_template -from cfme.fixtures.provider import rhel7_minimal -from cfme.fixtures.provider import ubuntu16_template +from cfme.fixtures.templates import rhel69_template +from cfme.fixtures.templates import rhel7_minimal +from cfme.fixtures.templates import ubuntu16_template from cfme.infrastructure.provider.rhevm import RHEVMProvider from cfme.infrastructure.provider.virtualcenter import VMwareProvider from cfme.markers.env_markers.provider import ONE_PER_TYPE diff --git a/cfme/tests/v2v/test_post_migrations.py b/cfme/tests/v2v/test_post_migrations.py index 0d1880fdd4..3a64275606 100644 --- a/cfme/tests/v2v/test_post_migrations.py +++ b/cfme/tests/v2v/test_post_migrations.py @@ -5,8 +5,8 @@ import pytest from cfme import test_requirements -from cfme.base.login import BaseLoggedInPage from cfme.cloud.provider.openstack import OpenStackProvider +from cfme.common import BaseLoggedInPage from cfme.fixtures.v2v_fixtures import get_migrated_vm from cfme.infrastructure.provider.rhevm import RHEVMProvider from cfme.infrastructure.provider.virtualcenter import VMwareProvider diff --git a/cfme/tests/v2v/test_v2v_ansible.py b/cfme/tests/v2v/test_v2v_ansible.py index bf487c0057..9d26a99146 100644 --- a/cfme/tests/v2v/test_v2v_ansible.py +++ b/cfme/tests/v2v/test_v2v_ansible.py @@ -3,7 +3,7 @@ from cfme import test_requirements from cfme.cloud.provider.openstack import OpenStackProvider -from cfme.fixtures.provider import rhel7_minimal +from cfme.fixtures.templates import rhel7_minimal from cfme.fixtures.v2v_fixtures import cleanup_target from cfme.fixtures.v2v_fixtures import get_migrated_vm from cfme.infrastructure.provider.rhevm import RHEVMProvider diff --git a/cfme/tests/v2v/test_v2v_cancel_migrations.py b/cfme/tests/v2v/test_v2v_cancel_migrations.py index 23de72963c..b5e5b0b3ba 100644 --- a/cfme/tests/v2v/test_v2v_cancel_migrations.py +++ b/cfme/tests/v2v/test_v2v_cancel_migrations.py @@ -5,7 +5,7 @@ from cfme import test_requirements from cfme.cloud.provider.openstack import OpenStackProvider -from cfme.fixtures.provider import rhel7_minimal +from cfme.fixtures.templates import rhel7_minimal from cfme.infrastructure.provider.rhevm import RHEVMProvider from cfme.infrastructure.provider.virtualcenter import VMwareProvider from cfme.markers.env_markers.provider import ONE_PER_TYPE diff --git a/cfme/tests/v2v/test_v2v_migrations.py b/cfme/tests/v2v/test_v2v_migrations.py index 68b9ae18f4..b47aab6a78 100644 --- a/cfme/tests/v2v/test_v2v_migrations.py +++ b/cfme/tests/v2v/test_v2v_migrations.py @@ -4,16 +4,16 @@ from cfme import test_requirements from cfme.cloud.provider.openstack import OpenStackProvider -from cfme.fixtures.provider import dportgroup_template -from cfme.fixtures.provider import dual_disk_template -from cfme.fixtures.provider import dual_network_template -from cfme.fixtures.provider import rhel69_template -from cfme.fixtures.provider import rhel7_minimal -from cfme.fixtures.provider import ubuntu16_template -from cfme.fixtures.provider import win10_template -from cfme.fixtures.provider import win2012_template -from cfme.fixtures.provider import win2016_template -from cfme.fixtures.provider import win7_template +from cfme.fixtures.templates import dportgroup_template +from cfme.fixtures.templates import dual_disk_template +from cfme.fixtures.templates import dual_network_template +from cfme.fixtures.templates import rhel69_template +from cfme.fixtures.templates import rhel7_minimal +from cfme.fixtures.templates import ubuntu16_template +from cfme.fixtures.templates import win10_template +from cfme.fixtures.templates import win2012_template +from cfme.fixtures.templates import win2016_template +from cfme.fixtures.templates import win7_template from cfme.fixtures.v2v_fixtures import cleanup_target from cfme.fixtures.v2v_fixtures import get_migrated_vm from cfme.infrastructure.provider.rhevm import RHEVMProvider diff --git a/cfme/tests/v2v/test_v2v_migrations_single_vcenter.py b/cfme/tests/v2v/test_v2v_migrations_single_vcenter.py index 0b83e6d8d4..af96d83bf0 100644 --- a/cfme/tests/v2v/test_v2v_migrations_single_vcenter.py +++ b/cfme/tests/v2v/test_v2v_migrations_single_vcenter.py @@ -5,10 +5,10 @@ from cfme import test_requirements from cfme.cloud.provider.openstack import OpenStackProvider -from cfme.fixtures.provider import rhel69_template -from cfme.fixtures.provider import rhel7_minimal -from cfme.fixtures.provider import ubuntu16_template -from cfme.fixtures.provider import win7_template +from cfme.fixtures.templates import rhel69_template +from cfme.fixtures.templates import rhel7_minimal +from cfme.fixtures.templates import ubuntu16_template +from cfme.fixtures.templates import win7_template from cfme.fixtures.v2v_fixtures import cleanup_target from cfme.fixtures.v2v_fixtures import get_migrated_vm from cfme.fixtures.v2v_fixtures import infra_mapping_default_data diff --git a/cfme/tests/v2v/test_v2v_migrations_ui.py b/cfme/tests/v2v/test_v2v_migrations_ui.py index 80d25c688f..f0e4a7215b 100644 --- a/cfme/tests/v2v/test_v2v_migrations_ui.py +++ b/cfme/tests/v2v/test_v2v_migrations_ui.py @@ -6,7 +6,7 @@ from cfme import test_requirements from cfme.cloud.provider.openstack import OpenStackProvider -from cfme.fixtures.provider import rhel7_minimal +from cfme.fixtures.templates import rhel7_minimal from cfme.fixtures.v2v_fixtures import cleanup_target from cfme.fixtures.v2v_fixtures import get_migrated_vm from cfme.fixtures.v2v_fixtures import infra_mapping_default_data diff --git a/cfme/tests/v2v/test_v2v_smoke.py b/cfme/tests/v2v/test_v2v_smoke.py index 7fbf9c7b48..4356dbc4fa 100644 --- a/cfme/tests/v2v/test_v2v_smoke.py +++ b/cfme/tests/v2v/test_v2v_smoke.py @@ -4,7 +4,7 @@ from cfme import test_requirements from cfme.cloud.provider.openstack import OpenStackProvider -from cfme.fixtures.provider import rhel7_minimal +from cfme.fixtures.templates import rhel7_minimal from cfme.fixtures.v2v_fixtures import cleanup_target from cfme.fixtures.v2v_fixtures import get_migrated_vm from cfme.infrastructure.provider.rhevm import RHEVMProvider diff --git a/cfme/utils/pytest_shortcuts.py b/cfme/utils/pytest_shortcuts.py index 97cb649143..cbb4926ca9 100644 --- a/cfme/utils/pytest_shortcuts.py +++ b/cfme/utils/pytest_shortcuts.py @@ -22,22 +22,24 @@ def extract_fixtures_values(item): def trim_items(iterable, keep_index): - return [e[1] for e in enumerate(iterable) if e[0] in keep_index] + return [e[1] + for e in enumerate(iterable) + if e[0] in keep_index] def fixture_filter(metafunc, argnames, argvalues): """Filter fixtures based on fixturenames in the function represented by ``metafunc``""" - # Identify indeces of matches between argnames and fixturenames + # Identify indices of matches between argnames and fixturenames keep_index = [e[0] for e in enumerate(argnames) if e[1] in metafunc.fixturenames] # Keep items at indices in keep_index - def f(l): - if isinstance(l, (list, tuple)) and not isinstance(l, ParameterSet): - return trim_items(l, keep_index) + def f(values): + if isinstance(values, (list, tuple)) and not isinstance(values, ParameterSet): + return trim_items(values, keep_index) else: - parameterset = ParameterSet.extract_from(l) + parameterset = ParameterSet.extract_from(values) return parameterset._replace(values=trim_items(parameterset.values, keep_index)) # Generate the new values diff --git a/cfme/utils/testgen.py b/cfme/utils/testgen.py index 85daed4c9f..5ec3a42687 100644 --- a/cfme/utils/testgen.py +++ b/cfme/utils/testgen.py @@ -256,7 +256,7 @@ def providers(metafunc, filters=None): if 'provider' in metafunc.fixturenames and 'provider' not in argnames: metafunc.function = pytest.mark.uses_testgen()(metafunc.function) argnames.append('provider') - if metafunc.config.getoption('sauce'): + if metafunc.config.getoption('sauce', default=False): break return argnames, argvalues, idlist @@ -327,11 +327,11 @@ def config_managers(metafunc, managers_type=None): data = cfme_data.get('configuration_managers', {}) for cfg_mgr_key in data: - config_mgr = get_config_manager_from_config(cfg_mgr_key) - if managers_type and config_mgr.type != managers_type: - logger.info('Checking config managers type for [{}], skipping mismatched: [{}]' - .format(managers_type, config_mgr)) - continue # skip it + config_mgr = get_config_manager_from_config(cfg_mgr_key, + appliance=None, + mgr_type=managers_type) + if config_mgr is None: + continue # type mismatch argvalues.append([config_mgr]) idlist.append(cfg_mgr_key) return argnames, argvalues, idlist diff --git a/cfme/utils/tests/test_datafile_fixture.py b/cfme/utils/tests/test_datafile_fixture.py index 3b9336aefd..fb541b87a2 100644 --- a/cfme/utils/tests/test_datafile_fixture.py +++ b/cfme/utils/tests/test_datafile_fixture.py @@ -2,26 +2,22 @@ import pytest pytestmark = [ - pytest.mark.nondestructive, - pytest.mark.skip_selenium + pytest.mark.non_destructive, ] def test_datafile_fixture_read(datafile): - myfile = datafile('test_template') - assert myfile.read() == '$replaceme' + with datafile('test_template') as myfile: + assert myfile.read() == '$replaceme' def test_datafile_fixture_read_slash_path(datafile): - myfile = datafile('/cfme/utils/test_datafile_fixture/test_template') - assert myfile.read() == '$replaceme' + with datafile('/cfme/utils/test_datafile_fixture/test_template') as myfile: + assert myfile.read() == '$replaceme' -@pytest.mark.xfail("sys.version_info >= (3,0)", reason="python 3 string type missmatch") def test_datafile_fixture_read_template(datafile): - replacements = { - 'replaceme': 'test!' - } + replacements = {'replaceme': 'test!'} - myfile = datafile('test_template', replacements=replacements) - assert myfile.read() == replacements['replaceme'] + with datafile('test_template', replacements=replacements) as myfile: + assert myfile.read() == replacements['replaceme'] diff --git a/cfme/utils/tests/test_fixtureconf_fixture.py b/cfme/utils/tests/test_fixtureconf_fixture.py index 2f9af3d28a..e11dd4cb50 100644 --- a/cfme/utils/tests/test_fixtureconf_fixture.py +++ b/cfme/utils/tests/test_fixtureconf_fixture.py @@ -1,8 +1,7 @@ import pytest pytestmark = [ - pytest.mark.nondestructive, - pytest.mark.skip_selenium + pytest.mark.non_destructive, ] diff --git a/cfme/utils/tests/test_ipappliance.py b/cfme/utils/tests/test_ipappliance.py index 4b7343ed07..ec8fa7e3a2 100644 --- a/cfme/utils/tests/test_ipappliance.py +++ b/cfme/utils/tests/test_ipappliance.py @@ -19,9 +19,9 @@ def test_ipappliance_from_url(): assert ip_a.hostname == address -@pytest.mark.skipif(pytest.config.getoption('--dummy-appliance'), +@pytest.mark.skipif(lambda request: request.config.getoption('--dummy-appliance', default=False), reason="infra_provider cant support dummy instance") -def test_ipappliance_managed_providers(appliance, infra_provider): +def test_ipappliance_managed_providers(appliance, infra_provider, request): assert infra_provider in appliance.managed_known_providers diff --git a/cfme/utils/tests/test_pytest_shortcuts.py b/cfme/utils/tests/test_pytest_shortcuts.py index a5382174aa..6b58c78689 100644 --- a/cfme/utils/tests/test_pytest_shortcuts.py +++ b/cfme/utils/tests/test_pytest_shortcuts.py @@ -14,8 +14,7 @@ def _values(val): @pytest.mark.parametrize('structure', [ pytest.param(pytest.param(1, 2), id='param'), pytest.param([1, 2], id='list'), - pytest.param([1, 2], id='tuple'), - pytest.param(pytest.mark.skip(reason="fun")((1, 2)), id='legacy_mark') + pytest.param((1, 2), id='tuple'), ]) def test_fixture_filter(structure): argnames, argvalues = fixture_filter(FakeMetaFunc(), ['a', 'b'], [structure]) diff --git a/cfme/utils/tests/test_soft_assert.py b/cfme/utils/tests/test_soft_assert.py index 713cd32595..d5d9f943fd 100644 --- a/cfme/utils/tests/test_soft_assert.py +++ b/cfme/utils/tests/test_soft_assert.py @@ -37,7 +37,11 @@ def test_soft_assert_call_hook(testdir, monkeypatch): result = testdir.runpytest_subprocess('--dummy-appliance') # replace the testfile name in the expected output names, # then check filename and lineno are correct in the failure output - result.stdout.fnmatch_lines([s.format(testfile=pyfile) for s in test_output_match_lines]) + result.stdout.fnmatch_lines( + [s.format(testfile=pyfile) + for s in test_output_match_lines] + ) + testdir.finalize() def test_soft_assert_cm(soft_assert): @@ -79,7 +83,7 @@ def test_soft_assert_helpers(soft_assert): assert False, 'message' with soft_assert.catch_assert(): - assert None + assert False is None # get the caught asserts; there are two of them caught_asserts = soft_assert.caught_asserts() diff --git a/cfme/utils/tests/test_ssh_client.py b/cfme/utils/tests/test_ssh_client.py index 67536a9515..09a8404510 100644 --- a/cfme/utils/tests/test_ssh_client.py +++ b/cfme/utils/tests/test_ssh_client.py @@ -3,8 +3,7 @@ from cfme.utils.appliance import DummyAppliance pytestmark = [ - pytest.mark.nondestructive, - pytest.mark.skip_selenium, + pytest.mark.non_destructive, ] diff --git a/cfme/v2v/infrastructure_mapping.py b/cfme/v2v/infrastructure_mapping.py index 39f69f8856..1554c4188b 100644 --- a/cfme/v2v/infrastructure_mapping.py +++ b/cfme/v2v/infrastructure_mapping.py @@ -11,7 +11,7 @@ from widgetastic_patternfly import Text from widgetastic_patternfly import TextInput -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.exceptions import ItemNotFound from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity diff --git a/cfme/v2v/migration_plans.py b/cfme/v2v/migration_plans.py index 26bb6e3cc3..c929e1c958 100644 --- a/cfme/v2v/migration_plans.py +++ b/cfme/v2v/migration_plans.py @@ -18,8 +18,8 @@ from widgetastic_patternfly import Text from widgetastic_patternfly import TextInput -from cfme.base.login import BaseLoggedInPage from cfme.cloud.provider.openstack import OpenStackProvider +from cfme.common import BaseLoggedInPage from cfme.exceptions import ItemNotFound from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity diff --git a/cfme/v2v/migration_settings.py b/cfme/v2v/migration_settings.py index b717b29db2..430e842ab6 100644 --- a/cfme/v2v/migration_settings.py +++ b/cfme/v2v/migration_settings.py @@ -9,7 +9,7 @@ from widgetastic_patternfly import Input from widgetastic_patternfly import Text -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.infrastructure.provider.rhevm import RHEVMProvider from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000000..9876edf29f --- /dev/null +++ b/conftest.py @@ -0,0 +1,101 @@ +# Markers, Meta, and fixture plugins + +pytest_plugins = [ + # Markers + "cfme.markers.composite", + "cfme.markers.crud", + "cfme.markers.destructive", + "cfme.markers.env", + "cfme.markers.fixtureconf", + "cfme.markers.isolation", + "cfme.markers.manual", + "cfme.markers.marker_filters", + "cfme.markers.meta", + "cfme.markers.polarion", # load before artifactor + "cfme.markers.regression", + "cfme.markers.requires", + "cfme.markers.rhel_tests", + "cfme.markers.rhv", + "cfme.markers.sauce", + "cfme.markers.skipper", + "cfme.markers.smoke", + "cfme.markers.stream_excluder", + "cfme.markers.uses", + "cfme.markers.uncollect", + # Meta + "cfme.metaplugins", + # Store and framework plugins + "cfme.fixtures.pytest_store", # import early + "cfme.test_framework.sprout.plugin", + "cfme.test_framework.appliance_police", + "cfme.test_framework.appliance", + "cfme.test_framework.appliance_log_collector", + "cfme.test_framework.browser_isolation", + "cfme.fixtures.ansible_fixtures", + "cfme.fixtures.ansible_tower", + "cfme.fixtures.appliance", + "cfme.fixtures.appliance_update", + "cfme.fixtures.artifactor_plugin", + "cfme.fixtures.authentication", + "cfme.fixtures.automate", + "cfme.fixtures.base", + "cfme.fixtures.blockers", + "cfme.fixtures.browser", + "cfme.fixtures.bzs", + "cfme.fixtures.candu", + "cfme.fixtures.cfme_data", + "cfme.fixtures.cli", + "cfme.fixtures.datafile", + "cfme.fixtures.depot", + "cfme.fixtures.dev_branch", + "cfme.fixtures.disable_forgery_protection", + "cfme.fixtures.embedded_ansible", + "cfme.fixtures.events", + "cfme.fixtures.fixtureconf", + "cfme.fixtures.generic_object", + "cfme.fixtures.has_persistent_volume", + "cfme.fixtures.log", + "cfme.fixtures.maximized", + "cfme.fixtures.merkyl", + "cfme.fixtures.model_collections", + "cfme.fixtures.multi_region", + "cfme.fixtures.nelson", + "cfme.fixtures.networks", + "cfme.fixtures.nuage", + "cfme.fixtures.page_screenshots", + "cfme.fixtures.parallelizer", + "cfme.fixtures.perf", + "cfme.fixtures.physical_switch", + "cfme.fixtures.portset", + "cfme.fixtures.prov_filter", + "cfme.fixtures.provider", + "cfme.fixtures.pxe", + "cfme.fixtures.qa_contact", + "cfme.fixtures.randomness", + "cfme.fixtures.rbac", + "cfme.fixtures.rdb", + "cfme.fixtures.sauce", + "cfme.fixtures.screenshots", + "cfme.fixtures.service_fixtures", + "cfme.fixtures.single_appliance_sprout", + "cfme.fixtures.skip_not_implemented", + "cfme.fixtures.smtp", + "cfme.fixtures.soft_assert", + "cfme.fixtures.ssh_client", + "cfme.fixtures.tag", + "cfme.fixtures.tccheck", + "cfme.fixtures.templateloader", + "cfme.fixtures.templates", + "cfme.fixtures.terminalreporter", + "cfme.fixtures.ui_coverage", + "cfme.fixtures.update_tests", + "cfme.fixtures.v2v_fixtures", + "cfme.fixtures.version_info", + "cfme.fixtures.video", + "cfme.fixtures.virtual_machine", + "cfme.fixtures.vm", + "cfme.fixtures.vm_console", + "cfme.fixtures.vporizer", + "cfme.fixtures.widgets", + "cfme.fixtures.xunit_tools", +] diff --git a/docs/guides/tipsntricks.rst b/docs/guides/tipsntricks.rst index 4f080232c3..01bb4b5a58 100644 --- a/docs/guides/tipsntricks.rst +++ b/docs/guides/tipsntricks.rst @@ -149,7 +149,7 @@ the cache somehow. You need to call an appropriate method on the appliance objec pytest store ------------ -The pytest store provides access to common pytest data structures and instances that may not be readily available elsewhere. It can be found in :py:mod:`fixtures.pytest_store`, and during a test run is exposed on the pytest module in the store namespace as ``pytest.store``. +The pytest store provides access to common pytest data structures and instances that may not be readily available elsewhere. It can be found in :py:mod:`fixtures.pytest_store`, and has to be used directly via :py:mod:`fixtures.pytest_store.store` singleton instance. Test generation (testgen) ------------------------- diff --git a/entry_points.txt b/entry_points.txt index b82e4e705f..d5b26d9c61 100644 --- a/entry_points.txt +++ b/entry_points.txt @@ -180,4 +180,4 @@ gce = cfme.cloud.instance.gce:GCEInstance openstack = cfme.cloud.instance.openstack:OpenStackInstance [pytest11] -cfme = cfme.test_framework.pytest_plugin +cfme = conftest diff --git a/requirements/constraints.txt b/requirements/constraints.txt index f03806be55..1dba72269a 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -1,4 +1,3 @@ azure-storage-common==1.4.0 pdfminer.six==20181108 -pytest<=3.4.1 websocket-client<=0.40.2,>=0.32.0 diff --git a/requirements/frozen.txt b/requirements/frozen.txt index e0da18c8d5..64293a2d28 100644 --- a/requirements/frozen.txt +++ b/requirements/frozen.txt @@ -7,6 +7,7 @@ apipkg==1.5 appdirs==1.4.3 asn1crypto==0.24.0 aspy.yaml==1.3.0 +atomicwrites==1.3.0 attrs==19.1.0 azure==4.0.0 azure-applicationinsights==0.1.0 @@ -226,7 +227,7 @@ pickleshare==0.7.5 pika==1.1.0 Pillow==6.2.1 pip-module-scanner==0.6 -pluggy==0.6.0 +pluggy==0.13.0 polarion-docstrings==0.22.0 polarion-tools-common==1.0.3 pre-commit==1.18.3 @@ -255,7 +256,7 @@ pyOpenSSL==19.0.0 pyparsing==2.4.0 pyperclip==1.7.0 pytesseract==0.3.0 -pytest==3.4.1 +pytest==5.2.1 pytest-polarion-collect==0.21.0 python-box==3.2.4 python-bugzilla==2.3.0 diff --git a/requirements/frozen_docs.py3.txt b/requirements/frozen_docs.py3.txt index 94077f5c99..13e9205738 100644 --- a/requirements/frozen_docs.py3.txt +++ b/requirements/frozen_docs.py3.txt @@ -272,7 +272,7 @@ wcwidth==0.1.7 websocket-client==0.40.0 Werkzeug==0.16.0 widgetastic.core==0.39 -widgetastic.patternfly==1.0.0 +widgetastic.patternfly==1.1.0 widgetsnbextension==3.4.2 wrapt==1.11.1 xmltodict==0.12.0 diff --git a/requirements/template_non_imported.txt b/requirements/template_non_imported.txt index 175b630df5..df992ff7a6 100644 --- a/requirements/template_non_imported.txt +++ b/requirements/template_non_imported.txt @@ -10,3 +10,4 @@ polarion-docstrings polarion-tools-common pre-commit pytest-polarion-collect +pytest-report-parameters diff --git a/requirements/template_scanned_imports.txt b/requirements/template_scanned_imports.txt index 808f97bc9d..9b2bd8596b 100644 --- a/requirements/template_scanned_imports.txt +++ b/requirements/template_scanned_imports.txt @@ -41,6 +41,7 @@ pytest python-bugzilla python-keycloak pytz +pyzmq requests riggerlib scp