#6 Modernization and CI
Merged 3 years ago by adamwill. Opened 3 years ago by adamwill.

file modified
+2
@@ -1,2 +1,4 @@ 

  [FORMAT]

  max-line-length=100

+ [MESSAGES CONTROL]

+ disable=bad-continuation

file added
+8
@@ -0,0 +1,8 @@ 

+ - job:

+     name: wikitcms-tox

+     run: ci/tox.yaml

+     nodeset: fedora-31-vm

+ - project:

+     check:

+       jobs:

+         - wikitcms-tox

file modified
+4 -2
@@ -1,2 +1,4 @@ 

- include *.requires

- include README.md

+ exclude .gitignore

+ exclude MANIFEST.in

+ exclude .zuul.yaml

+ recursive-exclude ci *

file modified
+4 -4
@@ -8,11 +8,11 @@ 

  

  ## Installation and use

  

- python-wikitcms is packaged in the official Fedora and EPEL 7+ repositories: to install on Fedora run `dnf install python-wikitcms`, on RHEL / CentOS with EPEL enabled, run `yum install python-wikitcms`. You may need to enable the *updates-testing* repository to get the latest version. To install on other distributions, you can run `python setup.py install`.

+ python-wikitcms is packaged in the official Fedora repositories: to install on Fedora run `dnf install python-wikitcms`. You may need to enable the *updates-testing* repository to get the latest version. To install on other distributions, you can run `python3 setup.py install`.

  

- You can visit [the python-wikitcms project page on Pagure][7], and clone with `git clone https://pagure.io/fedora-qa/python-wikitcms.git`. Tarballs are available [from PyPI][8].

+ You can visit [the python-wikitcms project page on Pagure][7], and clone with `git clone https://pagure.io/fedora-qa/python-wikitcms.git`. Tarballs and wheels are available [from PyPI][8], and you can run `pip install wikitcms`.

  

- You can also use the library directly from the git checkout if you place your code in the top-level directory, and you can copy or symlink the `wikitcms` directory into other source trees to conveniently use the latest code for development or testing purposes.

+ You can also use the library directly from the `src/` directory or add it to the Python import path, and you can copy or symlink the `wikitcms` directory into other source trees to conveniently use the latest code for development or testing purposes.

  

  ## Bugs, pull requests etc.

  
@@ -96,7 +96,7 @@ 

  

  ## Credits

  

- * [Mike Ruckman][11] (roshi) has been kind and patient in providing review and advice throughout python-wikitcms' development. Of course, all errors are my own!

+ * [Mike Ruckman][11] (roshi) was kind and patient in providing review and advice throughout python-wikitcms' early development.

  * [Patrick Uiterwijk][12] kindly provided the code to support OpenID Connect authentication.

  

  ## License

ci.requires tox.requires
file renamed
+2 -1
@@ -1,4 +1,5 @@ 

+ black

  coverage

  diff-cover

  pylint

- pytest-cov

+ toml

file added
+14
@@ -0,0 +1,14 @@ 

+ - hosts: all

+   tasks:

+     - name: Ensure tox is installed

+       include_role:

+         name: ensure-tox

+     - name: Install all Python versions to test

+       package:

+         name: ['python36', 'python37', 'python38']

+         state: present

+       become: yes

+     - name: Run tox

+       command: tox

+       args:

+         chdir: '{{ zuul.project.src_dir }}'

file added
+18
@@ -0,0 +1,18 @@ 

+ [build-system]

+ requires = ["setuptools>=40.6.0", "setuptools-git", "wheel"]

+ build-backend = "setuptools.build_meta"

+ 

+ [tool.coverage.run]

+ parallel = true

+ branch = true

+ source = ["wikitcms", "src/wikitcms"]

+ 

+ [tool.coverage.paths]

+ source = ["src", ".tox/*/site-packages"]

+ 

+ [tool.coverage.report]

+ show_missing = true

+ 

+ [tool.black]

+ # don't @ me, Hynek

+ line-length = 100

file modified
+3 -7
@@ -2,10 +2,7 @@ 

  

  baddeps=""

  # check deps

- rpm -qi python3-setuptools > /dev/null 2>&1 || baddeps="python3-setuptools"

- rpm -qi python3-setuptools_git > /dev/null 2>&1 || baddeps="${baddeps} python3-setuptools_git"

- rpm -qi python3-pypandoc > /dev/null 2>&1 || baddeps="${baddeps} python3-pypandoc"

- rpm -qi python3-twine > /dev/null 2>&1 || rpm -qi twine > /dev/null 2>&1 || baddeps="${baddeps} python3-twine (F27 and earlier) or twine (F28 and later)"

+ python3 -m pep517.__init__ || baddeps="python3-pep517"

  if [ -n "${baddeps}" ]; then

      echo "${baddeps} must be installed!"

      exit 1
@@ -25,6 +22,5 @@ 

  git push

  git tag -a -m "Release ${version}" ${version}

  git push origin ${version}

- python3 ./setup.py sdist --formats=tar

- gzip dist/${name}-${version}.tar

- twine upload dist/${name}-${version}.tar.gz

+ python3 -m pep517.build .

+ twine upload -r pypi dist/${name}-${version}*

file modified
+9 -29
@@ -17,32 +17,14 @@ 

  

  """Setuptools script."""

  

- import sys

- from setuptools import setup, find_packages

- from setuptools.command.test import test as TestCommand

+ from os import path

+ from setuptools import setup

  

+ HERE = path.abspath(path.dirname(__file__))

  

- class PyTest(TestCommand):

-     user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]

- 

-     def initialize_options(self):

-         TestCommand.initialize_options(self)

-         self.pytest_args = []

- 

-     def run_tests(self):

-         #import here, cause outside the eggs aren't loaded

-         import pytest

-         errno = pytest.main(self.pytest_args)

-         sys.exit(errno)

- 

- # From: https://github.com/pypa/pypi-legacy/issues/148

- # Produce rst-formatted long_description if pypandoc is available (to

- # look nice on pypi), otherwise just use the Markdown-formatted one

- try:

-     import pypandoc

-     longdesc = pypandoc.convert('README.md', 'rst')

- except ImportError:

-     longdesc = open('README.md').read()

+ # Get the long description from the README file

+ with open(path.join(HERE, 'README.md'), encoding='utf-8') as f:

+     LONGDESC = f.read()

  

  setup(

      name="wikitcms",
@@ -54,13 +36,11 @@ 

      keywords="fedora qa mediawiki validation",

      url="https://pagure.io/fedora-qa/python-wikitcms",

      packages=["wikitcms"],

-     setup_requires=[

-         'setuptools_git',

-     ],

+     package_dir={"": "src"},

      install_requires=open('install.requires').read().splitlines(),

      tests_require=open('tests.requires').read().splitlines(),

-     cmdclass={'test': PyTest},

-     long_description=longdesc,

+     long_description=LONGDESC,

+     long_description_content_type='text/markdown',

      classifiers=[

          "Development Status :: 5 - Production/Stable",

          "Topic :: Utilities",

src/wikitcms/__init__.py wikitcms/__init__.py
file renamed
-3
@@ -19,9 +19,6 @@ 

  wiki pages.

  """

  

- from __future__ import unicode_literals

- from __future__ import print_function

- 

  __version__ = "2.5.2"

  

  # vim: set textwidth=100 ts=8 et sw=4:

src/wikitcms/event.py wikitcms/event.py
file renamed
+95 -75
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2014 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -17,28 +17,28 @@ 

  #

  # Author: Adam Williamson <awilliam@redhat.com>

  

- """Classes that describe test events."""

+ # I disagree, pylint! Shut up!

+ # pylint: disable=too-many-arguments, too-many-instance-attributes

  

- from __future__ import unicode_literals

- from __future__ import print_function

+ """Classes that describe test events."""

  

  import abc

  import logging

  

+ from cached_property import cached_property

  import fedfind.helpers

  import fedfind.release

  import mwclient.errors

- from cached_property import cached_property

  

- from . import listing

- from . import page

- from . import helpers

- from .exceptions import FedfindNotFoundError

+ from wikitcms.exceptions import FedfindNotFoundError

+ import wikitcms.helpers

+ import wikitcms.listing

+ import wikitcms.page

  

  logger = logging.getLogger(__name__)

  

  

- class ValidationEvent(object):

+ class ValidationEvent:

      """A parent class for different types of release validation event.

      site must be an instance of wikitcms.Wiki, already with

      appropriate access rights for any actions to be performed (i.e.
@@ -48,46 +48,41 @@ 

      Fedora-Modular compose, with 'Modular' in the page names, category

      names and so on.

      """

+ 

      __metaclass__ = abc.ABCMeta

  

-     def __init__(self, site, release, milestone='', compose='', modular=False):

+     def __init__(self, site, release, milestone="", compose="", modular=False):

          self.site = site

          self.release = release

          self.milestone = str(milestone)

          try:

-             self.compose = fedfind.helpers.date_check(

-                 compose, fail_raise=True, out='str')

+             self.compose = fedfind.helpers.date_check(compose, fail_raise=True, out="str")

          except ValueError:

              self.compose = str(compose)

          self.modular = modular

-         self.version = "{0} {1} {2}".format(

-             self.release, self.milestone, self.compose)

+         self.version = f"{self.release} {self.milestone} {self.compose}"

          # Sorting helpers. sortname is a string, sorttuple is a

          # 4-tuple. sorttuple is more reliable. See the function docs.

-         self.sortname = helpers.fedora_release_sort(self.version)

-         self.sorttuple = helpers.triplet_sort(

-             self.release, self.milestone, self.compose)

+         self.sortname = wikitcms.helpers.fedora_release_sort(self.version)

+         self.sorttuple = wikitcms.helpers.triplet_sort(self.release, self.milestone, self.compose)

  

      @abc.abstractproperty

      def _current_content(self):

          """The content for the CurrentFedoraCompose template for

          this test event.

          """

-         pass

  

      @abc.abstractproperty

      def _pagetype(self):

          """The ValidationPage class to be used for this event's pages

          (for use by valid_pages).

          """

-         pass

  

      @abc.abstractproperty

      def category_page(self):

          """The category page for this event. Is a property because

          page instantiation requires a remote trip.

          """

-         pass

  

      @property

      def result_pages(self):
@@ -98,30 +93,29 @@ 

          _dist = "Fedora"

          if self.modular:

              _dist = "Fedora Modular"

-         pages = self.site.allresults(

-             prefix="{0} {1} ".format(_dist, self.version))

-         return [p for p in pages if isinstance(p, page.ValidationPage)]

+         pages = self.site.allresults(prefix=f"{_dist} {self.version} ")

+         return [p for p in pages if isinstance(p, wikitcms.page.ValidationPage)]

  

      @property

      def download_page(self):

          """The DownloadPage for this event. Is a property because page

          instantiation requires a remote trip.

          """

-         return page.DownloadPage(self.site, self, modular=self.modular)

+         return wikitcms.page.DownloadPage(self.site, self, modular=self.modular)

  

      @property

      def ami_page(self):

          """The AMIPage for this event. Is a property because page

          instantiation requires a remote trip.

          """

-         return page.AMIPage(self.site, self, modular=self.modular)

+         return wikitcms.page.AMIPage(self.site, self, modular=self.modular)

  

      @property

      def parent_category_page(self):

          """The parent category page for this event. Is a property for

          the same reason as download_page.

          """

-         return listing.ValidationCategory(self.site, self.release, modular=self.modular)

+         return wikitcms.listing.ValidationCategory(self.site, self.release, modular=self.modular)

  

      @property

      def valid_pages(self):
@@ -133,10 +127,17 @@ 

              types = self.site.modular_testtypes

          else:

              types = self.site.testtypes

-         return [self._pagetype(self.site, self.release, typ,

-                                milestone=self.milestone,

-                                compose=self.compose, modular=self.modular)

-                 for typ in types]

+         return [

+             self._pagetype(

+                 self.site,

+                 self.release,

+                 typ,

+                 milestone=self.milestone,

+                 compose=self.compose,

+                 modular=self.modular,

+             )

+             for typ in types

+         ]

  

      @property

      def summary_page(self):
@@ -144,7 +145,7 @@ 

          page. Very simple property, but not set in __init__ as the

          summary page object does (slow) wiki roundtrips in  __init__.

          """

-         return page.SummaryPage(self.site, self, modular=self.modular)

+         return wikitcms.page.SummaryPage(self.site, self, modular=self.modular)

  

      @cached_property

      def ff_release(self):
@@ -156,10 +157,9 @@ 

          if self.modular:

              dist = "Fedora-Modular"

          try:

-             return fedfind.release.get_release(release=self.release,

-                                                milestone=self.milestone,

-                                                compose=self.compose,

-                                                dist=dist)

+             return fedfind.release.get_release(

+                 release=self.release, milestone=self.milestone, compose=self.compose, dist=dist

+             )

          except ValueError as err:

              try:

                  if self._cid:
@@ -178,9 +178,8 @@ 

          rel = self.ff_release

          if rel.all_images:

              return rel

-         else:

-             raise FedfindNotFoundError("Could not find fedfind release with images for event"

-                                        "{0}".format(self.version))

+         err = f"Could not find fedfind release with images for event {self.version}"

+         raise FedfindNotFoundError(err)

  

      def update_current(self):

          """Make the CurrentFedoraCompose template on the wiki point to
@@ -193,9 +192,9 @@ 

          content += self._current_content

          content += "}}</onlyinclude>\n[[Category: Fedora Templates]]"

          if self.modular:

-             curr = self.site.pages['Template:CurrentFedoraModularCompose']

+             curr = self.site.pages["Template:CurrentFedoraModularCompose"]

          else:

-             curr = self.site.pages['Template:CurrentFedoraCompose']

+             curr = self.site.pages["Template:CurrentFedoraCompose"]

          curr.save(content, "relval: update to current event", createonly=None)

  

      def create(self, testtypes=None, force=False, current=True, check=False):
@@ -220,7 +219,7 @@ 

              createonly = None

          pages = self.valid_pages

          if testtypes:

-             logger.debug("Restricting to testtypes %s", ' '.join(testtypes))

+             logger.debug("Restricting to testtypes %s", " ".join(testtypes))

              pages = [pag for pag in pages if pag.testtype in testtypes]

          if not pages:

              raise ValueError("No result pages to create! Wrong test type?")
@@ -235,15 +234,23 @@ 

          # * you used get_validation_event and passed it a cid

          # Otherwise, the event will be created, but the download page

          # will not.

-         pages.extend((self.summary_page, self.download_page, self.ami_page,

-                       self.category_page, self.parent_category_page))

+         pages.extend(

+             (

+                 self.summary_page,

+                 self.download_page,

+                 self.ami_page,

+                 self.category_page,

+                 self.parent_category_page,

+             )

+         )

  

          def _handle_existing(err):

              """We need this in two places, so."""

-             if err.args[0] == 'articleexists':

+             if err.args[0] == "articleexists":

                  # no problem, just move on.

-                 logger.info("Page already exists, and forced write was not "

-                             "requested! Not writing.")

+                 logger.info(

+                     "Page already exists, and forced write was not " "requested! Not writing."

+                 )

              else:

                  raise err

  
@@ -257,12 +264,14 @@ 

              except FedfindNotFoundError:

                  # this happens if download page couldn't be created

                  # because fedfind release couldn't be found

-                 logger.warning("Could not create download page for event %s as fedfind release "

-                                "was not found!")

+                 logger.warning(

+                     "Could not create download page for event %s as fedfind release "

+                     "was not found!"

+                 )

  

              # stage 2 - update current. this is split so if we hit

              # 'page already exists', we don't skip update_current

-             if current and hasattr(pag, 'update_current'):

+             if current and hasattr(pag, "update_current"):

                  logger.info("Pointing Current redirect to above page")

                  pag.update_current()

  
@@ -279,8 +288,13 @@ 

          """Return the ValidationEvent object for a given ValidationPage

          object.

          """

-         return cls(pageobj.site, pageobj.release, pageobj.milestone,

-                    pageobj.compose, modular=pageobj.modular)

+         return cls(

+             pageobj.site,

+             pageobj.release,

+             pageobj.milestone,

+             pageobj.compose,

+             modular=pageobj.modular,

+         )

  

  

  class ComposeEvent(ValidationEvent):
@@ -288,38 +302,41 @@ 

      the testing for a particular nightly, test compose or release

      candidate build.

      """

-     def __init__(self, site, release, milestone, compose, modular=False, cid=''):

+ 

+     def __init__(self, site, release, milestone, compose, modular=False, cid=""):

          super(ComposeEvent, self).__init__(

-             site, release, milestone=milestone, compose=compose, modular=modular)

+             site, release, milestone=milestone, compose=compose, modular=modular

+         )

          # this is a little hint that gets set via get_validation_event

          # when getting a page or event by cid; it helps us find the

          # fedfind release for the event if the compose is not yet in

          # PDC or synced to stage

          self._cid = cid

-         self.shortver = "{0} {1}".format(self.milestone, self.compose)

+         self.shortver = f"{self.milestone} {self.compose}"

  

      @property

      def category_page(self):

          """The category page for this event. Is a property because

          page instantiation requires a remote trip.

          """

-         return listing.ValidationCategory(

-             self.site, self.release, self.milestone, modular=self.modular)

+         return wikitcms.listing.ValidationCategory(

+             self.site, self.release, self.milestone, modular=self.modular

+         )

  

      @property

      def _current_content(self):

          """The content for the CurrentFedoraCompose template for

          this test event.

          """

-         tmpl = ("| full = {0}\n| release = {1}\n| milestone = {2}\n"

-                 "| compose = {3}\n| date =\n")

-         return tmpl.format(

-             self.version, self.release, self.milestone, self.compose)

+         return (

+             f"| full = {self.version}\n| release = {self.release}\n"

+             f"| milestone = {self.milestone}\n| compose = {self.compose}\n| date =\n"

+         )

  

      @property

      def _pagetype(self):

          """For a ComposeEvent, obviously, ComposePage."""

-         return page.ComposePage

+         return wikitcms.page.ComposePage

  

      @cached_property

      def creation_date(self):
@@ -354,9 +371,8 @@ 

              rel = fedfind.release.get_release(cid=self._cid)

          if rel.all_images:

              return rel

-         else:

-             raise FedfindNotFoundError("Could not find fedfind release with images for event "

-                                        "{0}".format(self.version))

+         err = f"Could not find fedfind release with images for event {self.version}"

+         raise FedfindNotFoundError(err)

  

  

  class NightlyEvent(ValidationEvent):
@@ -372,33 +388,37 @@ 

      we believe the testing of that Rawhide nightly constitutes a part

      of the release validation testing for that future release.

      """

+ 

      def __init__(self, site, release, milestone, compose, modular=False):

          super(NightlyEvent, self).__init__(

-             site, release, milestone=milestone, compose=compose, modular=modular)

+             site, release, milestone=milestone, compose=compose, modular=modular

+         )

          self.shortver = self.compose

-         self.creation_date = compose.split('.')[0]

+         self.creation_date = compose.split(".")[0]

  

      @property

      def category_page(self):

          """The category page for this event. Is a property because

          page instantiation requires a remote trip.

          """

-         return listing.ValidationCategory(

-             self.site, self.release, nightly=True, modular=self.modular)

+         return wikitcms.listing.ValidationCategory(

+             self.site, self.release, nightly=True, modular=self.modular

+         )

  

      @property

      def _current_content(self):

          """The content for the CurrentFedoraCompose template for

          this test event.

          """

-         tmpl = ("| full = {0}\n| release = {1}\n| milestone = {2}\n"

-                 "| compose =\n| date = {3}\n")

-         return tmpl.format(

-             self.version, self.release, self.milestone, self.compose)

+         return (

+             f"| full = {self.version}\n| release = {self.release}\n"

+             f"| milestone = {self.milestone}\n| compose =\n| date = {self.compose}\n"

+         )

  

      @property

      def _pagetype(self):

          """For a NightlyEvent, obviously, NightlyPage."""

-         return page.NightlyPage

+         return wikitcms.page.NightlyPage

+ 

  

  # vim: set textwidth=100 ts=8 et sw=4:

src/wikitcms/exceptions.py wikitcms/exceptions.py
file renamed
+3 -9
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2015 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -19,23 +19,17 @@ 

  

  """Defines custom exceptions used by wikitcms."""

  

- from __future__ import unicode_literals

- from __future__ import print_function

- 

  

  class NoPageError(Exception):

      """Page does not exist."""

-     pass

  

  

  class NotFoundError(Exception):

      """Requested thing wasn't found."""

-     pass

  

  

  class TooManyError(Exception):

      """Found too many of the thing you asked for."""

-     pass

  

  

  # this inherits from ValueError as the things that raise this may
@@ -44,6 +38,6 @@ 

      """Couldn't find a fedfind release (probably the fedfind release

      that matches an event).

      """

-     pass

+ 

  

  # vim: set textwidth=100 ts=8 et sw=4:

src/wikitcms/helpers.py wikitcms/helpers.py
file renamed
+59 -57
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2014 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -20,46 +20,42 @@ 

  """This file contains helper functions that don't strictly belong in

  any particular class or even in another file but outside of a class."""

  

- from __future__ import unicode_literals

- from __future__ import print_function

- 

- import os

  import re

- from collections import OrderedDict

  from decimal import Decimal

  

  import fedfind.helpers

  import fedfind.release

  

  MILESTONE_PAIRS = (

-     ('Rawhide', '100'),

+     ("Rawhide", "100"),

      # We called Branched nightly pages 'Nightly' in F21 cycle.

-     ('Nightly', '149'),

-     ('Branched', '150'),

-     ('Pre-Alpha', '175'),

-     ('Alpha', '200'),

-     ('Pre-Beta', '375'),

-     ('Beta', '400'),

-     ('Preview', '600'),

-     ('Pre-Final', '775'),

-     ('Final', '800'),

-     ('Postrelease', '900'),

+     ("Nightly", "149"),

+     ("Branched", "150"),

+     ("Pre-Alpha", "175"),

+     ("Alpha", "200"),

+     ("Pre-Beta", "375"),

+     ("Beta", "400"),

+     ("Preview", "600"),

+     ("Pre-Final", "775"),

+     ("Final", "800"),

+     ("Postrelease", "900"),

  )

  

  COMPOSE_PAIRS = (

      # Some F12 crap

-     ('PreBeta', '100'),

-     ('TC', '200'),

+     ("PreBeta", "100"),

+     ("TC", "200"),

      # and this.

-     ('Pre-RC', '300'),

+     ("Pre-RC", "300"),

      # The extra digit here is a dumb way to make sure RC1 sorts

      # later than TC10 - otherwise you get 20010 vs. 6001, and 20010

      # wins. It might be better to treat the 'TC' / 'RC' as a separate

      # element in triplet_sort, but that's a bit harder and makes its

      # name a lie...

-     ('RC', '6000'),

+     ("RC", "6000"),

  )

  

+ 

  def fedora_release_sort(string):

      """Fed a string that looks something like a Fedora pre-release /

      compose version, this will output a modified version of the string
@@ -78,6 +74,7 @@ 

          string = string.replace(orig, repl)

      return string

  

+ 

  def triplet_sort(release, milestone, compose):

      """Just like fedora_release_sort, but requires you to pass the

      now-'standard' release, milestone, compose triplet of inputs.
@@ -94,11 +91,11 @@ 

          milestone = milestone.replace(orig, repl)

          if not milestone:

              # ('23', 'Final', '') == ('23', '', '')

-             milestone = '800'

+             milestone = "800"

      for (orig, repl) in COMPOSE_PAIRS:

          compose = compose.replace(orig, repl)

          if not compose:

-             compose = '999'

+             compose = "999"

      # We want to get numerical sorts if we possibly can, so e.g.

      # TC10 (becomes 20010) > TC9 (becomes 2009). But just in case

      # we get passed a character we don't substitute to a digit,
@@ -114,35 +111,36 @@ 

          # this is a bit magic but handles "TC1.1" (old-school),

          # "1.1" (new-school candidates), and "20160314.n.0" (new-

          # school nightlies)

-         (comp, respin) = (compose.split('.')[0], compose.split('.')[-1])

+         (comp, respin) = (compose.split(".")[0], compose.split(".")[-1])

          if comp.isdigit() and respin.isdigit():

-             compose = Decimal('.'.join((comp, respin)))

+             compose = Decimal(".".join((comp, respin)))

      return (release, milestone, compose)

  

+ 

  def triplet_unsort(release, milestone, compose):

      """Reverse of triplet_sort."""

-     (release, milestone, compose) = (str(release), str(milestone),

-                                      str(compose))

+     (release, milestone, compose) = (str(release), str(milestone), str(compose))

      for (repl, orig) in MILESTONE_PAIRS:

          milestone = milestone.replace(orig, repl)

-         if milestone == '800':

-             milestone = 'Final'

+         if milestone == "800":

+             milestone = "Final"

      # We don't want to do the replace here if the compose is a date,

      # because any of the values might happen to be in the date. This

      # is a horrible hack that should be OK (until 2100, at least).

      if fedfind.helpers.date_check(compose, fail_raise=False):

          pass

-     elif fedfind.helpers.date_check(compose.split('.')[0], fail_raise=False):

+     elif fedfind.helpers.date_check(compose.split(".")[0], fail_raise=False):

          # this is slightly magic but should do for now

-         (comp, respin) = compose.split('.')

-         compose = "{0}.n.{1}".format(comp, respin)

+         (comp, respin) = compose.split(".")

+         compose = f"{comp}.n.{respin}"

      else:

          for (repl, orig) in COMPOSE_PAIRS:

              compose = compose.replace(orig, repl)

-             if compose == '999':

-                 compose = ''

+             if compose == "999":

+                 compose = ""

      return (release, milestone, compose)

  

+ 

  def rreplace(string, old, new, occurrence):

      """A version of the str.replace() method which works from the right.

      Taken from https://stackoverflow.com/questions/2556108/
@@ -150,11 +148,13 @@ 

      elems = string.rsplit(old, occurrence)

      return new.join(elems)

  

+ 

  def normalize(text):

      """Lower case and replace ' ' with '_' so I don't have to

      keep retyping it.

      """

-     return text.lower().replace(' ', '_')

+     return text.lower().replace(" ", "_")

+ 

  

  def find_bugs(text):

      """Find RH bug references in a given chunk of text. More than one
@@ -165,19 +165,18 @@ 

      multiple times in the text will only appear once in the output).

      """

      bugs = set()

-     bzpatt = re.compile(r'({{bz *\| *|'

-                         r'\[\[rhbug *: *|'

-                         r'bugzilla\.redhat\.com/show_bug\.cgi\?id=)'

-                         r'(\d{6,7})')

+     bzpatt = re.compile(

+         r"({{bz *\| *|" r"\[\[rhbug *: *|" r"bugzilla\.redhat\.com/show_bug\.cgi\?id=)" r"(\d{6,7})"

+     )

      matches = bzpatt.finditer(text)

      for match in matches:

          bugs.add(match.group(2))

      # Filter out bug IDs usually used as examples

-     for bug in ('12345', '123456', '54321', '654321', '234567', '1234567',

-                 '7654321'):

+     for bug in ("12345", "123456", "54321", "654321", "234567", "1234567", "7654321"):

          bugs.discard(bug)

      return bugs

  

+ 

  def cid_to_event(cid):

      """Given a Pungi 4 compose ID, figure out the appropriate wikitcms

      (release, milestone, compose) triplet. Guesses the appropriate
@@ -186,32 +185,34 @@ 

      Event instances.

      """

      parsed = fedfind.helpers.parse_cid(cid, dic=True)

-     dist = parsed['short']

-     release = parsed['version'].lower()

-     vertype = parsed['version_type']

-     date = parsed['date']

-     typ = parsed['compose_type']

-     respin = parsed['respin']

+     dist = parsed["short"]

+     release = parsed["version"].lower()

+     vertype = parsed["version_type"]

+     date = parsed["date"]

+     typ = parsed["compose_type"]

+     respin = parsed["respin"]

      if dist not in ("Fedora", "Fedora-Modular"):

          # we only have validation events for Fedora and Modular

          # composes ATM - not e.g. FedoraRespin or Fedora-Atomic ones

-         raise ValueError("No validation events for {0} composes!".format(dist))

-     if vertype != 'ga':

+         raise ValueError(f"No validation events for {dist} composes!")

+     if vertype != "ga":

          # this ensures we don't create events for updates/u-t composes

          raise ValueError("No validation events for updates composes!")

      if typ == "production":

          # we need to get the label and parse that

          ffrel = fedfind.release.get_release(cid=cid)

          if not ffrel.label:

-             raise ValueError("{0} looks like a production compose, but found "

-                              "no label! Cannot determine event.".format(cid))

-         (milestone, compose) = ffrel.label.rsplit('-', 1)

+             raise ValueError(

+                 f"{cid} looks like a production compose, but found "

+                 "no label! Cannot determine event."

+             )

+         (milestone, compose) = ffrel.label.rsplit("-", 1)

          return (dist, release, milestone, compose)

  

-     # FIXME: we have no idea yet what to do for 'test' composes

      if not typ == "nightly":

-         raise ValueError(

-             "cid_to_event(): cannot guess event for 'test' compose yet!")

+         # we don't have validation events for any compose types other

+         # than 'production' and 'nightly'

+         raise ValueError("cid_to_event(): cannot guess event for 'test' compose")

  

      # nightlies

      if release == "rawhide":
@@ -223,7 +224,8 @@ 

          milestone = "Rawhide"

      else:

          milestone = "Branched"

-     compose = "{0}.n.{1}".format(date, respin)

+     compose = f"{date}.n.{respin}"

      return (dist, release, milestone, compose)

  

+ 

  # vim: set textwidth=100 ts=8 et sw=4:

src/wikitcms/listing.py wikitcms/listing.py
file renamed
+204 -163
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2014 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -24,14 +24,11 @@ 

  otherwise.

  """

  

- from __future__ import unicode_literals

- from __future__ import print_function

- 

  import re

  

- from mwclient import listing as mwl

+ import mwclient.listing

  

- from . import page as pg

+ import wikitcms.page

  

  # exceptions: the wikitcms getter raises these when it fails rather than

  # just returning None, so the generators can use try/except blocks to
@@ -39,15 +36,18 @@ 

  # just in case) where they're being used on something other than a list

  # of pages.

  

+ 

  class NoPageWarning(Exception):

      """Exception raised when the tcmswiki getter can't find a matching

      page. Not really an error, should always be handled.

      """

+ 

      def __init__(self, page):

+         super(NoPageWarning, self).__init__()

          self.page = page

  

      def __str__(self):

-         return "Could not produce a wikitcms page for: {0}".format(self.page)

+         return f"Could not produce a wikitcms page for: {self.page}"

  

  

  class PageCheckWarning(Exception):
@@ -56,23 +56,28 @@ 

      attributes doesn't match the page name the getter was given. Should

      usually be handled (and an mwclient Page instance returned instead).

      """

+ 

      def __init__(self, frompage, topage):

+         super(PageCheckWarning, self).__init__()

          self.frompage = frompage

          self.topage = topage

  

      def __str__(self):

-         return ("Expected page name {0} does not match source "

-                 "page name {1}".format(self.frompage, self.topage))

+         return f"Expected page name {self.frompage} does not match source page name {self.topage}"

+ 

  

+ def _check_page(name, page):

+     """A convenience function for _get_tcms sanity check."""

+     if page.checkname == name:

+         return page

+     raise PageCheckWarning(page.checkname, name)

  

- class TcmsGeneratorList(mwl.GeneratorList):

+ 

+ class TcmsGeneratorList(mwclient.listing.GeneratorList):

      """A GeneratorList which returns wikitcms page (and category etc.)

      instances when appropriate. _get_tcms is implemented as a separate

      function so TcmsPageList can use the discovery logic.

      """

-     def __init__(self, site, list_name, prefix, *args, **kwargs):

-         super(TcmsGeneratorList, self).__init__(

-             site, list_name, prefix, *args, **kwargs)

  

      def __next__(self):

          # We can't get the next entry from mwl.List ourselves, try and
@@ -88,141 +93,180 @@ 

          # and that causes a massive performance hit.

          nxt = super(TcmsGeneratorList, self).__next__()

          try:

+             # pylint: disable=protected-access

              return self._get_tcms(nxt.name, nxt._info)

          except (NoPageWarning, PageCheckWarning):

              return nxt

  

-     def next(self):

-         # for python2 compat

-         return self.__next__()

+     def _get_tcms_testresults(self, name, info):

+         """The main match logic for Test Results pages. Called by

+         _get_tcms when it finds a Test Results page.

+         """

+         nightly_patt = re.compile(

+             r"Test Results:Fedora (Modular )?(\d{1,3}) "

+             r"(Rawhide|Nightly|Branched) "

+             r"(\d{8,8}(\.n\.\d+)?|\d{4,4} \d{2,2}) "

+             r"(.+)$"

+         )

+         accept_patt = re.compile(

+             r"Test Results:Fedora (\d{1,3}) " r"([^ ]+?) (Rawhide |)Acceptance Test " r"(\d{1,2})$"

+         )

+         ms_patt = re.compile(

+             r"Test Results:Fedora (Modular )?(\d{1,3}) " r"([^ ]+?) ([^ ]+?) (.+)$"

+         )

+ 

+         # Modern standard nightly compose event pages, and F21-era

+         # monthly Rawhide/Branched test pages

+         match = nightly_patt.match(name)

+         if match:

+             if match.group(6) == "Summary":

+                 # we don't really ever need to do anything to existing

+                 # summary pages, and instantiating one from here is kinda

+                 # gross, so just fall through

+                 raise NoPageWarning(name)

+             modular = False

+             if match.group(1):

+                 modular = True

+             page = wikitcms.page.NightlyPage(

+                 self.site,

+                 release=match.group(2),

+                 testtype=match.group(6),

+                 milestone=match.group(3),

+                 compose=match.group(4),

+                 info=info,

+                 modular=modular,

+             )

+             return _check_page(name, page)

+ 

+         match = accept_patt.match(name)

+         if match:

+             # we don't handle these, yet.

+             raise NoPageWarning(name)

+ 

+         # milestone compose event pages

+         match = ms_patt.match(name)

+         if match:

+             if match.group(5) == "Summary":

+                 raise NoPageWarning(name)

+             modular = False

+             if match.group(1):

+                 modular = True

+             page = wikitcms.page.ComposePage(

+                 self.site,

+                 release=match.group(2),

+                 testtype=match.group(5),

+                 milestone=match.group(3),

+                 compose=match.group(4),

+                 info=info,

+                 modular=modular,

+             )

+             return _check_page(name, page)

+ 

+         # in case we matched nothing...

+         raise NoPageWarning(name)

  

-     def _check_page(self, name, page):

-         # convenience function for _get_tcms sanity check

-         if page.checkname == name:

-             return page

-         raise PageCheckWarning(page.checkname, name)

+     def _get_tcms_category(self, name, info):

+         """The main match logic for Category pages. Call by _get_tcms

+         when it finds a Category page.

+         """

+         cat_patt = re.compile(r"Category:Fedora (Modular )?(\d{1,3}) " r"(.*?) *?Test Results$")

+         tdcat_patt = re.compile(r"Category:Fedora (\d{1,3}) Test Days$")

+ 

+         # test result categories

+         match = cat_patt.match(name)

+         if match:

+             modular = False

+             if match.group(1):

+                 modular = True

+             if not match.group(3):

+                 page = ValidationCategory(self.site, match.group(2), info=info, modular=modular)

+                 return _check_page(name, page)

+ 

+             if match.group(3) == "Nightly":

+                 page = ValidationCategory(

+                     self.site, match.group(2), nightly=True, info=info, modular=modular

+                 )

+                 return _check_page(name, page)

+ 

+             # otherwise...

+             page = ValidationCategory(

+                 self.site, match.group(2), match.group(3), info=info, modular=modular

+             )

+             return _check_page(name, page)

+ 

+         # Test Day categories

+         match = tdcat_patt.match(name)

+         if match:

+             page = TestDayCategory(self.site, match.group(1), info=info)

+             return _check_page(name, page)

+ 

+         # in case we matched nothing...

+         raise NoPageWarning(name)

+ 

+     def _get_tcms_testday(self, name, info):

+         """The main match logic for Test Day pages. Called by

+         _get_tcms when it finds a Test Day page.

+         """

+         testday_patt = re.compile(r"Test Day:(\d{4}-\d{2}-\d{2}) *(.*)$")

+         # FIXME: There's a few like this, handle 'em sometime

+         # testday2_patt = re.compile(u'Test Day:(.+) (\d{4}-\d{2}-\d{2})$')

+ 

+         match = testday_patt.match(name)

+         if match:

+             page = wikitcms.page.TestDayPage(self.site, match.group(1), match.group(2), info=info)

+             return _check_page(name, page)

+ 

+         # in case we matched nothing...

+         raise NoPageWarning(name)

  

      def _get_tcms(self, name, info=()):

-         # this is the meat: it runs a bunch of string checks on page

-         # names, basically, and returns the appropriate wikitcms

-         # object if any matches.

+         """This is where the meat starts: we are going to parse the

+         page name and see if it looks like a page we have a class for,

+         if so, we will instantiate the class appropriately, check the

+         name we get matches the page we're actually running on, and

+         return the instance if so. This method quickly sorts pages we

+         *might* match into various categories then calls one of the

+         specialized methods above to do the rest of the work. At all

+         points, if we decide we can't match the page, we raise

+         NoPageWarning, which will cause whatever is using us to fall

+         through to mwclient.

+         """

          if isinstance(name, int):

              # we'll have to do a wiki roundtrip, as we need the text

              # name.

-             page = pg.Page(self.site, name)

+             page = wikitcms.page.Page(self.site, name)

              name = page.name

-         name = name.replace('_', ' ')

-         # quick non-RE check to see if we'll ever match (and filter

+         name = name.replace("_", " ")

+         # quick non-RE checks to see if we'll ever match (and filter

          # out some 'known bad' pages)

-         if (name.startswith('Test Results:') or

-                 (name.startswith('Test Day:') and not

-                  name.endswith('/ru') and not

-                  'metadata' in name.lower() and not

-                  'rendercheck' in name.lower()) or

-                 (name.startswith('Category:'))):

-             nightly_patt = re.compile(r'Test Results:Fedora (Modular )?(\d{1,3}) '

-                                       r'(Rawhide|Nightly|Branched) '

-                                       r'(\d{8,8}(\.n\.\d+)?|\d{4,4} \d{2,2}) '

-                                       r'(.+)$')

-             accept_patt = re.compile(r'Test Results:Fedora (\d{1,3}) '

-                                      r'([^ ]+?) (Rawhide |)Acceptance Test '

-                                      r'(\d{1,2})$')

-             ms_patt = re.compile(r'Test Results:Fedora (Modular )?(\d{1,3}) '

-                                  r'([^ ]+?) ([^ ]+?) (.+)$')

-             cat_patt = re.compile(r'Category:Fedora (Modular )?(\d{1,3}) '

-                                   r'(.*?) *?Test Results$')

-             tdcat_patt = re.compile(r'Category:Fedora (\d{1,3}) Test Days$')

-             testday_patt = re.compile(r'Test Day:(\d{4}-\d{2}-\d{2}) *(.*)$')

-             # FIXME: There's a few like this, handle 'em sometime

-             #testday2_patt = re.compile(u'Test Day:(.+) (\d{4}-\d{2}-\d{2})$')

- 

-             # Modern standard nightly compose event pages, and F21-era

-             # monthly Rawhide/Branched test pages

-             match = nightly_patt.match(name)

-             if match:

-                 if match.group(6) == 'Summary':

-                     # we don't really ever need to do anything to existing

-                     # summary pages, and instantiating one from here is kinda

-                     # gross, so just fall through

-                     raise NoPageWarning(name)

-                 modular = False

-                 if match.group(1):

-                     modular = True

-                 page = pg.NightlyPage(

-                     self.site, release=match.group(2), testtype=match.group(6),

-                     milestone=match.group(3), compose=match.group(4),

-                     info=info, modular=modular)

-                 return self._check_page(name, page)

- 

-             match = accept_patt.match(name)

-             if match:

-                 # we don't handle these, yet.

-                 raise NoPageWarning(name)

- 

-             # milestone compose event pages

-             match = ms_patt.match(name)

-             if match:

-                 if match.group(5) == 'Summary':

-                     raise NoPageWarning(name)

-                 modular = False

-                 if match.group(1):

-                     modular = True

-                 page = pg.ComposePage(

-                     self.site, release=match.group(2), testtype=match.group(5),

-                     milestone=match.group(3), compose=match.group(4),

-                     info=info, modular=modular)

-                 return self._check_page(name, page)

- 

-             # test result categories

-             match = cat_patt.match(name)

-             if match:

-                 modular = False

-                 if match.group(1):

-                     modular = True

-                 if not match.group(3):

-                     page = ValidationCategory(

-                         self.site, match.group(2), info=info, modular=modular)

-                     return self._check_page(name, page)

-                 elif match.group(3) == 'Nightly':

-                     page = ValidationCategory(self.site, match.group(2),

-                                               nightly=True, info=info, modular=modular)

-                     return self._check_page(name, page)

-                 else:

-                     page = ValidationCategory(self.site, match.group(2),

-                                               match.group(3), info=info, modular=modular)

-                     return self._check_page(name, page)

- 

-             # Test Day categories

-             match = tdcat_patt.match(name)

-             if match:

-                 page = TestDayCategory(self.site, match.group(1), info=info)

-                 return self._check_page(name, page)

- 

-             # test days

-             match = testday_patt.match(name)

-             if match:

-                 page = pg.TestDayPage(self.site, match.group(1),

-                                       match.group(2), info=info)

-                 return self._check_page(name, page)

+         if name.startswith("Test Results:"):

+             return self._get_tcms_testresults(name, info)

+         if name.startswith("Category:"):

+             return self._get_tcms_category(name, info)

+         if name.startswith("Test Day:") and not name.endswith("/ru"):

+             if not "metadata" in name.lower() and not "rendercheck" in name.lower():

+                 return self._get_tcms_testday(name, info)

+ 

+         # otherwise, we'll just bail out here

          raise NoPageWarning(name)

  

  

- class TcmsPageList(mwl.PageList, TcmsGeneratorList):

+ class TcmsPageList(mwclient.listing.PageList, TcmsGeneratorList):

      """A version of PageList which returns wikitcms page (and category

      etc.) objects when appropriate.

      """

+ 

      def get(self, name, info=()):

          modname = name

          if self.namespace:

-             modname = '{0}:{1}'.format(self.site.namespaces[self.namespace],

-                                        name)

+             modname = f"{self.site.namespaces[self.namespace]}:{name}"

          try:

              return self._get_tcms(modname, info)

          except (NoPageWarning, PageCheckWarning):

              return super(TcmsPageList, self).get(name, info)

  

  

- class TcmsCategory(pg.Page, TcmsGeneratorList):

+ class TcmsCategory(wikitcms.page.Page, TcmsGeneratorList):

      """A modified category class - just as mwclient's Category class

      inherits from both its Page class and its GeneratorList class,

      acting as both a page and a generator returning the members of
@@ -235,10 +279,10 @@ 

      get another ValidationCategory instance. There are sub-classes for

      various particular types of category (Test Days, validation, etc.)

      """

+ 

      def __init__(self, site, wikiname, info=None):

          super(TcmsCategory, self).__init__(site, wikiname, info=info)

-         TcmsGeneratorList.__init__(self, site, 'categorymembers', 'cm',

-                                    gcmtitle=self.name)

+         TcmsGeneratorList.__init__(self, site, "categorymembers", "cm", gcmtitle=self.name)

  

  

  class ValidationCategory(TcmsCategory):
@@ -250,48 +294,43 @@ 

      will be the top-level category for the given release.

      """

  

-     def __init__(self, site, release, milestone=None, nightly=False,

-                  info=None, modular=False):

+     # no, pylint, this is just the right number of arguments

+     # pylint: disable=too-many-arguments

+     def __init__(self, site, release, milestone=None, nightly=False, info=None, modular=False):

          _dist = "Fedora"

+         modtext = ""

          if modular:

              _dist = "Fedora Modular"

+             modtext = "|modular=true"

          if nightly is True:

-             wikiname = ("Category:{0} {1} Nightly Test "

-                         "Results").format(_dist, release)

-             if modular:

-                 self.seedtext = ("{{{{Validation results milestone category|"

-                                  "release={0}|nightly=true|modular=true}}}}").format(release)

-             else:

-                 self.seedtext = ("{{{{Validation results milestone category|"

-                                  "release={0}|nightly=true}}}}").format(release)

- 

-             self.summary = ("Relval bot-created validation result category "

-                             "page for {0} {1} nightly "

-                             "results").format(_dist, release)

+             wikiname = f"Category:{_dist} {release} Nightly Test Results"

+             self.seedtext = (

+                 "{{Validation results milestone category"

+                 f"|release={release}|nightly=true{modtext}}}}}"

+             )

+ 

+             self.summary = (

+                 "Relval bot-created validation result category "

+                 f"page for {_dist} {release} nightly results"

+             )

          elif milestone:

-             wikiname = "Category:{0} {1} {2} Test Results".format(

-                 _dist, release, milestone)

-             if modular:

-                 self.seedtext = ("{{{{Validation results milestone category"

-                                  "|release={0}|"

-                                  "milestone={1}|modular=true}}}}").format(release, milestone)

-             else:

-                 self.seedtext = ("{{{{Validation results milestone category"

-                                  "|release={0}|"

-                                  "milestone={1}}}}}").format(release, milestone)

-             self.summary = ("Relval bot-created validation result category "

-                             "page for {0} "

-                             "{1} {2}").format(_dist, release, milestone)

+             wikiname = f"Category:{_dist} {release} {milestone} Test Results"

+             self.seedtext = (

+                 f"{{{{Validation results milestone category|release={release}"

+                 f"|milestone={milestone}{modtext}}}}}"

+             )

+             self.summary = (

+                 f"Relval bot-created validation result category page for {_dist} "

+                 f"{release} {milestone}"

+             )

          else:

-             wikiname = "Category:{0} {1} Test Results".format(_dist, release)

-             if modular:

-                 self.seedtext = ("{{{{Validation results milestone category"

-                                  "|release={0}|modular=true}}}}").format(release)

-             else:

-                 self.seedtext = ("{{{{Validation results milestone category"

-                                  "|release={0}}}}}").format(release)

-             self.summary = ("Relval bot-created validation result category "

-                             "page for {0} {1}").format(_dist, release)

+             wikiname = f"Category:{_dist} {release} Test Results"

+             self.seedtext = (

+                 "{{Validation results milestone category" f"|release={release}{modtext}}}}}"

+             )

+             self.summary = (

+                 "Relval bot-created validation result category page for " f"{_dist} {release}"

+             )

  

          super(ValidationCategory, self).__init__(site, wikiname, info=info)

  
@@ -302,13 +341,15 @@ 

      """

  

      def __init__(self, site, release, info=None):

-         wikiname = "Category:Fedora {0} Test Days".format(str(release))

+         wikiname = f"Category:Fedora {str(release)} Test Days"

          self.seedtext = (

-             "This category contains all the Fedora {0} [[QA/Test_Days|Test "

+             f"This category contains all the Fedora {str(release)} [[QA/Test_Days|Test "

              "Day]] pages. A calendar of the Test Days can be found ["

              "https://apps.fedoraproject.org/calendar/list/QA/?subject=Test+Day"

-             " here].\n\n[[Category:Test Days]]").format(str(release))

+             " here].\n\n[[Category:Test Days]]"

+         )

          self.summary = "Created page (via wikitcms)"

          super(TestDayCategory, self).__init__(site, wikiname, info=info)

  

+ 

  # vim: set textwidth=100 ts=8 et sw=4:

src/wikitcms/page.py wikitcms/page.py
file renamed
+253 -224
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2014 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -22,25 +22,22 @@ 

  defined in this file.

  """

  

- from __future__ import unicode_literals

- from __future__ import print_function

- 

+ from collections import OrderedDict

  import datetime

  import logging

- import pytz

  import re

  import time

- from collections import OrderedDict

  

+ from cached_property import cached_property

  import fedfind.helpers

  import fedfind.release

- from cached_property import cached_property

  from mwclient import errors as mwe

  from mwclient import page as mwp

+ import pytz

  

- from . import result as rs

- from . import helpers

- from .exceptions import NoPageError, NotFoundError, TooManyError

+ from wikitcms.exceptions import NoPageError, NotFoundError, TooManyError

+ import wikitcms.helpers

+ import wikitcms.result

  

  logger = logging.getLogger(__name__)

  
@@ -70,9 +67,8 @@ 

          # but page is empty or something.

          if self._sections is None:

              try:

-                 apiout = self.site.api(

-                     'parse', page=self.name, prop='sections')

-                 self._sections = apiout['parse']['sections']

+                 apiout = self.site.api("parse", page=self.name, prop="sections")

+                 self._sections = apiout["parse"]["sections"]

              except mwe.APIError:

                  self._sections = []

          return self._sections
@@ -86,7 +82,7 @@ 

          this will always return an empty string.

          """

          pagetext = self.text()

-         comment = re.compile('<!--.*?-->', re.S)

+         comment = re.compile("<!--.*?-->", re.S)

          pos = -1

          for sep in self.results_separators:

              pos = pagetext.find(sep)
@@ -94,9 +90,9 @@ 

                  break

  

          if pos == -1:

-             return ''

+             return ""

          text = pagetext[pos:]

-         text = comment.sub('', text)

+         text = comment.sub("", text)

          return text

  

      @cached_property
@@ -104,24 +100,26 @@ 

          """Date the page was created. Used for sorting and seeing how

          long it's been since the last event, when creating new events.

          """

-         revs = self.revisions(limit=1, dir='newer', prop='timestamp')

+         revs = self.revisions(limit=1, dir="newer", prop="timestamp")

          try:

              origrev = next(revs)

          except StopIteration:

              # page doesn't exist

              return ""

-         return time.strftime('%Y%m%d', origrev['timestamp'])

+         return time.strftime("%Y%m%d", origrev["timestamp"])

  

      def write(self, createonly=True):

          """Create a page with its default content and summary. mwclient

          exception will be raised on any page write failure.

          """

-         seedtext = getattr(self, 'seedtext', None)

-         summary = getattr(self, 'summary', None)

+         seedtext = getattr(self, "seedtext", None)

+         summary = getattr(self, "summary", None)

          if seedtext is None or summary is None:

              raise ValueError("wikitcms.Page.write(): both seedtext and summary needed!")

          self.save(seedtext, summary, createonly=createonly)

  

+     # we're using *args, **kwargs. cool your jets, pylint.

+     # pylint: disable=signature-differs

      def save(self, *args, **kwargs):

          """Same as the original, but will retry once on fail. If you

          already retrieved the current text, you can pass it in as
@@ -131,18 +129,17 @@ 

          trip. Of course you could do this in the caller instead, it's

          just a convenience. Also clears the page sections cache.

          """

-         if 'oldtext' in kwargs and args[0] == kwargs['oldtext']:

-             return dict(nochange='')

+         if "oldtext" in kwargs and args[0] == kwargs["oldtext"]:

+             return dict(nochange="")

  

-         if 'oldtext' in kwargs:

+         if "oldtext" in kwargs:

              # avoid mwclient save() warning about unknown kwarg

-             del kwargs['oldtext']

+             del kwargs["oldtext"]

  

          try:

              ret = super(Page, self).save(*args, **kwargs)

          except mwe.EditError as err:

-             logger.warning("Page %s edit failed! Trying again in 15 seconds",

-                            self.name)

+             logger.warning("Page %s edit failed! Trying again in 15 seconds", self.name)

              logger.debug("Error was: %s", err)

              time.sleep(15)

              ret = super(Page, self).save(*args, **kwargs)
@@ -159,17 +156,17 @@ 

      page name, using the appropriate templates and template values,

      etc.

      """

-     def __init__(self, site, release, testtype, milestone='', compose='',

-                  info=None, modular=False):

+ 

+     # I like this number, pylint

+     # pylint: disable=too-many-instance-attributes, too-many-arguments

+     def __init__(self, site, release, testtype, milestone="", compose="", info=None, modular=False):

          self.release = release

          self.milestone = str(milestone)

          try:

-             self.compose = fedfind.helpers.date_check(

-                 compose, fail_raise=True, out='str')

+             self.compose = fedfind.helpers.date_check(compose, fail_raise=True, out="str")

          except ValueError:

              self.compose = str(compose)

-         self.version = "{0} {1} {2}".format(

-             self.release, self.milestone, self.compose)

+         self.version = f"{self.release} {self.milestone} {self.compose}"

          self.testtype = testtype

          self.modular = modular

  
@@ -178,21 +175,26 @@ 

          _dist = "Fedora"

          if modular:

              _dist = "Fedora Modular"

-         wikiname = "Test Results:{0} {1} {2}".format(_dist, self.version, self.testtype)

+         wikiname = f"Test Results:{_dist} {self.version} {self.testtype}"

          super(ValidationPage, self).__init__(site, wikiname, info)

  

          # Edit summary to be used for clean page creation.

-         self.summary = ("Relval bot-created {0} validation results page for {1} "

-                         "{2}").format(testtype, _dist, self.version)

+         self.summary = (

+             f"Relval bot-created {testtype} validation results page for {_dist} " f"{self.version}"

+         )

          self.results_separators = (

-             "Test Matri", "Test Areas", "An unsupported test or configuration."

-             "  No testing is required.")

+             "Test Matri",

+             "Test Areas",

+             "An unsupported test or configuration.  No testing is required.",

+         )

          # Sorting helpers. sortname is a string, sorttuple is a

          # 4-tuple. sorttuple is more reliable. See the function docs.

-         self.sortname = helpers.fedora_release_sort(

-             ' '.join((self.version, self.testtype)))

-         self.sorttuple = helpers.triplet_sort(

-             self.release, self.milestone, self.compose) + (self.testtype,)

+         self.sortname = wikitcms.helpers.fedora_release_sort(

+             " ".join((self.version, self.testtype))

+         )

+         self.sorttuple = wikitcms.helpers.triplet_sort(

+             self.release, self.milestone, self.compose

+         ) + (self.testtype,)

  

      @property

      def results_sections(self):
@@ -209,11 +211,11 @@ 

              return secs

          first = None

          for i, sec in enumerate(secs):

-             if 'Test Matri' in sec['line'] or 'Test Areas' in sec['line']:

+             if "Test Matri" in sec["line"] or "Test Areas" in sec["line"]:

                  first = i

                  break

-             elif 'Key' in sec['line']:

-                 first = i+1

+             if "Key" in sec["line"]:

+                 first = i + 1

          return secs[first:]

  

      def get_resultrows(self, statuses=None, transferred=True):
@@ -225,26 +227,27 @@ 

              return list()

          rows = list()

          pagetext = self.text()

-         comment = re.compile('<!--.*?-->', re.S)

+         comment = re.compile("<!--.*?-->", re.S)

          for i, sec in enumerate(sections):

              try:

-                 nextsec = sections[i+1]

+                 nextsec = sections[i + 1]

              except IndexError:

                  nextsec = None

-             section = sec['line']

-             secid = sec['index']

+             section = sec["line"]

+             secid = sec["index"]

              if nextsec:

-                 sectext = pagetext[sec['byteoffset']:nextsec['byteoffset']]

+                 sectext = pagetext[sec["byteoffset"] : nextsec["byteoffset"]]

              else:

-                 sectext = pagetext[sec['byteoffset']:]

+                 sectext = pagetext[sec["byteoffset"] :]

              # strip comments

-             sectext = comment.sub('', sectext)

-             newrows = rs.find_resultrows(sectext, section, secid, statuses,

-                                          transferred)

+             sectext = comment.sub("", sectext)

+             newrows = wikitcms.result.find_resultrows(

+                 sectext, section, secid, statuses, transferred

+             )

              rows.extend(newrows)

          return rows

  

-     def find_resultrow(self, testcase='', section='', testname='', env=''):

+     def find_resultrow(self, testcase="", section="", testname="", env=""):

          """Return exactly one result row with the desired attributes,

          or raise an exception (if more or less than one row is found).

          The Installation page contains some rows in the same section
@@ -257,10 +260,10 @@ 

              raise NoPageError("Page does not exist or has no result rows.")

  

          # Find the right row

-         nrml = helpers.normalize

-         rows = [r for r in rows if

-                 nrml(testcase) in nrml(r.testcase)

-                 or nrml(testcase) in nrml(r.name)]

+         nrml = wikitcms.helpers.normalize

+         rows = [

+             r for r in rows if nrml(testcase) in nrml(r.testcase) or nrml(testcase) in nrml(r.name)

+         ]

          if len(rows) > 1 and section:

              rows = [r for r in rows if nrml(section) in nrml(r.section)]

          if len(rows) > 1 and testname:
@@ -268,15 +271,17 @@ 

          if len(rows) > 1 and env:

              # the way this match is done must be kept in line with the

              # corresponding match in add_results, below

-             rows = [r for r in rows if nrml(env) in

-                     [renv.lower() for renv in r.results.keys()]]

+             rows = [r for r in rows if nrml(env) in [renv.lower() for renv in r.results.keys()]]

          # try a more precise name match - e.g. "upgrade_dnf" vs.

          # "upgrade_dnf_encrypted"

          if len(rows) > 1:

-             rows = [r for r in rows if

-                     nrml(testcase) == nrml(r.testcase) or

-                     nrml(testcase) == nrml(r.name) or

-                     nrml(testname) == nrml(r.name)]

+             rows = [

+                 r

+                 for r in rows

+                 if nrml(testcase) == nrml(r.testcase)

+                 or nrml(testcase) == nrml(r.name)

+                 or nrml(testname) == nrml(r.name)

+             ]

          if not rows:

              raise NotFoundError("Specified row cannot be found.")

          if len(rows) > 1:
@@ -288,13 +293,10 @@ 

          the given test type point to this page.

          """

          if self.modular:

-             curr = self.site.pages[

-                 'Test Results:Current Modular {0} Test'.format(self.testtype)]

+             curr = self.site.pages[f"Test Results:Current Modular {self.testtype} Test"]

          else:

-             curr = self.site.pages[

-                 'Test Results:Current {0} Test'.format(self.testtype)]

-         curr.save("#REDIRECT [[{0}]]".format(self.name),

-                   "relval: update to current event", createonly=None)

+             curr = self.site.pages[f"Test Results:Current {self.testtype} Test"]

+         curr.save(f"#REDIRECT [[{self.name}]]", "relval: update to current event", createonly=None)

  

      def add_results(self, resultsdict, allowdupe=False):

          """Adds multiple results to the page. Passed a dict whose
@@ -305,6 +307,9 @@ 

          result from the user. The return list contains a 3-tuple of

          (row, env, result) for each dupe.

          """

+         # look, pylint, this is complicated stuff. calm yoself.

+         # pylint: disable=too-many-locals, too-many-branches, too-many-statements

+ 

          # We need to sort the dict in a particular way: by the section

          # ID of each row, in reverse order. This is so when we edit

          # the page, we effectively do so backwards, and the byte
@@ -312,11 +317,14 @@ 

          # along the way (we don't edit section 1 before section 3 and

          # thus not quite slice the text correctly when we look for

          # section 3).

-         resultsdict = OrderedDict(sorted(resultsdict.items(),

-                                          key=lambda x: int(x[0].secid),

-                                          reverse=True))

-         nonetext = rs.Result().result_template

+         resultsdict = OrderedDict(

+             sorted(resultsdict.items(), key=lambda x: int(x[0].secid), reverse=True)

+         )

+         nonetext = wikitcms.result.Result().result_template

          dupes = list()

+         # this tracks rows that we don't wind up touching, so they

+         # can be left out of the summary text

+         notrows = list()

          newtext = oldtext = self.text()

          for (row, results) in resultsdict.items():

              # It's possible that we have rows with identical text in
@@ -327,14 +335,13 @@ 

              # to bother finding the *end* of the section.

              # We could just edit the page section-by-section, but that

              # involves doing one remote roundtrip per section.

-             secoff = [sec['byteoffset'] for sec in self.sections if

-                       sec['index'] == row.secid][0]

+             secoff = [sec["byteoffset"] for sec in self.sections if sec["index"] == row.secid][0]

              if secoff:

                  sectext = newtext[secoff:]

              else:

                  sectext = newtext

              oldrow = row.origtext

-             cells = oldrow.split('\n|')

+             cells = oldrow.split("\n|")

  

              for (env, result) in results:

                  if not env in row.results:
@@ -347,16 +354,14 @@ 

                          # ...if not, we'll see if the passed env is

                          # a substring of only one of the envs, case-

                          # insensitively.

-                         cands = [cand for cand in row.results.keys() if

-                                  env.lower() in cand.lower()]

+                         cands = [cand for cand in row.results.keys() if env.lower() in cand.lower()]

                          if len(cands) == 1:

                              env = cands[0]

                          else:

                              # LOG: bad env

                              continue

                  if not allowdupe:

-                     dupe = [r for r in row.results[env] if

-                             r.user == result.user]

+                     dupe = [r for r in row.results[env] if r.user == result.user]

                      if dupe:

                          dupes.append((row, env, result))

                          continue
@@ -364,15 +369,16 @@ 

                  rescell = cells[row.columns.index(env)]

                  if nonetext in rescell:

                      rescell = rescell.replace(nonetext, restext)

-                 elif '\n' in rescell:

-                     rescell = rescell.replace('\n', restext+'\n')

+                 elif "\n" in rescell:

+                     rescell = rescell.replace("\n", restext + "\n")

                  else:

                      rescell = rescell + restext

                  cells[row.columns.index(env)] = rescell

  

-             newrow = '\n|'.join(cells)

+             newrow = "\n|".join(cells)

              if newrow == oldrow:

                  # All dupes, or something.

+                 notrows.append(row)

                  continue

              sectext = sectext.replace(oldrow, newrow, 1)

              if secoff:
@@ -380,13 +386,13 @@ 

              else:

                  newtext = sectext

  

-         if len(resultsdict) > 3:

-             testtext = ', '.join(row.name for row in list(resultsdict.keys())[:3])

-             testtext = '{0}...'.format(testtext)

+         touchedrows = [row for row in resultsdict.keys() if row not in notrows]

+         if len(touchedrows) > 3:

+             testtext = ", ".join(row.name for row in touchedrows[:3])

+             testtext = f"{testtext}..."

          else:

-             testtext = ', '.join(row.name for row in resultsdict.keys())

-         summary = ("Result(s) for test(s): {0} filed via "

-                    "relval").format(testtext)

+             testtext = ", ".join(row.name for row in touchedrows)

+         summary = f"Result(s) for test(s): {testtext} filed via relval"

          self.save(newtext, summary, oldtext=oldtext, createonly=None)

          return dupes

  
@@ -408,49 +414,67 @@ 

      """A Page class that describes a single result page from a test

      compose or release candidate validation test event.

      """

+ 

+     # I like this number, pylint

+     # pylint: disable=too-many-arguments

      def __init__(self, site, release, testtype, milestone, compose, info=None, modular=False):

          super(ComposePage, self).__init__(

-             site, release=release, milestone=milestone, compose=compose,

-             testtype=testtype, info=info, modular=modular)

-         self.shortver = "{0} {1}".format(self.milestone, self.compose)

+             site,

+             release=release,

+             milestone=milestone,

+             compose=compose,

+             testtype=testtype,

+             info=info,

+             modular=modular,

+         )

+         self.shortver = f"{self.milestone} {self.compose}"

  

          # String that will generate a clean copy of the page using the

          # test page generation template system.

          if self.modular:

-             seedtmpl = ("{{{{subst:Modular validation results|testtype={0}|release={1}|"

-                         "milestone={2}|compose={3}}}}}")

+             tmpl = "Modular validation results"

          else:

-             seedtmpl = ("{{{{subst:Validation results|testtype={0}|release={1}|"

-                         "milestone={2}|compose={3}}}}}")

-         self.seedtext = seedtmpl.format(

-             testtype, self.release, self.milestone, self.compose)

+             tmpl = "Validation results"

+         self.seedtext = (

+             f"{{{{subst:{tmpl}|testtype={testtype}|release={self.release}|"

+             f"milestone={self.milestone}|compose={self.compose}"

+         )

  

  

  class NightlyPage(ValidationPage):

      """A Page class that describes a single result page from a nightly

      validation test event.

      """

+ 

+     # I like this number, pylint

+     # pylint: disable=too-many-arguments

      def __init__(self, site, release, testtype, milestone, compose, info=None, modular=False):

          super(NightlyPage, self).__init__(

-             site, release=release, milestone=milestone, compose=compose,

-             testtype=testtype, info=info, modular=modular)

+             site,

+             release=release,

+             milestone=milestone,

+             compose=compose,

+             testtype=testtype,

+             info=info,

+             modular=modular,

+         )

          self.shortver = self.compose

          # overridden for nightlies to avoid expensive roundtrips

-         if '.' in compose:

-             self.creation_date = compose.split('.')[0]

+         if "." in compose:

+             self.creation_date = compose.split(".")[0]

          else:

              self.creation_date = compose

  

          # String that will generate a clean copy of the page using the

          # test page generation template system.

          if self.modular:

-             seedtmpl = ("{{{{subst:Modular validation results|testtype={0}|release={1}|"

-                         "milestone={2}|date={3}}}}}")

+             tmpl = "Modular validation results"

          else:

-             seedtmpl = ("{{{{subst:Validation results|testtype={0}|release={1}|"

-                         "milestone={2}|date={3}}}}}")

-         self.seedtext = seedtmpl.format(

-             testtype, self.release, self.milestone, self.compose)

+             tmpl = "Validation results"

+         self.seedtext = (

+             f"{{{{subst:{tmpl}|testtype={testtype}|release={self.release}|"

+             f"milestone={self.milestone}|date={self.compose}"

+         )

  

  

  class SummaryPage(Page):
@@ -458,25 +482,26 @@ 

      event. event is the parent Event() for the page; summary pages are

      always considered to be a part of an Event.

      """

+ 

      def __init__(self, site, event, info=None, modular=False):

          self.modular = modular

-         wikiname = "Test Results:Fedora {0} Summary".format(event.version)

+         wikiname = f"Test Results:Fedora {event.version} Summary"

          if modular:

-             wikiname = "Test Results:Fedora Modular {0} Summary".format(event.version)

+             wikiname = f"Test Results:Fedora Modular {event.version} Summary"

          super(SummaryPage, self).__init__(site, wikiname, info)

          _dist = "Fedora"

          if modular:

              _dist = "Fedora Modular"

-         self.summary = ("Relval bot-created validation results summary for "

-                         "{0} {1}").format(_dist, event.version)

+         self.summary = f"Relval bot-created validation results summary for {_dist} {event.version}"

          self.seedtext = (

-             "{0} {1} [[QA:Release validation test plan|release "

+             f"{_dist} {event.version} [[QA:Release validation test plan|release "

              "validation]] summary. This page shows the results from all the "

              "individual result pages for this compose together. You can file "

              "results directly from this page and they will be saved into the "

              "correct individual result page. To see test instructions, visit "

              "any of the individual pages (the section titles are links). You "

-             "can find download links below.\n\n").format(_dist, event.version)

+             "can find download links below.\n\n"

+         )

          self.seedtext += "__TOC__\n\n"

          self.seedtext += "== Downloads ==\n{{" + _dist + " " + event.version + " Download}}"

          for testpage in event.valid_pages:
@@ -489,24 +514,23 @@ 

          event point to this page.

          """

          if self.modular:

-             curr = self.site.pages['Test Results:Current Modular Summary']

+             curr = self.site.pages["Test Results:Current Modular Summary"]

          else:

-             curr = self.site.pages['Test Results:Current Summary']

-         curr.save("#REDIRECT [[{0}]]".format(self.name),

-                   "relval: update to current event", createonly=None)

+             curr = self.site.pages["Test Results:Current Summary"]

+         curr.save(f"#REDIRECT [[{self.name}]]", "relval: update to current event", createonly=None)

  

  

  class DownloadPage(Page):

      """The page containing image download links for a ValidationEvent.

      As with SummaryPage, is always associated with a specific event.

      """

+ 

      def __init__(self, site, event, info=None, modular=False):

          _dist = "Fedora"

          if modular:

              _dist = "Fedora Modular"

-         wikiname = "Template:{0} {1} Download".format(_dist, event.version)

-         self.summary = "Relval bot-created download page for {0} {1}".format(

-             _dist, event.version)

+         wikiname = f"Template:{_dist} {event.version} Download"

+         self.summary = f"Relval bot-created download page for {_dist} {event.version}"

          super(DownloadPage, self).__init__(site, wikiname, info)

          self.event = event

  
@@ -520,43 +544,43 @@ 

          for the given image type and arch then there'll be a download

          link in the appropriate column.

          """

+         # pylint: disable=too-many-branches

+ 

          # sorting score values (see below)

-         archscores = (

-             (('x86_64', 'i386'), 2000),

-         )

+         archscores = ((("x86_64", "i386"), 2000),)

          loadscores = (

-             (('everything',), 300),

-             (('workstation',), 220),

-             (('server',), 210),

-             (('cloud', 'desktop', 'cloud_base', 'docker_base', 'atomic'), 200),

-             (('kde',), 190),

-             (('minimal',), 90),

-             (('xfce',), 80),

-             (('soas',), 73),

-             (('mate',), 72),

-             (('cinnamon',), 71),

-             (('lxde',), 70),

-             (('source',), -10),

+             (("everything",), 300),

+             (("workstation",), 220),

+             (("server",), 210),

+             (("cloud", "desktop", "cloud_base", "docker_base", "atomic"), 200),

+             (("kde",), 190),

+             (("minimal",), 90),

+             (("xfce",), 80),

+             (("soas",), 73),

+             (("mate",), 72),

+             (("cinnamon",), 71),

+             (("lxde",), 70),

+             (("source",), -10),

          )

          # Start by iterating over all images and grouping them by load

          # (that's imagedict) and keeping a record of each arch we

          # encounter (that's arches).

          arches = set()

-         imagedict = dict()

+         imagedict = OrderedDict()

          for img in self.event.ff_release_images.all_images:

-             if img['arch']:

-                 arches.add(img['arch'])

+             if img["arch"]:

+                 arches.add(img["arch"])

              # simple human-readable identifier for the image

-             desc = ' '.join((img['subvariant'], img['type']))

+             desc = " ".join((img["subvariant"], img["type"]))

              # assign a 'score' to the image; this will be used for

              # ordering the download table's rows.

-             img['score'] = 0

+             img["score"] = 0

              for (values, score) in archscores:

-                 if img['arch'] in values:

-                     img['score'] = score

+                 if img["arch"] in values:

+                     img["score"] = score

              for (values, score) in loadscores:

-                 if img['subvariant'].lower() in values:

-                     img['score'] += score

+                 if img["subvariant"].lower() in values:

+                     img["score"] += score

              # The dict values are lists of images. We could use a

              # DefaultDict here, but faking it is easy too.

              if desc in imagedict:
@@ -566,9 +590,9 @@ 

          # Now we have our data, sort the dict using the weight we

          # calculated earlier. We use the max score of all arches in

          # each group of images.

-         imagedict = OrderedDict(sorted(imagedict.items(),

-                                        key=lambda x: max(img['score'] for img in x[1]),

-                                        reverse=True))

+         imagedict = OrderedDict(

+             sorted(imagedict.items(), key=lambda x: max(img["score"] for img in x[1]), reverse=True)

+         )

          # ...and sort the arches (just so they don't move around in

          # each new page and confuse people).

          arches = sorted(arches)
@@ -576,33 +600,32 @@ 

          # Now generate the table.

          table = '{| class="wikitable sortable mw-collapsible" width=100%\n|-\n'

          # Start of the header row...

-         table += '! Image'

+         table += "! Image"

          for arch in arches:

              # Add a column for each arch

-             table += ' !! {0}'.format(arch)

-         table += '\n'

+             table += f" !! {arch}"

+         table += "\n"

          for (subvariant, imgs) in imagedict.items():

              # Add a row for each subvariant

-             table += '|-\n'

-             table += '| {0}\n'.format(subvariant)

+             table += "|-\n"

+             table += f"| {subvariant}\n"

              for arch in arches:

                  # Add a cell for each arch (whether we have an image

                  # or not)

-                 table += '| '

+                 table += "| "

                  for img in imgs:

-                     if img['arch'] == arch:

+                     if img["arch"] == arch:

                          # Add a link to the image if we have one

-                         table += '[{0} Download]'.format(img['url'])

-                 table += '\n'

+                         table += f"[{img['url']} Download]"

+                 table += "\n"

          # Close out the table when we're done

-         table += '|-\n|}'

+         table += "|-\n|}"

          return table

  

      def update_current(self):

          """Kind of a hack - relval needs this to exist as things

          stand. I'll probably refactor it later.

          """

-         pass

  

  

  class AMIPage(Page):
@@ -610,13 +633,13 @@ 

      in the Cloud validation page to make it easy for people to find

      the correct AMIs.

      """

+ 

      def __init__(self, site, event, info=None, modular=False):

          _dist = "Fedora"

          if modular:

              _dist = "Fedora Modular"

-         wikiname = "Template:{0} {1} AMI".format(_dist, event.version)

-         self.summary = "Relval bot-created AMI page for {0} {1}".format(

-             _dist, event.version)

+         wikiname = f"Template:{_dist} {event.version} AMI"

+         self.summary = f"Relval bot-created AMI page for {_dist} {event.version}"

          super(AMIPage, self).__init__(site, wikiname, info)

          self.event = event

  
@@ -626,65 +649,64 @@ 

          have to query this information out of datagrepper, that's the

          only place where it's available.

          """

+         # pylint: disable=too-many-locals

          text = ""

          # first, let's get the information out of datagrepper. We'll

          # ask for messages up to 2 days after the event date.

-         date = fedfind.helpers.parse_cid(self.event.ff_release.cid, dic=True)['date']

-         start = datetime.datetime.strptime(date, '%Y%m%d').replace(tzinfo=pytz.utc)

+         date = fedfind.helpers.parse_cid(self.event.ff_release.cid, dic=True)["date"]

+         start = datetime.datetime.strptime(date, "%Y%m%d").replace(tzinfo=pytz.utc)

          end = start + datetime.timedelta(days=2)

-         # convert to epoch (this is what datagrepper wants). In Python

-         # 3 we can use just .timestamp() but sadly not in Python 2.

-         # https://stackoverflow.com/questions/6999726

-         epoch = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)

-         start = (start - epoch).total_seconds()

-         end = (end - epoch).total_seconds()

          url = "https://apps.fedoraproject.org/datagrepper/raw"

          url += "?topic=org.fedoraproject.prod.fedimg.image.publish"

-         url += "&start={0}&end={1}".format(start, end)

+         # convert to epoch (this is what datagrepper wants)

+         url += f"&start={start.timestamp()}&end={end.timestamp()}"

          json = fedfind.helpers.download_json(url)

-         msgs = json['raw_messages']

+         msgs = json["raw_messages"]

          # handle pagination

-         for page in range(2, json['pages'] + 1):

-             newurl = url + "&page={0}".format(page)

+         for page in range(2, json["pages"] + 1):

+             newurl = f"{url}&page={page}"

              newjson = fedfind.helpers.download_json(newurl)

-             msgs.extend(newjson['raw_messages'])

+             msgs.extend(newjson["raw_messages"])

  

          # now let's find the messages for our event compose

-         ours = [msg['msg'] for msg in msgs if msg['msg']['compose'] == self.event.ff_release.cid]

+         ours = [msg["msg"] for msg in msgs if msg["msg"]["compose"] == self.event.ff_release.cid]

  

          def _table_line(msg):

              """Convenience function for generating a table line."""

-             destination = msg['destination']

-             ami = msg['extra']['id']

+             destination = msg["destination"]

+             ami = msg["extra"]["id"]

              url = "https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?"

-             url += "region={0}#LaunchInstanceWizard:ami={1}".format(destination, ami)

-             return "| {0}\n| {1}\n| [{2} Launch in EC2]\n|-\n".format(destination, ami, url)

+             url += f"region={destination}#LaunchInstanceWizard:ami={ami}"

+             return f"| {destination}\n| {ami}\n| [{url} Launch in EC2]\n|-\n"

  

          def _table(arch, virttype, voltype):

              """Convenience function for adding a table."""

-             ret = "== {0} {1} {2} AMIs ==\n\n".format(arch, virttype, voltype)

+             ret = f"== {arch} {virttype} {voltype} AMIs ==\n\n"

              ret += '{| class="wikitable sortable mw-collapsible'

-             if arch != 'x86_64' or virttype != 'hvm' or voltype != 'standard':

+             if arch != "x86_64" or virttype != "hvm" or voltype != "standard":

                  # we expand the x86_64 hvm standard table by default

-                 ret += ' mw-collapsed'

+                 ret += " mw-collapsed"

              ret += '" width=100%\n|-\n'

              ret += "! Region !! AMI ID !! Direct launch link\n|-\n"

              # find the right messages for this arch and types

-             relevants = [msg for msg in ours if

-                          msg['architecture'] == arch and

-                          msg['extra']['virt_type'] == virttype and

-                          msg['extra']['vol_type'] == voltype]

+             relevants = [

+                 msg

+                 for msg in ours

+                 if msg["architecture"] == arch

+                 and msg["extra"]["virt_type"] == virttype

+                 and msg["extra"]["vol_type"] == voltype

+             ]

              # sort the messages by region so the table is easier to scan

-             relevants.sort(key=lambda x:x['destination'])

+             relevants.sort(key=lambda x: x["destination"])

              for msg in relevants:

                  ret += _table_line(msg)

              ret += "|}\n\n"

              return ret

  

          # now let's create and populate the tables

-         for arch in ('x86_64', 'arm64'):

-             for virttype in ('hvm',):

-                 for voltype in ('standard', 'gp2'):

+         for arch in ("x86_64", "arm64"):

+             for virttype in ("hvm",):

+                 for voltype in ("standard", "gp2"):

                      text += _table(arch, virttype, voltype)

  

          return text
@@ -698,27 +720,27 @@ 

      for its own methods, do *not* try writing one of these to the

      wiki.

      """

+ 

      def __init__(self, site, date, subject, info=None):

          # Handle names with no subject, e.g. Test_Day:2012-03-14

-         wikiname = "Test Day:{0}".format(date)

+         wikiname = f"Test Day:{date}"

          if subject:

-             wikiname = "{0} {1}".format(wikiname, subject)

+             wikiname = f"{wikiname} {subject}"

          super(TestDayPage, self).__init__(site, wikiname, info)

          self.date = date

          self.subject = subject

-         self.results_separators = ('Test Results', 'Results')

+         self.results_separators = ("Test Results", "Results")

  

-     def write(self):

+     def write(self, createonly=True):

          print("Creating Test Day pages is not yet supported.")

-         return

  

      @cached_property

      def bugs(self):

          """Returns a list of bug IDs referenced in the results section

          (as strings). Will find bugs in {{result}} and {{bz}}

          templates."""

-         bugs = helpers.find_bugs(self.results_wikitext)

-         for res in rs.find_results_by_row(self.results_wikitext):

+         bugs = wikitcms.helpers.find_bugs(self.results_wikitext)

+         for res in wikitcms.result.find_results_by_row(self.results_wikitext):

              bugs.update(res.bugs)

          return sorted(bugs)

  
@@ -728,13 +750,15 @@ 

          parameters to the template like it should. This fixes that, in

          a fairly rough and ready way.

          """

-         badres = re.compile(r'({{result.*?)}} {0,2}'

-                             r'(<ref>{{bz\|\d{6,7}}}</ref>) ?'

-                             r'(<ref>{{bz\|\d{6,7}}}</ref>)? ?'

-                             r'(<ref>{{bz\|\d{6,7}}}</ref>)? ?'

-                             r'(<ref>{{bz\|\d{6,7}}}</ref>)? ?'

-                             r'(<ref>{{bz\|\d{6,7}}}</ref>)? ?'

-                             r'(<ref>{{bz\|\d{6,7}}}</ref>)?')

+         badres = re.compile(

+             r"({{result.*?)}} {0,2}"

+             r"(<ref>{{bz\|\d{6,7}}}</ref>) ?"

+             r"(<ref>{{bz\|\d{6,7}}}</ref>)? ?"

+             r"(<ref>{{bz\|\d{6,7}}}</ref>)? ?"

+             r"(<ref>{{bz\|\d{6,7}}}</ref>)? ?"

+             r"(<ref>{{bz\|\d{6,7}}}</ref>)? ?"

+             r"(<ref>{{bz\|\d{6,7}}}</ref>)?"

+         )

          text = oldtext = self.text()

          oldtext = text

          matches = badres.finditer(text)
@@ -744,11 +768,13 @@ 

              for group in groups[1:]:

                  if group:

                      bugs.append(group[10:-8])

-             text = text.replace(match.group(0),

-                                 match.group(1) + '||' + '|'.join(bugs) + '}}')

-         return self.save(text, summary=u"Fix testday app-generated results to "

-                          "use {{result}} template for bug references",

-                          oldtext=oldtext)

+             text = text.replace(match.group(0), match.group(1) + "||" + "|".join(bugs) + "}}")

+         return self.save(

+             text,

+             summary="Fix testday app-generated results to "

+             "use {{result}} template for bug references",

+             oldtext=oldtext,

+         )

  

      def long_refs(self):

          """People tend to include giant essays as <ref> notes on test
@@ -767,23 +793,26 @@ 

          text = oldtext = self.text()

          if '<ref group="long">' in text:

              # Don't run if we've already been run on this page

-             return dict(nochange='')

-         refpatt = re.compile('<ref>(.+?</ref>)', re.S)

+             return dict(nochange="")

+         refpatt = re.compile("<ref>(.+?</ref>)", re.S)

          matches = refpatt.finditer(text)

          found = False

          for match in matches:

              if len(match.group(0)) > 150:

                  found = True

-                 text = text.replace(match.group(0),

-                                     '<ref group="long">' + match.group(1))

+                 text = text.replace(match.group(0), '<ref group="long">' + match.group(1))

          if found:

-             text = helpers.rreplace(

-                 text.strip(), '\n\n',

-                 '\n\n== Long comments ==\n<references group="long" />\n\n', 1)

-             return self.save(text, summary=u"Move long comments to a separate "

-                              "section at end of page", oldtext=oldtext)

-         else:

-             # If we didn't find any long refs, don't do anything

-             return dict(nochange='')

+             text = wikitcms.helpers.rreplace(

+                 text.strip(), "\n\n", '\n\n== Long comments ==\n<references group="long" />\n\n', 1

+             )

+             return self.save(

+                 text,

+                 summary="Move long comments to a separate " "section at end of page",

+                 oldtext=oldtext,

+             )

+ 

+         # If we didn't find any long refs, don't do anything

+         return dict(nochange="")

+ 

  

  # vim: set textwidth=100 ts=8 et sw=4:

src/wikitcms/release.py wikitcms/release.py
file renamed
+14 -14
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2014 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -19,32 +19,29 @@ 

  

  """Classes that describe distribution releases are defined here."""

  

- from __future__ import unicode_literals

- from __future__ import print_function

+ import wikitcms.page

+ import wikitcms.listing

  

- from . import page as pg

- from . import listing as li

  

- class Release(object):

+ class Release:

      """Class for a Fedora release. wiki is a wikitcms site object.

      Release is a string containing a Fedora release version (e.g. 21).

      """

+ 

      def __init__(self, release, wiki, modular=False):

          self.release = release

          self.modular = modular

          dist = "Fedora"

          if modular:

              dist = "Fedora Modular"

-         self.category_name = "Category:{0} {1} Test Results".format(

-             dist, self.release)

+         self.category_name = f"Category:{dist} {self.release} Test Results"

          self.site = wiki

  

      @property

      def testday_pages(self):

          """All Test Day pages for this release (as a list)."""

-         cat = self.site.pages[

-             'Category:Fedora {0} Test Days'.format(self.release)]

-         return [page for page in cat if isinstance(page, pg.TestDayPage)]

+         cat = self.site.pages[f"Category:Fedora {self.release} Test Days"]

+         return [page for page in cat if isinstance(page, wikitcms.page.TestDayPage)]

  

      def milestone_pages(self, milestone=None):

          """If no milestone, will give all release validation pages for
@@ -53,8 +50,11 @@ 

          works by category; you may get somewhat different results by

          using page name prefixes.

          """

-         cat = li.ValidationCategory(self.site, self.release, milestone, modular=self.modular)

+         cat = wikitcms.listing.ValidationCategory(

+             self.site, self.release, milestone, modular=self.modular

+         )

          pgs = self.site.walk_category(cat)

-         return (p for p in pgs if isinstance(p, pg.ValidationPage))

+         return (p for p in pgs if isinstance(p, wikitcms.page.ValidationPage))

+ 

  

  # vim: set textwidth=100 ts=8 et sw=4:

src/wikitcms/result.py wikitcms/result.py
file renamed
+147 -212
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2014 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -20,40 +20,40 @@ 

  """This file defines various classes and helper functions for working

  with results."""

  

- from __future__ import unicode_literals

- from __future__ import print_function

- 

- import re

- 

  from collections import OrderedDict

+ import re

  

- from wikitcms import helpers

+ import wikitcms.helpers

  

  # These are used by multiple functions, so let's share them.

  # Wiki table row separator

- SEP_PATT = re.compile(r'\|[-\}].*?\n')

+ SEP_PATT = re.compile(r"\|[-\}].*?\n")

  # Identifies an instance of the result template. Will break if the

  # result contains another template, but don't do that. the lookahead

  # is used to capture the 'comments' for the result: we keep matching

  # until we hit the next instance of the template, or a cell or row

  # separator (newline starting with a |). The re.S is vital.

- RES_PATT = re.compile(r'{{result.+?}}.*?(?=\n*{{result|$|\n\|)', re.S)

+ RES_PATT = re.compile(r"{{result.+?}}.*?(?=\n*{{result|$|\n\|)", re.S)

+ 

  

  def _filter_results(results, statuses=None, transferred=True, bot=True):

      """Filter results. Shared between next two functions."""

      # Drop example / sample results

-     results = [r for r in results if not r.user or

-                r.user.lower() not in

-                ('sampleuser', 'exampleuser', 'example', 'username', 'fasname')]

+     results = [

+         r

+         for r in results

+         if not r.user

+         or r.user.lower() not in ("sampleuser", "exampleuser", "example", "username", "fasname")

+     ]

      if statuses:

-         results = [r for r in results for s in statuses if r.status and

-                    s in r.status]

+         results = [r for r in results for s in statuses if r.status and s in r.status]

      if not transferred:

          results = [r for r in results if not r.transferred]

      if not bot:

          results = [r for r in results if not r.bot]

      return results

  

+ 

  def find_results(text, statuses=None, transferred=True, bot=True):

      """Find test results in a given chunk of wiki text. Returns a list

      of Result objects. If statuses is not None, it should be an
@@ -66,7 +66,7 @@ 

      results = list()

      # Identifies an instance of the old {{testresult template, in the

      # same way as RES_PATT (above).

-     oldres_patt = re.compile(r'{{testresult.+?}}.*?(?={{testresult|$)', re.M)

+     oldres_patt = re.compile(r"{{testresult.+?}}.*?(?={{testresult|$)", re.M)

      for res in RES_PATT.findall(text):

          results.append(Result.from_result_template(res))

      for oldres in oldres_patt.findall(text):
@@ -75,6 +75,7 @@ 

      results = _filter_results(results, statuses, transferred, bot)

      return results

  

+ 

  def find_results_by_row(text, statuses=None):

      """Find test results using a row-by-row scan, guessing the user

      for results which do not have one. Used for Test Day pages. Note:
@@ -85,10 +86,10 @@ 

      # cell. Good enough to find the contents of the first cell in the

      # row, which we do because it'll usually be the user name in a

      # Test Day results table.

-     cell_patt = re.compile(r'\| *(.*?) *\n\|')

+     cell_patt = re.compile(r"\| *(.*?) *\n\|")

      # Captures the user name from a wikilink to a user page (typically

      # used to indicate the user who reports a result).

-     user_patt = re.compile(r'\[\[User: *(.*?) *[|\]]')

+     user_patt = re.compile(r"\[\[User: *(.*?) *[|\]]")

      results = list()

  

      for row in SEP_PATT.split(text):
@@ -110,21 +111,25 @@ 

      results = _filter_results(results, statuses)

      return results

  

- def find_resultrows(text, section='', secid=0, statuses=None, transferred=True):

+ 

+ def find_resultrows(text, section="", secid=0, statuses=None, transferred=True):

      """Find result rows in a given chunk of wiki text. Returns a list

      of ResultRow objects. 'statuses' and 'transferred' are passed all

      the way through ResultRow to find_results() and behave as

      described there, for the Result objects in each ResultRow.

      """

+     # oh shut up pylint what do you have against variables

+     # pylint: disable=too-many-locals

+ 

      # identify all test case names, including old ones. modern ones

      # match QA:Testcase.*, but older ones sometimes have QA/TestCase.

-     testcase_pattern = re.compile(r'(QA[:/]Test.+?)[\|\]\n]')

+     testcase_pattern = re.compile(r"(QA[:/]Test.+?)[\|\]\n]")

      # row separator is |-, end of table is |}

      columns = list()

      resultrows = list()

      rows = SEP_PATT.split(text)

      for row in rows:

-         rowlines = row.split('\n')

+         rowlines = row.split("\n")

          for line in rowlines:

              # check if this is a column header row, and update column

              # names. Sometimes the header row doesn't have an explicit
@@ -132,11 +137,11 @@ 

              # preceding lines, so we split the row into lines and

              # check each line in the row.

              line = line.strip()

-             if line.find('!') == 0 and line.find('!!') > 0:

+             if line.find("!") == 0 and line.find("!!") > 0:

                  # column titles. note: mw syntax in fact allows for

                  # '! title\n! title\n! title' as well as '! title !!

                  # title !! title'. But we don't use that syntax.

-                 columns = line.lstrip('!').split('!!')

+                 columns = line.lstrip("!").split("!!")

                  for column in columns:

                      # sanitize names a bit

                      newcol = column.strip()
@@ -145,13 +150,13 @@ 

                      try:

                          # drop out any <ref> block

                          posa = newcol.index("<ref>")

-                         posb = newcol.index("</ref>") + 6 # length

+                         posb = newcol.index("</ref>") + 6  # length

                          newcol = newcol[:posa] + newcol[posb:]

                          newcol = newcol.strip()

                      except ValueError:

                          pass

                      try:

-                         newcol = newcol.split('|')[1]

+                         newcol = newcol.split("|")[1]

                      except IndexError:

                          pass

                      if newcol != column:
@@ -167,15 +172,15 @@ 

              # Transferred=, because the resrow.results dict will

              # always have a key for each result column, though its

              # value may be an empty list.

-             resrow = ResultRow.from_wiki_row(tcmatch.group(1), columns, row,

-                                              section, secid, statuses,

-                                              transferred)

+             resrow = ResultRow.from_wiki_row(

+                 tcmatch.group(1), columns, row, section, secid, statuses, transferred

+             )

              if resrow.results:

                  resultrows.append(resrow)

      return resultrows

  

  

- class Result(object):

+ class Result:

      """A class that represents a single test result. Note that a

      'none' result, as you get if you just instantiate this class

      without arguments, is a thing, at least for wikitcms; when text
@@ -188,16 +193,18 @@ 

  

      Methods that parse existing results will use one of the class

      methods that returns a Result() with the appropriate attributes.

-     When one of those parsers produces an instance it will set the

-     attribute origtext to record the exact string parsed to produce

-     the instance.

+     When one of those parsers produces an instance it will pass

+     origtext to record the exact string parsed to produce the instance;

+     otherwise origtext should be left blank.

  

      transferred, if True, indicates the result is of the "previous

      (compose) run" type that is used to indicate where we think a

      result from a previous compose is valid for a later one.

      """

-     def __init__(

-             self, status=None, user=None, bugs=None, comment='', bot=False):

+ 

+     # pylint: disable=too-many-instance-attributes

+     def __init__(self, status=None, user=None, bugs=None, comment="", bot=False, origtext=""):

+         # pylint: disable=too-many-arguments

          self.status = status

          self.user = user

          self.bugs = bugs
@@ -206,33 +213,34 @@ 

          self.comment = comment

          self.bot = bot

          self.transferred = False

-         self.comment_bugs = helpers.find_bugs(self.comment)

+         self.comment_bugs = wikitcms.helpers.find_bugs(self.comment)

+         self.origtext = origtext

  

      def __str__(self):

          if not self.status:

              return "Result placeholder - {{result|none}}"

          if self.bot:

-             bot = 'BOT '

+             bot = "BOT "

          else:

-             bot = ''

-         status = 'Result: ' + self.status.capitalize()

+             bot = ""

+         status = "Result: " + self.status.capitalize()

          if self.transferred:

-             user = ' transferred: ' + self.user

+             user = " transferred: " + self.user

          elif self.user:

-             user = ' from ' + self.user

+             user = " from " + self.user

          else:

-             user = ''

+             user = ""

          if self.bugs:

-             bugs = ', bugs: ' + ', '.join(self.bugs)

+             bugs = ", bugs: " + ", ".join(self.bugs)

          else:

-             bugs = ''

+             bugs = ""

          if self.comment:

-             comment = ', comment: ' + self.comment

+             comment = ", comment: " + self.comment

              # Don't display ref tags

-             refpatt = re.compile(r'</?ref.*?>')

-             comment = refpatt.sub('', comment)

+             refpatt = re.compile(r"</?ref.*?>")

+             comment = refpatt.sub("", comment)

          else:

-             comment = ''

+             comment = ""

          return bot + status + user + bugs + comment

  

      @property
@@ -240,25 +248,21 @@ 

          """The {{result}} template string that would represent the

          properties of this result in a wiki page.

          """

-         bugtext = ''

+         bugtext = ""

          commtext = self.comment

-         usertext = ''

-         bottext = ''

+         usertext = ""

+         bottext = ""

          if self.status is None:

-             status = 'none'

+             status = "none"

          else:

              status = self.status

          if self.bugs:

-             bugtext = "|" + '|'.join(self.bugs)

+             bugtext = "|" + "|".join(self.bugs)

          if self.user:

              usertext = "|" + self.user

          if self.bot:

              bottext = "|bot=true"

-         tmpl = ("{{{{result|{status}{usertext}{bugtext}{bottext}}}}}"

-                 "{commtext}").format(status=status, usertext=usertext,

-                                      bugtext=bugtext, bottext=bottext,

-                                      commtext=commtext)

-         return tmpl

+         return f"{{{{result|{status}{usertext}{bugtext}{bottext}}}}}{commtext}"

  

      @classmethod

      def from_result_template(cls, string):
@@ -273,19 +277,20 @@ 

          we need to find them and extract them first. We record the

          comment exactly as is.

          """

-         template, comment = string.strip().split('}}', 1)

+         # pylint: disable=too-many-locals

+         template, comment = string.strip().split("}}", 1)

          comment = comment.strip()

-         template = template.lstrip('{')

-         params = template.split('|')

+         template = template.lstrip("{")

+         params = template.split("|")

          namedpars = dict()

          bot = False

  

          for param in params:

-             if '=' in param:

-                 (par, val) = param.split('=', 1)

+             if "=" in param:

+                 (par, val) = param.split("=", 1)

                  namedpars[par.strip()] = val.strip()

                  params.remove(param)

-         if 'bot' in namedpars and namedpars['bot']:

+         if "bot" in namedpars and namedpars["bot"]:

              # This maybe doesn't do what you expect for 'bot=false',

              # but we don't handle that in Mediawiki either and we want

              # to stay consistent.
@@ -294,11 +299,11 @@ 

          # 'params' now contains only numbered params

          # Pad the non-existent parameters to make things cleaner later

          while len(params) < 3:

-             params.append('')

+             params.append("")

  

          for i, param in enumerate(params):

              params[i] = param.strip()

-             if params[i] == '':

+             if params[i] == "":

                  params[i] = None

          status, user = params[1:3]

          bugs = params[3:]
@@ -309,33 +314,32 @@ 

              bugs = [b.strip() for b in bugs if b and b.strip()]

              for i, bug in enumerate(bugs):

                  # sometimes people write 123456#c7, remove the suffix

-                 if '#' in bug:

-                     newbug = bug.split('#')[0]

+                 if "#" in bug:

+                     newbug = bug.split("#")[0]

                      if newbug.isdigit():

                          bugs[i] = newbug

  

-         res = cls(status, user, bugs, comment, bot)

-         res.origtext = string

+         res = cls(status, user, bugs, comment, bot, origtext=string)

          if user and "previous " in user:

              res.transferred = True

          return res

  

      @classmethod

      def from_testresult_template(cls, string):

-         '''Returns a Result object based on the {{testresult}} template.

+         """Returns a Result object based on the {{testresult}} template.

          This was used in Fedora 12. It looks like this:

          {{testresult/pass|FASName}} <ref>comment or bug</ref>

          The bug handling here is very special-case - it relies on the

          fact that bug IDs were always six-digit strings, at the time,

          and on the template folks used to link to bug reports - but

          should be good enough.

-         '''

-         bug_patt = re.compile(r'({{bz.*?(\d{6,6}).*?}})')

-         emptyref_patt = re.compile(r'<ref> *?</ref>')

-         template, comment = string.strip().split('}}', 1)

-         template = template.lstrip('{')

-         template = template.split('/')[1]

-         params = template.split('|')

+         """

+         bug_patt = re.compile(r"({{bz.*?(\d{6,6}).*?}})")

+         emptyref_patt = re.compile(r"<ref> *?</ref>")

+         template, comment = string.strip().split("}}", 1)

+         template = template.lstrip("{")

+         template = template.split("/")[1]

+         params = template.split("|")

          try:

              status = params[0].strip().lower()

              if status == "none":
@@ -348,114 +352,57 @@ 

              user = None

          bugs = [b[1] for b in bug_patt.findall(comment)]

          if comment:

-             comment = bug_patt.sub('', comment)

-             comment = emptyref_patt.sub('', comment)

-             if comment.replace(' ', '') == '':

-                 comment = ''

+             comment = bug_patt.sub("", comment)

+             comment = emptyref_patt.sub("", comment)

+             if comment.replace(" ", "") == "":

+                 comment = ""

              comment = comment.strip()

          else:

              pass

-         res = cls(status, user, bugs, comment)

-         res.origtext = string

+         res = cls(status, user, bugs, comment, origtext=string)

          if user and "previous " in user:

              res.transferred = True

          return res

  

-     @classmethod

-     def from_qatracker(cls, result):

-         '''Converts a result object from the QA Tracker library to a

-         wikitcms-style Result. Returns a Result instance with origres

-         as an extra property that is a pointer to the qatracker result

-         object.

-         '''

-         if result.result == 1:

-             status = 'pass'

-         elif result.result == 0:

-             status = 'fail'

-         else:

-             status = None

-         if result.reportername:

-             user = result.reportername

-         else:

-             user = None

-         if result.comment:

-             comment = result.comment

-         else:

-             comment = ''

-         # This produces an empty string if there are no bugs, a dict

-         # if there are bugs. FIXME: this is completely wrong, it's

-         # a JSON dict, we should parse it as JSON, but I can't be

-         # bothered fixing this Ubuntu stuff right now. Nothing uses

-         # it.

-         bugs = eval(result.bugs)

-         if bugs:

-             bugs = list(bugs.keys())

-         else:

-             bugs = None

-         res = cls(status, user, bugs, comment)

-         res.origres = result

-         return res

  

- class TestInstance(object):

-     """Represents the broad concept of a 'test instance': that is, in

-     any test management system, the 'basic unit' of a single test for

-     which some results are expected to be reported. In 'Wikitcms', for

-     instance, this corresponds to a single row in a results table, and

-     that is what the ResultRow() subclass represents. A subclass for

-     QATracker would represent a single test in a build of a product.

- 

-     The 'testcase' is the basic identifier of a test instance. It will

-     not necessarily be unique, though - in any test management system

-     you may find multiple test instances for the same test case (in

-     different builds and different products). The concept of the

-     name derives from Wikitcms, where it is not uncommon for a set of

-     test instances to have the same 'testcase' but a different 'name',

-     which in that system is the link text: there will a column which

-     for each row contains [[testcase|name]], the testcase being the

-     same but the name being different. The concept doesn't seem

-     entirely specific to Wiki TCMS, though, so it's represented here.

-     Commonly the 'testcase' and 'name' will be the same, when each

-     instance within a set has a different 'testcase' the name should

-     be identical to the testcase.

- 

-     milestone is, roughly, the priority of the test: milestone is

-     slightly Fedora-specific language, a hangover from early wikitcms

-     versions which didn't consider other systems. For Fedora it will

-     be Alpha, Beta or Final, usually. For Ubuntu it may be

-     'mandatory', 'optional' or possibly 'disabled'.

+ class ResultRow:

+     """Represents the 'test instance' concept for Wikitcms, where it

+     is a result row from the tables used to contain results. We think

+     of a "test instance" as the 'basic unit' of a single test for

+     which some results are expected to be reported.

+ 

+     The 'testcase' is the basic identifier of a ResultRow. It will

+     not necessarily be unique, though - it is not uncommon for a set

+     of ResultRows to have the same 'testcase' but a different 'name',

+     which is the link text: there will a column which for each row

+     contains [[testcase|name]], the testcase being the same but the

+     name being different. Commonly the 'testcase' and 'name' will be

+     the same, when each instance within a set has a different

+     'testcase' the name should be identical to the testcase.

+ 

+     milestone is, roughly, the priority of the test: it will usually

+     be Basic, Beta or Final.

  

      results is required to be a dict of lists; the dict keys represent

-     the test's environments. If the test system does not have the

-     concept of environments, the dict can have a single key with some

-     sort of generic name (like 'Results'). The values must be lists of

-     instances of wikitcms.Result or a subclass of it.

-     """

-     def __init__(self, testcase, milestone='', results=None):

-         self.testcase = testcase

-         self.name = testcase

-         self.milestone = milestone

-         if not results:

-             self.results = dict()

-         else:

-             self.results = results

- 

- 

- class ResultRow(TestInstance):

-     """Represents the 'test instance' concept for Wikitcms, where it

-     is a result row from the tables used to contain results. Some

-     Wikitcms-specific properties are encoded here. columns is the list

-     of columns in the table in which the result was found (this is

-     needed to figure out the environments for the results, as the envs

-     are represented by table columns, and to know which cell to edit

-     when modifying results). origtext is the text which was parsed to

-     produce the instance, if it was produced by the from_wiki_row()

-     class method which parses wiki text to produce instances. section

-     and secid are the wiki page section in which the table from which

-     the row came is located; though these are in a way attributes of

-     the page, this is really another case where an MW attribute is

-     just a way of encoding information about a *test*. The splitting

-     of result pages into sections is a way of sub-grouping tests in

-     each page. So it's appropriate to store those attributes here.

+     the test's environments. The values must be lists of instances of

+     wikitcms.Result or a subclass of it.

+ 

+     columns is the list of columns in the table in which the result

+     was found (this is needed to figure out the environments for the

+     results, as the envs are represented by table columns, and to know

+     which cell to edit when modifying results).

+ 

+     origtext is the text which was parsed to produce the instance, if

+     it was produced by the from_wiki_row() class method which parses

+     wiki text to produce instances.

+ 

+     section and secid are the wiki page section in which the table

+     from which the row came is located; though these are in a way

+     attributes of the page, this is really another case where an MW

+     attribute is just a way of encoding information about a *test*.

+     The splitting of result pages into sections is a way of sub-

+     grouping tests in each page. So it's appropriate to store those

+     attributes here.

  

      At present you typically get ResultRow instances by calling a

      ComposePage's get_resultrows() method, which runs its text through
@@ -464,9 +411,19 @@ 

      method. This will always provide instances with a full set of

      the above-described attributes.

      """

-     def __init__(self, testcase, columns, section='', secid=None, milestone='',

-                  origtext='', results=None):

-         super(ResultRow, self).__init__(testcase, milestone, results)

+ 

+     # pylint: disable=too-many-instance-attributes

+     def __init__(

+         self, testcase, columns, section="", secid=None, milestone="", origtext="", results=None

+     ):

+         # pylint: disable=too-many-arguments

+         self.testcase = testcase

+         self.name = testcase

+         self.milestone = milestone

+         if not results:

+             self.results = dict()

+         else:

+             self.results = results

          self.columns = columns

          self.origtext = origtext

          self.section = section
@@ -487,23 +444,24 @@ 

              ours = (self.testcase, self.name, self.secid, self.origtext)

              theirs = (other.testcase, other.name, other.secid, other.origtext)

              return ours == theirs

-         return NotImplemented

+         return False

  

      @classmethod

-     def from_wiki_row(cls, testcase, columns, text, section, secid,

-                       statuses=None, transferred=True):

+     def from_wiki_row(

+         cls, testcase, columns, text, section, secid, statuses=None, transferred=True

+     ):

          """Instantiate a ResultRow from some wikitext and some info

          that is worked out from elsewhere in the page.

          """

+         # pylint: disable=too-many-locals, too-many-arguments

          results = OrderedDict()

          # this is presumptuous, but holds up for every result page

          # tested so far; there may be some with whitespace, and

          # '| cell || cell || cell' is permitted as an alternative to

          # '| cell\n| cell\n| cell' but we do not seem to use it.

-         cells = text.split('\n|')

-         milestone = ''

-         for mile in ('Alpha', 'Basic', 'Beta', 'Final', 'Optional', 'Tier1',

-                      'Tier2', 'Tier3'):

+         cells = text.split("\n|")

+         milestone = ""

+         for mile in ("Alpha", "Basic", "Beta", "Final", "Optional", "Tier1", "Tier2", "Tier3"):

              if mile in cells[0]:

                  milestone = mile

                  # we take the *first* milestone we find, so we treat
@@ -514,14 +472,14 @@ 

                  try:

                      # see if we can find some link text for the test

                      # case, and assume it's the test's "name" if so

-                     altname = cell.strip().strip('[]').split('|')[1]

+                     altname = cell.strip().strip("[]").split("|")[1]

                      continue

                  except IndexError:

                      try:

-                         altname = cell.strip().strip('[]').split(maxsplit=1)[1]

+                         altname = cell.strip().strip("[]").split(maxsplit=1)[1]

                      except IndexError:

                          altname = None

-             if '{{result' in cell or '{{testresult' in cell:

+             if "{{result" in cell or "{{testresult" in cell:

                  # any cell containing a result string is a 'result

                  # cell', and the index of the cell in columns will be

                  # the title of the column it is in. find_results()
@@ -529,8 +487,7 @@ 

                  # out, so the results dict's keys will always

                  # represent the full set of environments for this test

                  try:

-                     results[columns[i]] = find_results(cell, statuses,

-                                                        transferred)

+                     results[columns[i]] = find_results(cell, statuses, transferred)

                  except IndexError:

                      # FIXME: log (messy table, see e.g. F15 'Multi

                      # Image')
@@ -541,26 +498,4 @@ 

          return row

  

  

- class TrackerBuildTest(TestInstance):

-     """Represents a 'result instance' from QA Tracker: this is the

-     data associated with a single testcase in a single build.

-     """

-     def __init__(self, tcname, tcid, milestone='', results=None):

-         super(TrackerBuildTest, self).__init__(tcname, milestone, results)

-         self.tcid = tcid

- 

-     @classmethod

-     def from_api(cls, testcase, build):

-         """I don't remember what the shit this does, I'm just making

-         pylint happy.

-         """

-         results = dict()

-         results['Results'] = list()

-         tcname = testcase.title

-         tcid = testcase.id

-         milestone = testcase.status_string

-         for res in build.get_results(testcase):

-             results['Results'].append(Result.from_qatracker(res))

-         return cls(tcname, tcid, milestone, results)

- 

  # vim: set textwidth=100 ts=8 et sw=4:

src/wikitcms/wiki.py wikitcms/wiki.py
file renamed
+282 -188
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2014 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -22,9 +22,6 @@ 

  user credentials.

  """

  

- from __future__ import unicode_literals

- from __future__ import print_function

- 

  from collections import namedtuple

  import datetime

  import re
@@ -33,12 +30,12 @@ 

  import mwclient

  from productmd.composeinfo import get_date_type_respin

  

- from . import page as pg

- from . import event as ev

- from . import helpers as hl

- from . import listing as li

- from . import result as rs

- from .exceptions import NoPageError, NotFoundError, TooManyError

+ import wikitcms.event

+ from wikitcms.exceptions import NoPageError, NotFoundError, TooManyError

+ import wikitcms.helpers

+ import wikitcms.listing

+ import wikitcms.page

+ import wikitcms.result

  

  try:

      from openidc_client import OpenIDCClient
@@ -49,30 +46,113 @@ 

  except ImportError:

      OpenIDCClientAuther = None

  

+ 

+ def _check_compose(compose):

+     """Trivial checker shared between get_validation_event and

+     get_validation_compose.

+     """

+     date = fedfind.helpers.date_check(compose, out="obj", fail_raise=False)

+     # all nightlies after F24 branch are Pungi 4-style; plain date

+     # is never a valid 'compose' value after that date, 2016-02-23

+     if date and date < datetime.datetime(2016, 2, 23):

+         return "date"

+ 

+     # check if we have a valid Pungi4-style identifier

+     try:

+         (date, typ, respin) = get_date_type_respin(compose)

+         if date and typ and respin is not None:

+             if fedfind.helpers.date_check(date, fail_raise=False):

+                 return "date"

+     except ValueError:

+         pass

+ 

+     # regex to match TC/RC names: TC1, RC10, RC23.6

+     patt = re.compile(r"^[TR]C\d+\.?\d*$")

+     if patt.match(compose.upper()):

+         return "compose"

+ 

+     # regex for Pungi 4 milestone composes: 1.1, 1.2 ... 10.10 ...

+     patt = re.compile(r"^\d+\.\d+$")

+     if patt.match(compose):

+         return "compose"

+ 

+     # if nothing matched, value is invalid

+     raise ValueError(

+         "Compose must be a TC/RC identifier (TC1, RC3...) for pre-"

+         "Fedora 24 milestone composes, a Pungi 4 milestone compose"

+         " identifier (1.1, 10.10...) for post-Fedora 23 milestone "

+         "composes, a date in YYYYMMDD format (for pre-Fedora 24 "

+         "nightlies) or a Pungi 4 nightly identifier (20160308.n.0"

+         ", 20160310.n.2) for post-Fedora 23 nightlies."

+     )

+ 

+ 

  # I'd really like to use namedlist.namedtuple, but it isn't widely

  # available.

- class ResTuple(namedtuple('ResTuple', 'testtype release milestone compose '

-                           'testcase section testname env status user bugs '

-                           'comment bot cid modular')):

+ class ResTuple(

+     namedtuple(

+         "ResTuple",

+         "testtype release milestone compose "

+         "testcase section testname env status user bugs "

+         "comment bot cid modular",

+     )

+ ):

      """namedtuple (with default values) used for report_validation_

      results(). See that method's docstring for details.

      """

-     def __new__(cls, testtype, release='', milestone='', compose='',

-                 testcase='', section='', testname='', env='', status='',

-                 user='', bugs='', comment='', bot=False, cid='', modular=False):

+ 

+     # pylint: disable=too-many-arguments, too-many-locals

+     def __new__(

+         cls,

+         testtype,

+         release="",

+         milestone="",

+         compose="",

+         testcase="",

+         section="",

+         testname="",

+         env="",

+         status="",

+         user="",

+         bugs="",

+         comment="",

+         bot=False,

+         cid="",

+         modular=False,

+     ):

          return super(ResTuple, cls).__new__(

-             cls, testtype, release, milestone, compose, testcase, section,

-             testname, env, status, user, bugs, comment, bot, cid, modular)

+             cls,

+             testtype,

+             release,

+             milestone,

+             compose,

+             testcase,

+             section,

+             testname,

+             env,

+             status,

+             user,

+             bugs,

+             comment,

+             bot,

+             cid,

+             modular,

+         )

+ 

  

  class Wiki(mwclient.Site):

      """Extends the mwclient.Site class with some extra capabilities."""

-     # parent class has a whole bunch of args, so just pass whatever through.

-     # always init this the same as a regular mwclient.Site instance.

-     def __init__(self, host='fedoraproject.org', *args, **kwargs):

+ 

+     # parent class has a whole bunch of args, so just pass whatever

+     # through. always init this the same as a regular mwclient.Site

+     # instance. we have to disable this warning as the args must be

+     # in this exact order to maintain compatibility

+     # pylint: disable=keyword-arg-before-vararg

+     def __init__(self, host="fedoraproject.org", *args, **kwargs):

          super(Wiki, self).__init__(host, *args, **kwargs)

          # override the 'pages' property so it returns wikitcms Pages when

          # appropriate

-         self.pages = li.TcmsPageList(self)

+         self.pages = wikitcms.listing.TcmsPageList(self)

  

      @property

      def current_compose(self):
@@ -83,8 +163,8 @@ 

          normally written by ValidationEvent.update_current().

          """

          currdict = dict()

-         valpatt = re.compile(r'^\| *?(\w+?) *?= *([\w .]*?) *$', re.M)

-         page = self.pages['Template:CurrentFedoraCompose']

+         valpatt = re.compile(r"^\| *?(\w+?) *?= *([\w .]*?) *$", re.M)

+         page = self.pages["Template:CurrentFedoraCompose"]

          for match in valpatt.finditer(page.text()):

              currdict[match.group(1)] = match.group(2)

          return currdict
@@ -97,8 +177,10 @@ 

          # Use of 'max' plus get_validation_event handles getting us

          # the right kind of event.

          return self.get_validation_event(

-             release=curr['release'], milestone=curr['milestone'],

-             compose=max(curr['date'], curr['compose']))

+             release=curr["release"],

+             milestone=curr["milestone"],

+             compose=max(curr["date"], curr["compose"]),

+         )

  

      @property

      def current_modular_compose(self):
@@ -110,8 +192,8 @@ 

          ValidationEvent.update_current().

          """

          currdict = dict()

-         valpatt = re.compile(r'^\| *?(\w+?) *?= *([\w .]*?) *$', re.M)

-         page = self.pages['Template:CurrentFedoraModularCompose']

+         valpatt = re.compile(r"^\| *?(\w+?) *?= *([\w .]*?) *$", re.M)

+         page = self.pages["Template:CurrentFedoraModularCompose"]

          for match in valpatt.finditer(page.text()):

              currdict[match.group(1)] = match.group(2)

          return currdict
@@ -124,8 +206,11 @@ 

          # Use of 'max' plus get_validation_event handles getting us

          # the right kind of event.

          return self.get_validation_event(

-             release=curr['release'], milestone=curr['milestone'],

-             compose=max(curr['date'], curr['compose']), modular=True)

+             release=curr["release"],

+             milestone=curr["milestone"],

+             compose=max(curr["date"], curr["compose"]),

+             modular=True,

+         )

  

      @property

      def matrices(self):
@@ -137,7 +222,7 @@ 

          the member pages, whereas the other does not. The sort order is

          used in creating the overview summary page.

          """

-         category = self.pages['Category:QA test matrix templates']

+         category = self.pages["Category:QA test matrix templates"]

          return category.members(generator=False)

  

      @property
@@ -145,8 +230,9 @@ 

          """Test types, derived from the matrix page names according to

          a naming convention. A list of strings.

          """

-         return [m['title'].replace('Template:', '')

-                 .replace(' test matrix', '') for m in self.matrices]

+         return [

+             m["title"].replace("Template:", "").replace(" test matrix", "") for m in self.matrices

+         ]

  

      @property

      def modular_matrices(self):
@@ -158,7 +244,7 @@ 

          the member pages, whereas the other does not. The sort order is

          used in creating the overview summary page.

          """

-         category = self.pages['Category:QA modular test matrix templates']

+         category = self.pages["Category:QA modular test matrix templates"]

          return category.members(generator=False)

  

      @property
@@ -166,10 +252,12 @@ 

          """Test types, derived from the matrix page names according to

          a naming convention. A list of strings.

          """

-         return [m['title'].replace('Template:', '')

-                 .replace(' modular test matrix', '') for m in self.modular_matrices]

+         return [

+             m["title"].replace("Template:", "").replace(" modular test matrix", "")

+             for m in self.modular_matrices

+         ]

  

-     def login(self, *args, **kwargs):

+     def login(self, *args, **kwargs):  # pylint: disable=signature-differs

          """Login method, overridden to use openidc auth when necessary.

          This will open a browser window and run through FAS auth on

          the first use, then a token will be saved that will allow auth
@@ -182,11 +270,11 @@ 

          host = self.host

          if isinstance(host, (list, tuple)):

              host = host[1]

-         if host.endswith('fedoraproject.org') and self.version[:2] >= (1, 29):

+         if host.endswith("fedoraproject.org") and self.version[:2] >= (1, 29):

              use_openidc = True

          if not use_openidc:

              # just work like mwclient

-             return super(Wiki, self).login(*args, **kwargs)

+             super(Wiki, self).login(*args, **kwargs)

  

          # Fedora wiki since upgrade to 1.29 doesn't allow native

          # mediawiki auth with FAS creds any more, it only allows
@@ -194,26 +282,24 @@ 

          # openidc auther, call site_init() to trigger auth and

          # update the site properties, and return.

          if OpenIDCClient is None:

-             raise ImportError('python-openidc-client is needed for OIDC')

+             raise ImportError("python-openidc-client is needed for OIDC")

          if OpenIDCClientAuther is None:

-             raise ImportError('python-openidc-client 0.4.0 or higher is '

-                               'required for OIDC')

+             raise ImportError("python-openidc-client 0.4.0 or higher is " "required for OIDC")

          client = OpenIDCClient(

-             app_identifier='wikitcms',

-             id_provider='https://id.{0}/openidc/'.format(host),

-             id_provider_mapping={'Token': 'Token',

-                                  'Authorization': 'Authorization'},

-             client_id='wikitcms',

-             client_secret='notsecret',

-             useragent='wikitcms')

+             app_identifier="wikitcms",

+             id_provider=f"https://id.{host}/openidc/",

+             id_provider_mapping={"Token": "Token", "Authorization": "Authorization"},

+             client_id="wikitcms",

+             client_secret="notsecret",

+             useragent="wikitcms",

+         )

  

-         auther = OpenIDCClientAuther(client,

-                                      ['openid', 'https://fedoraproject.org/wiki/api'])

+         auther = OpenIDCClientAuther(client, ["openid", "https://fedoraproject.org/wiki/api"])

  

          self.connection.auth = auther

          self.site_init()

  

-     def add_to_category(self, page_name, category_name, summary=''):

+     def add_to_category(self, page_name, category_name, summary=""):

          """Add a given page to a given category if it is not already a

          member. Takes strings for the names of the page and the

          category, not mwclient objects.
@@ -221,7 +307,7 @@ 

          page = self.pages[page_name]

          text = page.text()

          if category_name not in text:

-             text += "\n[[{0}]]".format(category_name)

+             text += f"\n[[{category_name}]]"

              page.save(text, summary, createonly=False)

  

      def walk_category(self, category):
@@ -241,63 +327,33 @@ 

          pages = pages.values()

          return pages

  

-     def allresults(self, prefix=None, start=None, redirects='all', end=None):

+     def allresults(self, prefix=None, start=None, redirects="all", end=None):

          """A generator for pages in the Test Results: namespace,

          similar to mwclient's allpages, allcategories etc. generators.

          This is a TcmsPageList, so it returns wikitcms objects when

          appropriate. Note, if passing prefix, start or end, leave out

          the "Test Results:" part of the name.

          """

-         gen = li.TcmsPageList(self, prefix=prefix, start=start,

-                               namespace=116, redirects=redirects, end=end)

+         gen = wikitcms.listing.TcmsPageList(

+             self, prefix=prefix, start=start, namespace=116, redirects=redirects, end=end

+         )

          return gen

  

-     def alltestdays(self, prefix=None, start=None, redirects='all', end=None):

+     def alltestdays(self, prefix=None, start=None, redirects="all", end=None):

          """A generator for pages in the Test Day: namespace,

          similar to mwclient's allpages, allcategories etc. generators.

          This is a TcmsPageList, so it returns wikitcms objects when

          appropriate. Note, if passing prefix, start or end, leave out

          the "Test Day:" part of the name.

          """

-         gen = li.TcmsPageList(self, prefix=prefix, start=start,

-                               namespace=114, redirects=redirects, end=end)

+         gen = wikitcms.listing.TcmsPageList(

+             self, prefix=prefix, start=start, namespace=114, redirects=redirects, end=end

+         )

          return gen

  

-     def _check_compose(self, compose):

-         """Trivial checker shared between following two methods."""

-         date = fedfind.helpers.date_check(compose, out='obj', fail_raise=False)

-         # all nightlies after F24 branch are Pungi 4-style; plain date

-         # is never a valid 'compose' value after that date, 2016-02-23

-         if date and date < datetime.datetime(2016, 2, 23):

-             return 'date'

-         else:

-             # check if we have a valid Pungi4-style identifier

-             try:

-                 (date, typ, respin) = get_date_type_respin(compose)

-                 if date and typ and respin is not None:

-                     if fedfind.helpers.date_check(date, fail_raise=False):

-                         return 'date'

-             except ValueError:

-                 pass

-             # regex to match TC/RC names: TC1, RC10, RC23.6

-             patt = re.compile(r'[TR]C\d+\.?\d*')

-             if patt.match(compose.upper()):

-                 return 'compose'

-             # regex for Pungi 4 milestone composes: 1.1, 1.2 ... 10.10 ...

-             patt = re.compile(r'\d+\.\d+')

-             if patt.match(compose):

-                 return 'compose'

-             else:

-                 raise ValueError(

-                     "Compose must be a TC/RC identifier (TC1, RC3...) for pre-"

-                     "Fedora 24 milestone composes, a Pungi 4 milestone compose"

-                     " identifier (1.1, 10.10...) for post-Fedora 23 milestone "

-                     "composes, a date in YYYYMMDD format (for pre-Fedora 24 "

-                     "nightlies) or a Pungi 4 nightly identifier (20160308.n.0"

-                     ", 20160310.n.2) for post-Fedora 23 nightlies.")

- 

-     def get_validation_event(self, release='', milestone='', compose='',

-                              cid='', modular=False):

+     def get_validation_event(self, release="", milestone="", compose="", cid="", modular=False):

+         # we could factor this out but I think it's fine

+         # pylint: disable=too-many-arguments, too-many-branches

          """Get an appropriate ValidationEvent object for the values

          given. As with get_validation_page(), this method is for

          sloppy instantiation of pages that follow the rules. This
@@ -342,8 +398,8 @@ 

          method with sufficient information to avoid guessing.

          """

          if cid:

-             (dist, release, milestone, compose) = hl.cid_to_event(cid)

-             modular = bool(dist == 'Fedora-Modular')

+             (dist, release, milestone, compose) = wikitcms.helpers.cid_to_event(cid)

+             modular = bool(dist == "Fedora-Modular")

          if not compose or not release:

              # Can't really make an educated guess without a compose

              # and release, so just get the current event and return it
@@ -354,60 +410,66 @@ 

                  event = self.current_event

              if release and event.release != release:

                  raise ValueError(

-                     "get_validation_event(): Guessed event release {0} does "

-                     "not match requested release {1}".format(

-                         event.release, release))

+                     f"get_validation_event(): Guessed event release {event.release} does "

+                     f"not match requested release {release}"

+                 )

              if milestone and event.milestone != milestone:

                  raise ValueError(

-                     "get_validation_event(): Guessed event milestone {0} "

-                     "does not match specified milestone {1}".format(

-                         event.milestone, milestone))

+                     f"get_validation_event(): Guessed event milestone {event.milestone} "

+                     "does not match specified milestone {milestone}"

+                 )

              # all checks OK

              return event

  

-         if self._check_compose(compose) == 'date':

+         if _check_compose(compose) == "date":

              if milestone:

-                 return ev.NightlyEvent(

-                     self, release=release, milestone=milestone,

-                     compose=compose, modular=modular)

-             else:

-                 # we have a date and no milestone. Try both and return

-                 # whichever exists. We check whether the first result

-                 # page has any contents so that if someone mistakenly

-                 # creates the wrong event, we can clean up by blanking

-                 # the pages, rather than by getting an admin to

-                 # actually *delete* them.

-                 rawev = ev.NightlyEvent(self, release, 'Rawhide', compose, modular=modular)

-                 pgs = rawev.result_pages

-                 if pgs and pgs[0].text():

-                     return rawev

-                 brev = ev.NightlyEvent(self, release, 'Branched', compose, modular=modular)

-                 pgs = brev.result_pages

-                 if pgs and pgs[0].text():

-                     return brev

-                 # Here, we failed to guess. Boohoo.

-                 raise ValueError(

-                     "get_validation_event(): Could not find any event for "

-                     "release {0} and date {1}.".format(release, compose))

+                 return wikitcms.event.NightlyEvent(

+                     self, release=release, milestone=milestone, compose=compose, modular=modular

+                 )

+ 

+             # we have a date and no milestone. Try both and return

+             # whichever exists. We check whether the first result

+             # page has any contents so that if someone mistakenly

+             # creates the wrong event, we can clean up by blanking

+             # the pages, rather than by getting an admin to

+             # actually *delete* them.

+             rawev = wikitcms.event.NightlyEvent(self, release, "Rawhide", compose, modular=modular)

+             pgs = rawev.result_pages

+             if pgs and pgs[0].text():

+                 return rawev

+             brev = wikitcms.event.NightlyEvent(self, release, "Branched", compose, modular=modular)

+             pgs = brev.result_pages

+             if pgs and pgs[0].text():

+                 return brev

+             # Here, we failed to guess. Boohoo.

+             raise ValueError(

+                 "get_validation_event(): Could not find any event for "

+                 f"release {release} and date {compose}."

+             )

  

-         elif self._check_compose(compose) == 'compose':

+         if _check_compose(compose) == "compose":

              compose = str(compose).upper()

              if not milestone:

                  raise ValueError(

                      "get_validation_event(): For a TC/RC compose, a milestone "

-                     "- Alpha, Beta, or Final - must be specified.")

+                     "- Alpha, Beta, or Final - must be specified."

+                 )

              # With Pungi 4, the 'Final' milestone became 'RC', let's

              # be nice and convert it

-             if int(release) > 23 and milestone.lower() == 'final':

-                 milestone = 'RC'

-             return ev.ComposeEvent(self, release, milestone, compose, modular=modular, cid=cid)

-         else:

-             # We should never get here, but just in case.

-             raise ValueError(

-                 "get_validation_event(): Something very strange happened.")

- 

-     def get_validation_page(self, testtype, release='', milestone='',

-                             compose='', cid='', modular=False):

+             if int(release) > 23 and milestone.lower() == "final":

+                 milestone = "RC"

+             return wikitcms.event.ComposeEvent(

+                 self, release, milestone, compose, modular=modular, cid=cid

+             )

+ 

+         # We should never get here, but just in case.

+         raise ValueError("get_validation_event(): Something very strange happened.")

+ 

+     def get_validation_page(

+         self, testtype, release="", milestone="", compose="", cid="", modular=False

+     ):

+         # we could factor this out but I think it's fine

+         # pylint: disable=too-many-arguments

          """Get an appropriate ValidationPage object for the values

          given. As with get_validation_event(), this method is for

          sloppy instantiation of pages that follow the rules. This
@@ -453,63 +515,74 @@ 

          method with sufficient information to avoid guessing.

          """

          if cid:

-             (dist, release, milestone, compose) = hl.cid_to_event(cid)

-             modular = bool(dist == 'Fedora-Modular')

+             (dist, release, milestone, compose) = wikitcms.helpers.cid_to_event(cid)

+             modular = bool(dist == "Fedora-Modular")

          if not compose or not release:

              # Can't really make an educated guess without a compose

              # and release, so just get the current event and return it

              # if it matches any other values passed.

              curr = self.current_compose

              page = self.get_validation_page(

-                 testtype, release=curr['release'], milestone=curr['milestone'],

-                 compose=max(curr['compose'], curr['date']), modular=modular)

+                 testtype,

+                 release=curr["release"],

+                 milestone=curr["milestone"],

+                 compose=max(curr["compose"], curr["date"]),

+                 modular=modular,

+             )

              if release and page.release != release:

                  raise ValueError(

-                     "get_validation_page(): Guessed page release {0} does "

-                     "not match requested release {1}".format(

-                         page.release, release))

+                     f"get_validation_page(): Guessed page release {page.release} does "

+                     f"not match requested release {release}"

+                 )

              if milestone and page.milestone != milestone:

                  raise ValueError(

-                     "get_validation_page(): Guessed page milestone {0} "

-                     "does not match specified milestone {1}".format(

-                         page.milestone, milestone))

+                     f"get_validation_page(): Guessed page milestone {page.milestone} "

+                     f"does not match specified milestone {milestone}"

+                 )

              return page

  

-         if self._check_compose(compose) == 'date':

+         if _check_compose(compose) == "date":

              if milestone:

-                 return pg.NightlyPage(

-                     self, release, testtype, milestone, compose, modular=modular)

-             else:

-                 rawpg = pg.NightlyPage(

-                     self, release, testtype, 'Rawhide', compose, modular=modular)

-                 if rawpg.exists:

-                     return rawpg

-                 brpg = pg.NightlyPage(

-                     self, release, testtype, 'Branched', compose, modular=modular)

-                 if brpg.exists:

-                     return brpg

-                 # Here, we failed to guess. Boohoo.

-                 raise ValueError(

-                     "get_validation_page(): Could not find any event for "

-                     "release {0} and date {1}.".format(release, compose))

+                 return wikitcms.page.NightlyPage(

+                     self, release, testtype, milestone, compose, modular=modular

+                 )

+ 

+             # date, but no milestone

+             rawpg = wikitcms.page.NightlyPage(

+                 self, release, testtype, "Rawhide", compose, modular=modular

+             )

+             if rawpg.exists:

+                 return rawpg

+             brpg = wikitcms.page.NightlyPage(

+                 self, release, testtype, "Branched", compose, modular=modular

+             )

+             if brpg.exists:

+                 return brpg

+             # Here, we failed to guess. Boohoo.

+             raise ValueError(

+                 "get_validation_page(): Could not find any event for "

+                 f"release {release} and date {compose}."

+             )

  

-         elif self._check_compose(compose) == 'compose':

+         if _check_compose(compose) == "compose":

              if not milestone:

                  raise ValueError(

                      "get_validation_page(): For a milestone compose, a "

-                     " milestone - Alpha, Beta, or Final - must be specified.")

+                     " milestone - Alpha, Beta, or Final - must be specified."

+                 )

              # With Pungi 4, the 'Final' milestone became 'RC', let's

              # be nice and convert it

-             if int(release) > 23 and milestone.lower() == 'final':

-                 milestone = 'RC'

-             return pg.ComposePage(

-                 self, release, testtype, milestone, compose, modular=modular)

-         else:

-             # We should never get here, but just in case.

-             raise ValueError(

-                 "get_validation_page(): Something very strange happened.")

+             if int(release) > 23 and milestone.lower() == "final":

+                 milestone = "RC"

+             return wikitcms.page.ComposePage(

+                 self, release, testtype, milestone, compose, modular=modular

+             )

+ 

+         # We should never get here, but just in case.

+         raise ValueError("get_validation_page(): Something very strange happened.")

  

      def report_validation_results(self, reslist, allowdupe=False):

+         # pylint: disable=too-many-locals,too-many-branches

          """High-level result reporting function. Pass it an iterable

          of objects identifying results. It's pretty forgiving about

          what these can be. They can be any kind of sequence or
@@ -570,7 +643,7 @@ 

          constructs a result dict to pass to the ValidationPage

          add_results() method.

          """

-         pagedict = dict() # KEY: (testtype, release, milestone, compose, cid, modular)

+         pagedict = dict()  # KEY: (testtype, release, milestone, compose, cid, modular)

          insufficients = list()

          dupes = list()

  
@@ -590,8 +663,14 @@ 

                  # If no username was given, guess at the wiki account

                  # name, lower-cased.

                  user = self.username.lower()

-             key = (restup.testtype, restup.release, restup.milestone,

-                    restup.compose, restup.cid, restup.modular)

+             key = (

+                 restup.testtype,

+                 restup.release,

+                 restup.milestone,

+                 restup.compose,

+                 restup.cid,

+                 restup.modular,

+             )

              # We construct a dict to sort the results by page. The

              # value for each page is a 2-tuple containing the actual

              # ValidationPage object and the 'results dictionary' we
@@ -636,11 +715,11 @@ 

              (page, resdict) = pagedict[key]

              try:

                  myrow = page.find_resultrow(

-                     restup.testcase, restup.section, restup.testname,

-                     restup.env)

-                 myres = rs.Result(

-                     restup.status, user, restup.bugs,

-                     restup.comment, restup.bot)

+                     restup.testcase, restup.section, restup.testname, restup.env

+                 )

+                 myres = wikitcms.result.Result(

+                     restup.status, user, restup.bugs, restup.comment, restup.bot

+                 )

              except (NoPageError, NotFoundError, TooManyError):

                  # We couldn't find precisely one result row from the

                  # provided information.
@@ -664,14 +743,29 @@ 

          # and dupes.

          for (page, resdict) in pagedict.values():

              if page:

-                 _dupes = (page.add_results(resdict, allowdupe))

+                 _dupes = page.add_results(resdict, allowdupe)

                  for (row, env, result) in _dupes:

-                     dupes.append(ResTuple(

-                         page.testtype, page.release, page.milestone,

-                         page.compose, row.testcase, row.section, row.name,

-                         env, result.status, result.user, result.bugs,

-                         result.comment, result.bot, '', page.modular))

+                     dupes.append(

+                         ResTuple(

+                             page.testtype,

+                             page.release,

+                             page.milestone,

+                             page.compose,

+                             row.testcase,

+                             row.section,

+                             row.name,

+                             env,

+                             result.status,

+                             result.user,

+                             result.bugs,

+                             result.comment,

+                             result.bot,

+                             "",

+                             page.modular,

+                         )

+                     )

  

          return (insufficients, dupes)

  

+ 

  # vim: set textwidth=100 ts=8 et sw=4:

file modified
+1 -1
@@ -1,2 +1,2 @@ 

  pytest

- mock

+ openidc_client

file added
+118
@@ -0,0 +1,118 @@ 

+ # Copyright (C) 2020 Red Hat

+ #

+ # This file is part of python-wikitcms.

+ #

+ # python-wikitcms is free software; you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation, either version 3 of the License, or

+ # (at your option) any later version.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.

+ #

+ # Author: Adam Williamson <awilliam@redhat.com>

+ 

+ # these are all kinda inappropriate for pytest patterns

+ # pylint: disable=no-init, protected-access, no-self-use, unused-argument

+ # pylint: disable=invalid-name

+ 

+ """Test fixtures, etc."""

+ 

+ from __future__ import unicode_literals

+ from __future__ import print_function

+ 

+ import json

+ import os

+ from unittest import mock

+ 

+ import pytest

+ 

+ DATAPATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data")

+ 

+ 

+ @pytest.fixture

+ def fakemwp(request):

+     """Fixture that mocks out mwclient.page.Page.__init__ as long

+     as it's active.

+     """

+ 

+     def fakemwpinit(self, site, name, *args, **kwargs):

+         """Stub init for mwclient.Page, we can't just mock it out as we

+         need to set site and name (and for category generator testing,

+         _info, though it can be None).

+         """

+         self.site = site

+         # this is for testing TcmsGeneratorList's capability to handle

+         # getting a page by its id

+         if name == 80736:

+             name = "Test Day:2019-12-09 Kernel 5.4 Test Week"

+         self.name = name

+         self._info = None

+ 

+     with mock.patch("mwclient.page.Page.__init__", fakemwpinit):

+         yield

+ 

+ 

+ @pytest.fixture

+ def fakeapisections(request):

+     """Fixture that mocks out the API to give a response that's

+     appropriate for a 'sections' call on a test result page, and

+     yields the mock for modifications. Based on the real response for

+     Test Results:Fedora 32 Branched 20200322.n.0 Server on 2020-03-25.

+     """

+     sectpath = os.path.join(

+         DATAPATH, "Test_Results:Fedora_32_Branched_20200322.n.0_Server.sections.json"

+     )

+     with open(sectpath, "r") as sectfh:

+         sectjson = json.load(sectfh)

+     with mock.patch("wikitcms.wiki.Wiki.api", autospec=True) as fakeapi:

+         fakeapi.return_value = {"parse": {"sections": sectjson}}

+         yield fakeapi

+ 

+ 

+ @pytest.fixture

+ def fakepages(request):

+     """Fixture that mocks out various Page attributes, methods etc.

+     with return values read from data files based on the page name.

+     The backing files contain real data from the real wiki.

+     """

+ 

+     def faketext(self, section=None, expandtemplates=False, cache=True, slot="main"):

+         textpath = os.path.join(DATAPATH, self.name.replace(" ", "_")) + ".txt"

+         with open(textpath, "r") as textfh:

+             text = textfh.read()

+         return text

+ 

+     @property

+     def fakesections(self):

+         sectpath = os.path.join(DATAPATH, self.name.replace(" ", "_")) + ".sections.json"

+         with open(sectpath, "r") as sectfh:

+             sectjson = json.load(sectfh)

+         return sectjson

+ 

+     with mock.patch("mwclient.page.Page.text", faketext):

+         with mock.patch("wikitcms.page.Page.sections", fakesections):

+             yield

+ 

+ 

+ @pytest.fixture

+ def fakeimages(request):

+     """Fixture that mocks out fedfind all_images with an appropriate

+     value (from backing data) for the compose. Used when we need an

+     event to have authentic image data.

+     """

+ 

+     @property

+     def fakeallimages(self):

+         imagespath = os.path.join(DATAPATH, self.cid) + ".images.json"

+         with open(imagespath, "r") as imagesfh:

+             imagesjson = json.load(imagesfh)

+         return imagesjson

+ 

+     with mock.patch("fedfind.release.Release.all_images", fakeallimages):

+         yield

@@ -0,0 +1,815 @@ 

+ {

+     "pages": 2,

+     "raw_messages": [

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-west-2",

+                 "extra": {

+                     "id": "ami-02e2b58b7cc0bb9a0",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-northeast-1",

+                 "extra": {

+                     "id": "ami-0e2d0ec7efa1b8ab3",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-central-1",

+                 "extra": {

+                     "id": "ami-0cbc0cb681225a2d1",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-west-1",

+                 "extra": {

+                     "id": "ami-0d764508c381285a6",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-west-2",

+                 "extra": {

+                     "id": "ami-09b5db3c568a73c2d",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-southeast-2",

+                 "extra": {

+                     "id": "ami-0dfdfaafec7792bfd",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ca-central-1",

+                 "extra": {

+                     "id": "ami-02ef0473035f36b99",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-southeast-1",

+                 "extra": {

+                     "id": "ami-06f833d5fa0c15197",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "sa-east-1",

+                 "extra": {

+                     "id": "ami-0491c2c92be47fdec",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-east-2",

+                 "extra": {

+                     "id": "ami-04a772681cfd0d769",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-northeast-2",

+                 "extra": {

+                     "id": "ami-062f10c8b8294e994",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-west-1",

+                 "extra": {

+                     "id": "ami-04eecc0083c2c1588",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-south-1",

+                 "extra": {

+                     "id": "ami-00079397a56c100f1",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-west-2",

+                 "extra": {

+                     "id": "ami-0342fd715a54f6ce3",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-northeast-1",

+                 "extra": {

+                     "id": "ami-07609be171a746469",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-central-1",

+                 "extra": {

+                     "id": "ami-08c7508c66d10d4d5",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-west-1",

+                 "extra": {

+                     "id": "ami-010bdf3663382e463",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-west-2",

+                 "extra": {

+                     "id": "ami-000a5e4f1eae697df",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-southeast-2",

+                 "extra": {

+                     "id": "ami-04091daac7ae2437a",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ca-central-1",

+                 "extra": {

+                     "id": "ami-09b35f81cf57c4d26",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-southeast-1",

+                 "extra": {

+                     "id": "ami-0bdb751c1c8232d18",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "sa-east-1",

+                 "extra": {

+                     "id": "ami-09507736ba73cb278",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-east-2",

+                 "extra": {

+                     "id": "ami-02412e7cad7ab7423",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-northeast-2",

+                 "extra": {

+                     "id": "ami-09c47c63c9b5053a8",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-west-1",

+                 "extra": {

+                     "id": "ami-07531ad50e0f8f210",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-south-1",

+                 "extra": {

+                     "id": "ami-0254bbc7831ec3e4c",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-west-2",

+                 "extra": {

+                     "id": "ami-0be03c632b49517eb",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-northeast-1",

+                 "extra": {

+                     "id": "ami-0a3e5ecd1af437cf6",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-central-1",

+                 "extra": {

+                     "id": "ami-0bfe51e7af68b9980",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-west-1",

+                 "extra": {

+                     "id": "ami-036700754636cd5d6",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-west-2",

+                 "extra": {

+                     "id": "ami-0b5bffa2ebe93b6b1",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-southeast-2",

+                 "extra": {

+                     "id": "ami-06a13536a962bff9f",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ca-central-1",

+                 "extra": {

+                     "id": "ami-0500fc6a8144abc1e",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-southeast-1",

+                 "extra": {

+                     "id": "ami-0f7c110aa63ebf24f",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "sa-east-1",

+                 "extra": {

+                     "id": "ami-067b6e5526e573986",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-east-2",

+                 "extra": {

+                     "id": "ami-0d4391c189dbf5314",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-northeast-2",

+                 "extra": {

+                     "id": "ami-0b045a55b71328675",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-west-1",

+                 "extra": {

+                     "id": "ami-000b9d8c6830276dd",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-south-1",

+                 "extra": {

+                     "id": "ami-0af5597b67dca44b2",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-west-2",

+                 "extra": {

+                     "id": "ami-0fac4adf87a0b23be",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-northeast-1",

+                 "extra": {

+                     "id": "ami-053db6774d7445008",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-central-1",

+                 "extra": {

+                     "id": "ami-0ae7e51b5eacc4cfa",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-west-1",

+                 "extra": {

+                     "id": "ami-067f7c28153092394",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-west-2",

+                 "extra": {

+                     "id": "ami-07650985f321a99f3",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-southeast-2",

+                 "extra": {

+                     "id": "ami-0d5379ab9e543f8c9",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ca-central-1",

+                 "extra": {

+                     "id": "ami-039195f65eace16e5",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-southeast-1",

+                 "extra": {

+                     "id": "ami-01aa690f13cd7c4cb",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "sa-east-1",

+                 "extra": {

+                     "id": "ami-02226636bef6e4bfe",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-east-2",

+                 "extra": {

+                     "id": "ami-08387bf7d423dd979",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-northeast-2",

+                 "extra": {

+                     "id": "ami-0442ce2f15031feb4",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "eu-west-1",

+                 "extra": {

+                     "id": "ami-02e9b94502ffdd3b9",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "ap-south-1",

+                 "extra": {

+                     "id": "ami-06fc0551e1d52cef7",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-east-1",

+                 "extra": {

+                     "id": "ami-032fccb1cb4ee0530",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": "https://kojipkgs.fedoraproject.org/compose/32/Fedora-32-20200312.0/compose/Cloud/aarch64/images/Fedora-Cloud-Base-32_Beta-1.2.aarch64.raw.xz",

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-east-1",

+                 "extra": {

+                     "id": "ami-0717887b79d906fa8",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.aarch64",

+                 "image_url": "https://kojipkgs.fedoraproject.org/compose/32/Fedora-32-20200312.0/compose/Cloud/aarch64/images/Fedora-Cloud-Base-32_Beta-1.2.aarch64.raw.xz",

+                 "service": "EC2"

+             }

+         }

+     ]

+ }

@@ -0,0 +1,50 @@ 

+ {

+     "pages": 2,

+     "raw_messages": [

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-east-1",

+                 "extra": {

+                     "id": "ami-06c522b66d738d1b4",

+                     "virt_type": "hvm",

+                     "vol_type": "gp2"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": "https://kojipkgs.fedoraproject.org/compose/32/Fedora-32-20200312.0/compose/Cloud/x86_64/images/Fedora-Cloud-Base-32_Beta-1.2.x86_64.raw.xz",

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "x86_64",

+                 "compose": "Fedora-32-20200312.0",

+                 "destination": "us-east-1",

+                 "extra": {

+                     "id": "ami-0f4f76118f4ca5b59",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-32_Beta-1.2.x86_64",

+                 "image_url": "https://kojipkgs.fedoraproject.org/compose/32/Fedora-32-20200312.0/compose/Cloud/x86_64/images/Fedora-Cloud-Base-32_Beta-1.2.x86_64.raw.xz",

+                 "service": "EC2"

+             }

+         },

+         {

+             "msg": {

+                 "architecture": "arm64",

+                 "compose": "Fedora-Rawhide-20200313.n.0",

+                 "destination": "eu-west-1",

+                 "extra": {

+                     "id": "ami-001280023403e3e6b",

+                     "virt_type": "hvm",

+                     "vol_type": "standard"

+                 },

+                 "image_name": "Fedora-Cloud-Base-Rawhide-20200313.n.0.aarch64",

+                 "image_url": null,

+                 "service": "EC2"

+             }

+         }

+     ]

+ }

The added file is too large to be shown here, see it at: tests/data/Fedora-32-20200312.0.images.json
@@ -0,0 +1,256 @@ 

+ == x86_64 hvm standard AMIs ==

+ 

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ ! Region !! AMI ID !! Direct launch link

+ |-

+ | ap-northeast-1

+ | ami-053db6774d7445008

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1#LaunchInstanceWizard:ami=ami-053db6774d7445008 Launch in EC2]

+ |-

+ | ap-northeast-2

+ | ami-0442ce2f15031feb4

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-northeast-2#LaunchInstanceWizard:ami=ami-0442ce2f15031feb4 Launch in EC2]

+ |-

+ | ap-south-1

+ | ami-06fc0551e1d52cef7

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-south-1#LaunchInstanceWizard:ami=ami-06fc0551e1d52cef7 Launch in EC2]

+ |-

+ | ap-southeast-1

+ | ami-01aa690f13cd7c4cb

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-southeast-1#LaunchInstanceWizard:ami=ami-01aa690f13cd7c4cb Launch in EC2]

+ |-

+ | ap-southeast-2

+ | ami-0d5379ab9e543f8c9

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-southeast-2#LaunchInstanceWizard:ami=ami-0d5379ab9e543f8c9 Launch in EC2]

+ |-

+ | ca-central-1

+ | ami-039195f65eace16e5

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ca-central-1#LaunchInstanceWizard:ami=ami-039195f65eace16e5 Launch in EC2]

+ |-

+ | eu-central-1

+ | ami-0ae7e51b5eacc4cfa

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-central-1#LaunchInstanceWizard:ami=ami-0ae7e51b5eacc4cfa Launch in EC2]

+ |-

+ | eu-west-1

+ | ami-02e9b94502ffdd3b9

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-west-1#LaunchInstanceWizard:ami=ami-02e9b94502ffdd3b9 Launch in EC2]

+ |-

+ | eu-west-2

+ | ami-0fac4adf87a0b23be

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-west-2#LaunchInstanceWizard:ami=ami-0fac4adf87a0b23be Launch in EC2]

+ |-

+ | sa-east-1

+ | ami-02226636bef6e4bfe

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=sa-east-1#LaunchInstanceWizard:ami=ami-02226636bef6e4bfe Launch in EC2]

+ |-

+ | us-east-1

+ | ami-0f4f76118f4ca5b59

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstanceWizard:ami=ami-0f4f76118f4ca5b59 Launch in EC2]

+ |-

+ | us-east-2

+ | ami-08387bf7d423dd979

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-east-2#LaunchInstanceWizard:ami=ami-08387bf7d423dd979 Launch in EC2]

+ |-

+ | us-west-1

+ | ami-067f7c28153092394

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-west-1#LaunchInstanceWizard:ami=ami-067f7c28153092394 Launch in EC2]

+ |-

+ | us-west-2

+ | ami-07650985f321a99f3

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-west-2#LaunchInstanceWizard:ami=ami-07650985f321a99f3 Launch in EC2]

+ |-

+ |}

+ 

+ == x86_64 hvm gp2 AMIs ==

+ 

+ {| class="wikitable sortable mw-collapsible mw-collapsed" width=100%

+ |-

+ ! Region !! AMI ID !! Direct launch link

+ |-

+ | ap-northeast-1

+ | ami-0a3e5ecd1af437cf6

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1#LaunchInstanceWizard:ami=ami-0a3e5ecd1af437cf6 Launch in EC2]

+ |-

+ | ap-northeast-2

+ | ami-0b045a55b71328675

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-northeast-2#LaunchInstanceWizard:ami=ami-0b045a55b71328675 Launch in EC2]

+ |-

+ | ap-south-1

+ | ami-0af5597b67dca44b2

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-south-1#LaunchInstanceWizard:ami=ami-0af5597b67dca44b2 Launch in EC2]

+ |-

+ | ap-southeast-1

+ | ami-0f7c110aa63ebf24f

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-southeast-1#LaunchInstanceWizard:ami=ami-0f7c110aa63ebf24f Launch in EC2]

+ |-

+ | ap-southeast-2

+ | ami-06a13536a962bff9f

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-southeast-2#LaunchInstanceWizard:ami=ami-06a13536a962bff9f Launch in EC2]

+ |-

+ | ca-central-1

+ | ami-0500fc6a8144abc1e

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ca-central-1#LaunchInstanceWizard:ami=ami-0500fc6a8144abc1e Launch in EC2]

+ |-

+ | eu-central-1

+ | ami-0bfe51e7af68b9980

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-central-1#LaunchInstanceWizard:ami=ami-0bfe51e7af68b9980 Launch in EC2]

+ |-

+ | eu-west-1

+ | ami-000b9d8c6830276dd

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-west-1#LaunchInstanceWizard:ami=ami-000b9d8c6830276dd Launch in EC2]

+ |-

+ | eu-west-2

+ | ami-0be03c632b49517eb

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-west-2#LaunchInstanceWizard:ami=ami-0be03c632b49517eb Launch in EC2]

+ |-

+ | sa-east-1

+ | ami-067b6e5526e573986

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=sa-east-1#LaunchInstanceWizard:ami=ami-067b6e5526e573986 Launch in EC2]

+ |-

+ | us-east-1

+ | ami-06c522b66d738d1b4

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstanceWizard:ami=ami-06c522b66d738d1b4 Launch in EC2]

+ |-

+ | us-east-2

+ | ami-0d4391c189dbf5314

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-east-2#LaunchInstanceWizard:ami=ami-0d4391c189dbf5314 Launch in EC2]

+ |-

+ | us-west-1

+ | ami-036700754636cd5d6

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-west-1#LaunchInstanceWizard:ami=ami-036700754636cd5d6 Launch in EC2]

+ |-

+ | us-west-2

+ | ami-0b5bffa2ebe93b6b1

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-west-2#LaunchInstanceWizard:ami=ami-0b5bffa2ebe93b6b1 Launch in EC2]

+ |-

+ |}

+ 

+ == arm64 hvm standard AMIs ==

+ 

+ {| class="wikitable sortable mw-collapsible mw-collapsed" width=100%

+ |-

+ ! Region !! AMI ID !! Direct launch link

+ |-

+ | ap-northeast-1

+ | ami-07609be171a746469

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1#LaunchInstanceWizard:ami=ami-07609be171a746469 Launch in EC2]

+ |-

+ | ap-northeast-2

+ | ami-09c47c63c9b5053a8

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-northeast-2#LaunchInstanceWizard:ami=ami-09c47c63c9b5053a8 Launch in EC2]

+ |-

+ | ap-south-1

+ | ami-0254bbc7831ec3e4c

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-south-1#LaunchInstanceWizard:ami=ami-0254bbc7831ec3e4c Launch in EC2]

+ |-

+ | ap-southeast-1

+ | ami-0bdb751c1c8232d18

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-southeast-1#LaunchInstanceWizard:ami=ami-0bdb751c1c8232d18 Launch in EC2]

+ |-

+ | ap-southeast-2

+ | ami-04091daac7ae2437a

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-southeast-2#LaunchInstanceWizard:ami=ami-04091daac7ae2437a Launch in EC2]

+ |-

+ | ca-central-1

+ | ami-09b35f81cf57c4d26

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ca-central-1#LaunchInstanceWizard:ami=ami-09b35f81cf57c4d26 Launch in EC2]

+ |-

+ | eu-central-1

+ | ami-08c7508c66d10d4d5

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-central-1#LaunchInstanceWizard:ami=ami-08c7508c66d10d4d5 Launch in EC2]

+ |-

+ | eu-west-1

+ | ami-07531ad50e0f8f210

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-west-1#LaunchInstanceWizard:ami=ami-07531ad50e0f8f210 Launch in EC2]

+ |-

+ | eu-west-2

+ | ami-0342fd715a54f6ce3

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-west-2#LaunchInstanceWizard:ami=ami-0342fd715a54f6ce3 Launch in EC2]

+ |-

+ | sa-east-1

+ | ami-09507736ba73cb278

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=sa-east-1#LaunchInstanceWizard:ami=ami-09507736ba73cb278 Launch in EC2]

+ |-

+ | us-east-1

+ | ami-0717887b79d906fa8

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstanceWizard:ami=ami-0717887b79d906fa8 Launch in EC2]

+ |-

+ | us-east-2

+ | ami-02412e7cad7ab7423

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-east-2#LaunchInstanceWizard:ami=ami-02412e7cad7ab7423 Launch in EC2]

+ |-

+ | us-west-1

+ | ami-010bdf3663382e463

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-west-1#LaunchInstanceWizard:ami=ami-010bdf3663382e463 Launch in EC2]

+ |-

+ | us-west-2

+ | ami-000a5e4f1eae697df

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-west-2#LaunchInstanceWizard:ami=ami-000a5e4f1eae697df Launch in EC2]

+ |-

+ |}

+ 

+ == arm64 hvm gp2 AMIs ==

+ 

+ {| class="wikitable sortable mw-collapsible mw-collapsed" width=100%

+ |-

+ ! Region !! AMI ID !! Direct launch link

+ |-

+ | ap-northeast-1

+ | ami-0e2d0ec7efa1b8ab3

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1#LaunchInstanceWizard:ami=ami-0e2d0ec7efa1b8ab3 Launch in EC2]

+ |-

+ | ap-northeast-2

+ | ami-062f10c8b8294e994

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-northeast-2#LaunchInstanceWizard:ami=ami-062f10c8b8294e994 Launch in EC2]

+ |-

+ | ap-south-1

+ | ami-00079397a56c100f1

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-south-1#LaunchInstanceWizard:ami=ami-00079397a56c100f1 Launch in EC2]

+ |-

+ | ap-southeast-1

+ | ami-06f833d5fa0c15197

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-southeast-1#LaunchInstanceWizard:ami=ami-06f833d5fa0c15197 Launch in EC2]

+ |-

+ | ap-southeast-2

+ | ami-0dfdfaafec7792bfd

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ap-southeast-2#LaunchInstanceWizard:ami=ami-0dfdfaafec7792bfd Launch in EC2]

+ |-

+ | ca-central-1

+ | ami-02ef0473035f36b99

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=ca-central-1#LaunchInstanceWizard:ami=ami-02ef0473035f36b99 Launch in EC2]

+ |-

+ | eu-central-1

+ | ami-0cbc0cb681225a2d1

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-central-1#LaunchInstanceWizard:ami=ami-0cbc0cb681225a2d1 Launch in EC2]

+ |-

+ | eu-west-1

+ | ami-04eecc0083c2c1588

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-west-1#LaunchInstanceWizard:ami=ami-04eecc0083c2c1588 Launch in EC2]

+ |-

+ | eu-west-2

+ | ami-02e2b58b7cc0bb9a0

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=eu-west-2#LaunchInstanceWizard:ami=ami-02e2b58b7cc0bb9a0 Launch in EC2]

+ |-

+ | sa-east-1

+ | ami-0491c2c92be47fdec

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=sa-east-1#LaunchInstanceWizard:ami=ami-0491c2c92be47fdec Launch in EC2]

+ |-

+ | us-east-1

+ | ami-032fccb1cb4ee0530

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstanceWizard:ami=ami-032fccb1cb4ee0530 Launch in EC2]

+ |-

+ | us-east-2

+ | ami-04a772681cfd0d769

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-east-2#LaunchInstanceWizard:ami=ami-04a772681cfd0d769 Launch in EC2]

+ |-

+ | us-west-1

+ | ami-0d764508c381285a6

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-west-1#LaunchInstanceWizard:ami=ami-0d764508c381285a6 Launch in EC2]

+ |-

+ | us-west-2

+ | ami-09b5db3c568a73c2d

+ | [https://redirect.fedoraproject.org/console.aws.amazon.com/ec2/v2/home?region=us-west-2#LaunchInstanceWizard:ami=ami-09b5db3c568a73c2d Launch in EC2]

+ |-

+ |}

+ 

@@ -0,0 +1,264 @@ 

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ ! Image !! aarch64 !! armhfp !! ppc64le !! s390x !! x86_64

+ |-

+ | Everything boot

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Everything/aarch64/iso/Fedora-Everything-netinst-aarch64-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Everything/armhfp/iso/Fedora-Everything-netinst-armhfp-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Everything/ppc64le/iso/Fedora-Everything-netinst-ppc64le-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Everything/s390x/iso/Fedora-Everything-netinst-s390x-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Everything/x86_64/iso/Fedora-Everything-netinst-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Workstation live

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Workstation/ppc64le/iso/Fedora-Workstation-Live-ppc64le-32-20200322.n.0.iso Download]

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Server dvd

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/aarch64/iso/Fedora-Server-dvd-aarch64-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/armhfp/iso/Fedora-Server-dvd-armhfp-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/ppc64le/iso/Fedora-Server-dvd-ppc64le-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/s390x/iso/Fedora-Server-dvd-s390x-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/x86_64/iso/Fedora-Server-dvd-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Server boot

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/aarch64/iso/Fedora-Server-netinst-aarch64-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/armhfp/iso/Fedora-Server-netinst-armhfp-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/ppc64le/iso/Fedora-Server-netinst-ppc64le-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/s390x/iso/Fedora-Server-netinst-s390x-32-20200322.n.0.iso Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/x86_64/iso/Fedora-Server-netinst-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Cloud_Base qcow2

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/aarch64/images/Fedora-Cloud-Base-32-20200322.n.0.aarch64.qcow2 Download]

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/s390x/images/Fedora-Cloud-Base-32-20200322.n.0.s390x.qcow2 Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/x86_64/images/Fedora-Cloud-Base-32-20200322.n.0.x86_64.qcow2 Download]

+ |-

+ | Cloud_Base raw-xz

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/aarch64/images/Fedora-Cloud-Base-32-20200322.n.0.aarch64.raw.xz Download]

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/s390x/images/Fedora-Cloud-Base-32-20200322.n.0.s390x.raw.xz Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/x86_64/images/Fedora-Cloud-Base-32-20200322.n.0.x86_64.raw.xz Download]

+ |-

+ | Cloud_Base vmdk

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/aarch64/images/Fedora-Cloud-Base-32-20200322.n.0.aarch64.vmdk Download]

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/s390x/images/Fedora-Cloud-Base-32-20200322.n.0.s390x.vmdk Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/x86_64/images/Fedora-Cloud-Base-32-20200322.n.0.x86_64.vmdk Download]

+ |-

+ | Cloud_Base vagrant-libvirt

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/x86_64/images/Fedora-Cloud-Base-Vagrant-32-20200322.n.0.x86_64.vagrant-libvirt.box Download]

+ |-

+ | Cloud_Base vagrant-virtualbox

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Cloud/x86_64/images/Fedora-Cloud-Base-Vagrant-32-20200322.n.0.x86_64.vagrant-virtualbox.box Download]

+ |-

+ | KDE live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/x86_64/iso/Fedora-KDE-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Xfce live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/x86_64/iso/Fedora-Xfce-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | SoaS live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/x86_64/iso/Fedora-SoaS-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Mate live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/x86_64/iso/Fedora-MATE_Compiz-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Cinnamon live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/x86_64/iso/Fedora-Cinnamon-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | LXDE live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/x86_64/iso/Fedora-LXDE-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Container_Base docker

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Container/aarch64/images/Fedora-Container-Base-32-20200322.n.0.aarch64.tar.xz Download]

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Container/s390x/images/Fedora-Container-Base-32-20200322.n.0.s390x.tar.xz Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Container/x86_64/images/Fedora-Container-Base-32-20200322.n.0.x86_64.tar.xz Download]

+ |-

+ | Container_Minimal_Base docker

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Container/aarch64/images/Fedora-Container-Minimal-Base-32-20200322.n.0.aarch64.tar.xz Download]

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Container/ppc64le/images/Fedora-Container-Minimal-Base-32-20200322.n.0.ppc64le.tar.xz Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Container/s390x/images/Fedora-Container-Minimal-Base-32-20200322.n.0.s390x.tar.xz Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Container/x86_64/images/Fedora-Container-Minimal-Base-32-20200322.n.0.x86_64.tar.xz Download]

+ |-

+ | Python_Classroom vagrant-libvirt

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Labs/x86_64/images/Fedora-Python-Classroom-Vagrant-32-20200322.n.0.x86_64.vagrant-libvirt.box Download]

+ |-

+ | Python_Classroom vagrant-virtualbox

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Labs/x86_64/images/Fedora-Python-Classroom-Vagrant-32-20200322.n.0.x86_64.vagrant-virtualbox.box Download]

+ |-

+ | Astronomy_KDE live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Labs/x86_64/iso/Fedora-Astronomy_KDE-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Comp_Neuro live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Labs/x86_64/iso/Fedora-Comp_Neuro-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Games live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Labs/x86_64/iso/Fedora-Games-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Jam_KDE live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Labs/x86_64/iso/Fedora-Jam_KDE-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Python_Classroom live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Labs/x86_64/iso/Fedora-Python-Classroom-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Security live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Labs/x86_64/iso/Fedora-Security-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Silverblue dvd-ostree

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Silverblue/aarch64/iso/Fedora-Silverblue-ostree-aarch64-32-20200322.n.0.iso Download]

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Silverblue/ppc64le/iso/Fedora-Silverblue-ostree-ppc64le-32-20200322.n.0.iso Download]

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Silverblue/x86_64/iso/Fedora-Silverblue-ostree-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | LXQt live

+ | 

+ | 

+ | 

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/x86_64/iso/Fedora-LXQt-Live-x86_64-32-20200322.n.0.iso Download]

+ |-

+ | Workstation raw-xz

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Workstation/aarch64/images/Fedora-Workstation-32-20200322.n.0.aarch64.raw.xz Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Workstation/armhfp/images/Fedora-Workstation-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ | Server raw-xz

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/aarch64/images/Fedora-Server-32-20200322.n.0.aarch64.raw.xz Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Server/armhfp/images/Fedora-Server-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ | KDE raw-xz

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/armhfp/images/Fedora-KDE-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ | Minimal raw-xz

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/aarch64/images/Fedora-Minimal-32-20200322.n.0.aarch64.raw.xz Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/armhfp/images/Fedora-Minimal-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ | Xfce raw-xz

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/aarch64/images/Fedora-Xfce-32-20200322.n.0.aarch64.raw.xz Download]

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/armhfp/images/Fedora-Xfce-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ | SoaS raw-xz

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/armhfp/images/Fedora-SoaS-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ | Mate raw-xz

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/armhfp/images/Fedora-Mate-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ | LXDE raw-xz

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/armhfp/images/Fedora-LXDE-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ | Python_Classroom raw-xz

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Labs/armhfp/images/Fedora-Python-Classroom-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ | LXQt raw-xz

+ | 

+ | [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-32-20200322.n.0/compose/Spins/armhfp/images/Fedora-LXQt-armhfp-32-20200322.n.0-sda.raw.xz Download]

+ | 

+ | 

+ | 

+ |-

+ |} 

\ No newline at end of file

@@ -0,0 +1,199 @@ 

+ {{Infobox_group

+ | name = Fedora Test Days

+ | image = [[File:Echo-testing-48px.png|link=QA/Fedora_20_test_days]]

+ | caption = [[Printing|Printing]]

+ | date = 2013-11-05

+ | time = all day

+ | website = [[QA/Fedora_20_test_days]]

+ | irc = [irc://irc.freenode.net/#fedora-test-day #fedora-test-day] ([http://webchat.freenode.net/?channels=fedora-test-day webirc])

+ | fedora_mailing_list = test

+ }}

+ 

+ {{admon/note | Can't make the date? | If you come to this page before or after the test day is completed, your testing is still valuable, and you can use the information on this page to test, file any bugs you find at [http://bugzilla.redhat.com Bugzilla], and add your results to the results section. If this page is more than a month old when you arrive here, please check the [[QA/Test_Days|current schedule]] and see if a similar but more recent Test Day is planned or has already happened.}}

+ 

+ == What to test? ==

+ 

+ Today's instalment of Fedora Test Day will focus on '''Printing'''.

+ 

+ This test day is for testing all aspects of printing, including setting up the printer, sharing printers on the network, and printing jobs.

+ 

+ The changes in Fedora 20 are relatively minor: switching to CUPS 1.7 and Ghostscript 9.10, and some improvements to cups-filters and the "Printers" part of GNOME Settings.

+ 

+ Note that since Fedora 19, printer sharing and discovery uses mDNS/DNS-SD rather than the CUPS Browsing method that's been the default in older releases.  The '''cups-browsed''' service provides backwards compatibility for CUPS Browsing/BrowsePoll.  One of the changes in Fedora 20 is for cups-browsed to use a more efficient method for BrowsePoll (asking for new/removed printers rather than fetching the entire list each time).

+ 

+ Remember that CUPS unit testing is only one small part of the story: printing is very much in need of '''integration''' testing. Try printing with different applications, using options you don't normally use in the print dialog. Try to see how many different ways you can break it!

+ 

+ If you see a problem and are not sure which component is to blame, [[Printing/Debugging|the Debugging Printing page]] can help you to diagnose it.

+ 

+ == Who's available ==

+ 

+ The following cast of characters will be available testing, workarounds, bug fixes, and general discussion ...

+ * Development - [[User:Twaugh|Tim Waugh]] (twaugh), [[User:Mkasik|Marek Kasik]] (mkasik), [[User:Jpopelka|Jiri Popelka]] (jpopelka)

+ * Quality Assurance - [[User:Psklenar|Petr Sklenar]] (psklenar)

+ 

+ == Prerequisite for Test Day == 

+ 

+ All you need is:

+ 

+ * An updated [http://fedoraproject.org/get-prerelease Fedora 20 pre-release]

+ * Access to a printer

+ 

+ == How to test? ==

+ 

+ First, make sure you have applied updates from the updates-testing repository.

+ 

+ * Configuring a printer not yet known to the system

+ * Printing a test page

+ * Printing something more complicated, e.g. LibreOffice document, PDF, email

+ * Taking advantage of extra print features of your printer e.g. duplexing, stapling

+ 

+ == Test Cases ==

+ 

+ Use the [http://testdays.qa.fedoraproject.org/testdays/show_event?event_id=13 TestDayApp] to view test case instructions and submit test results. When it asks you about your "Hardware", enter your printer make and model name.

+ 

+ {{admon/tip|Checking the Device ID is correctly listed|The hpijs, gutenprint-cups, foomatic-db-ppds and foomatic packages all contain tags that associate them with the Device IDs for the printers they support. If you are not given the opportunity to install one of these packages when your printer is supported by them, it may be that they do not list its Device ID correctly.  You can check this by running <code>/usr/share/system-config-printer/check-device-ids.py</code> as root.}}

+ 

+ == Test Results ==

+ 

+ If you have problems with any of the tests, report a bug to [https://bugzilla.redhat.com Bugzilla].  Choose the correct component:

+ 

+ * [https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&version=19&component=gnome-settings-daemon gnome-settings-daemon] for problems relating to job reporting and printer status feedback in GNOME Shell

+ * [https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&version=19&component=control-center control-center] for problems specific to printer administration in GNOME Shell e.g. the new system settings module

+ * [https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&version=19&component=cups cups] for printing problems that persist even when using command line utilities such as '''lp'''

+ * [https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&version=19&component=gtk2 gtk2] for printing problems common to all GTK+ applications using the GTK+ print dialog

+ * [https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&version=19&component=system-config-printer system-config-printer] for bugs in the printing configuration program used in GNOME fallback mode and in other graphical environments

+ * [https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&version=19&component=selinux-policy selinux-policy] if there is some selinux issues

+ 

+ If you see something unexpected, even if you are not entirely sure whether it's a bug, please take the time to report it as one.  Without doing that, it may be difficult to follow up and fix.

+ 

+ === Basic Tests ===

+ 

+ {| class="wikitable" width=100%

+ ! User

+ ! Hardware

+ ! [[QA:Testcase_Printing_New_Printer|Connect a local printer]]

+ ! [[QA:Testcase_Printing_Known_Printer|Reconnect a local printer]]

+ ! [[QA:Testcase_Printing_Network_Printer|Print to network printer]]

+ ! [[QA:Testcase_Printing_Complex|Print dialog options]]

+ ! [[QA:Testcase_Printing_Troubleshooting|Status feedback]]

+ ! [[QA:Testcase_selinux-AVCs|SELinux AVCs]]

+ ! [[QA:Testcase_KDE4_Printer|KDE printer setup]]

+ ! References

+ |-

+ | [[User:lnie|lnie]]

+ | Lenovo Thinkstation S20

+ | 

+ | 

+ | {{result|pass}}  

+ | {{result|pass}}  

+ | 

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:twaugh|twaugh]]

+ | HP DeskJet 990Cxi

+ | {{result|pass}}  <ref>Configured as A4 automatically (correct). Uses hpcups... right/bottom margins look a little wide though.</ref>

+ | {{result|pass}}  

+ | 

+ | {{result|pass}}  <ref group="long">Tested 2 collated copies of 3-page document, duplexed, with libreoffice, evince, and firefox. Also same test of 1-page document with evolution.</ref>

+ | 

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:twaugh|twaugh]]

+ | HP Photosmart 5510 (hp backend)

+ | 

+ | 

+ | 

+ | {{result|pass}}  <ref>Out of paper notification works correctly when hp backend is used (not dnssd).</ref>

+ | 

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:twaugh|twaugh]]

+ | HP Photosmart 5510

+ | 

+ | 

+ | {{result|fail||1026940}}<ref>Hard to set up printer because the dnssd backend is unreliable.</ref>

+ | 

+ | {{result|fail||1026949}}

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:twaugh|twaugh]]

+ | HP PSC 2210

+ | {{result|pass}}  

+ | {{result|pass}}  

+ | 

+ | 

+ | {{result|fail||1026928}}{{result|fail||1026909|1026914}}<ref>No status feedback. In addition, print was corrupted after paper replaced.</ref>

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:Roshi|Roshi]]

+ | HP Deskjet 1000 j110 on i386 RC3 installation Xfce

+ | {{result|pass}}  

+ | {{result|pass}}  

+ | 

+ | {{result|pass}}  <ref>All options not available on my printer were blurred out.</ref>

+ | {{result|pass}}  

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:Roshi|Roshi]]

+ | HP Deskjet 1000 j110 on x86_64 RC2 Live KDE

+ | {{result|warn||1027425}}{{result|fail}}  <ref>KDE from the live image recognizes the printer and configures it. When you attempt to print - you get a 'filter failed' error. </ref>

+ | 

+ | 

+ | 

+ | 

+ | 

+ | {{result|fail}}  <ref>Printer get recognized and configured correctly by KDE - printing fails with 'filter error.'</ref>

+ | <references/>

+ |-

+ | [[User:satellit|satellit]]

+ | system76 laptop i7

+ | {{result|pass}}  <ref>wireless connectio to HP 6100 turned off/on prints successfully</ref>

+ | 

+ | 

+ | {{result|warn}}  <ref>printed from pdf file 15 pages and then cancelled running job sucessfully</ref>

+ | 

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:Astrik|Astrik]]

+ | HP C4180 All in One

+ | {{result|pass}}  <ref>Printer recognized and pop up stated it was configuring printer.</ref>

+ | {{result|pass}}  

+ | 

+ | {{result|pass}}  <ref>Different options selected all seemed to work as expected.</ref>

+ | {{result|pass}}  

+ | {{result|pass}}  

+ | 

+ | <references/>

+ |-

+ | [[User:luya|luya]]

+ | HP Photosmart eStation C510

+ | {{result|pass}}  

+ | {{result|pass}}  

+ | {{result|pass}}  

+ | {{result|pass}}  

+ | 

+ | {{result|pass}}  

+ | 

+ | <references/>

+ |-

+ |}

+ 

+ == Long comments ==

+ <references group="long" />

+ 

+  [[Category:Fedora 20 Test Days]]

+ 

@@ -0,0 +1,500 @@ 

+ {{Infobox_group

+ | name = Fedora Test Day

+ | image = [[File:Echo-testing-48px.png|link=QA/Test Days]]

+ | caption = [[Fedora Cloud Test day]]

+ | date = 2016-10-24

+ | time = all day

+ | website = [[QA/Test Days]]

+ | irc = [irc://irc.freenode.net/#fedora-test-day #fedora-test-day] ([http://webchat.freenode.net/?channels=fedora-test-day webirc])

+ | fedora_mailing_list = test

+ }}

+ 

+ {{admon/note | Can't make the date? | If you come to this page before or after the test day is completed, your testing is still valuable, and you can use the information on this page to test, file any bugs you find at [http://bugzilla.redhat.com Bugzilla], and add your results to the results section. If this page is more than a month old when you arrive here, please check the [[QA/Test_Days|current schedule]] and see if a similar but more recent Test Day is planned or has already happened.}}

+ 

+ == What to test? ==

+ 

+ Today's installment of Fedora Test Day will focus on '''Cloud, Atomic Host''' images for Fedora 25.

+ 

+ == Who's available ==

+ 

+ The following cast of characters will be available testing, workarounds, bug fixes, and general discussion ...

+ * Development - 

+ * Quality Assurance -

+ * Community Members - dustymabe, jasonbrooks, kushal, bowlofeggs

+ 

+ == Prerequisite for Test Day == 

+ 

+ You will require at least a system with libvirt/kvm installed. Having access to any Openstack or other Cloud IAAS system where you can upload an image will be even better.

+ 

+ Download the images from below links

+ 

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Atomic-25-20161023.n.0.x86_64.qcow2 Atomic Host QCOW2]

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Atomic-Vagrant-25-20161023.n.0.x86_64.vagrant-virtualbox.box Vagrant Virtualbox Atomic]

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Atomic-Vagrant-25-20161023.n.0.x86_64.vagrant-libvirt.box Vagrant libvirt Atomic]

+ 

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Cloud-Base-25-20161023.n.0.x86_64.qcow2 Cloud Base QCOW2]

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Cloud-Base-Vagrant-25-20161023.n.0.x86_64.vagrant-virtualbox.box Vagrant Virtualbox Cloud Base]

+ 

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Cloud-Base-Vagrant-25-20161023.n.0.x86_64.vagrant-libvirt.box Vagrant libvirt Cloud Base]

+ 

+  

+ List of AMIs

+ 

+ {| class="wikitable"

+ |-

+ ! Image Name

+ ! AMI id

+ ! Destination

+ ! Virt type   

+ ! Vol type

+ |-

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-west-2)      || ami-2d4bef4d    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-west-2)      || ami-7d48ec1d    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-west-1)      || ami-c4450ea4    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (sa-east-1)      || ami-1bd64a77    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (eu-west-1)      || ami-1b327b68    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (eu-central-1)   || ami-2645bc49    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-southeast-2) || ami-100b3673    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-southeast-1) || ami-afe948cc    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-northeast-1) || ami-5abd1c3b    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-west-1)      || ami-6a400b0a    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (sa-east-1)      || ami-39d64a55    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (eu-west-1)      || ami-52337a21    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (eu-central-1)   || ami-9d44bdf2    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-southeast-2) || ami-7a0a3719    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-southeast-1) || ami-71e94812    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-northeast-1) || ami-8fba1bee    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-east-1)      || ami-a7461bb0    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-east-1)      || ami-71471a66    || hvm           || gp2 

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-2)      ||  ami-a74befc7    ||  paravirtual   ||  gp2

+ |-            

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-2)      ||  ami-a54befc5    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-1)      ||  ami-bc470cdc    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (sa-east-1)      ||  ami-e2da468e    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-west-1)      ||  ami-05337a76    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-central-1)   ||  ami-2b45bc44    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-2) ||  ami-33053850    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-1) ||  ami-9be746f8    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-1)      ||  ami-07460d67    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (sa-east-1)      ||  ami-47d4482b    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-west-1)      ||  ami-51347d22    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-central-1)   ||  ami-7db84012    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-2) ||  ami-dc0a37bf    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-1) ||  ami-99e746fa    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-northeast-1) ||  ami-ffbf1e9e    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-northeast-1) ||  ami-37802156    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-east-1)      ||  ami-ed401dfa    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-east-1)      ||  ami-f1461be6    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-2)      ||  ami-194feb79    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (sa-east-1)      ||  ami-ce069ba2    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-1)      ||  ami-a1470cc1    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-west-1)      ||  ami-91337ae2    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-central-1)   ||  ami-fb46bf94    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-2) ||  ami-130b3670    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-1) ||  ami-a4e849c7    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-2)      ||  ami-114aee71    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-northeast-1) ||  ami-3cbe1f5d    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (sa-east-1)      ||  ami-cd069ba1    ||  hvm           ||  gp2  

+ |-          

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-1)      ||  ami-c6450ea6    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-west-1)      ||  ami-b1327bc2    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-central-1)   ||  ami-f546bf9a    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-2) ||  ami-1a0b3679    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-1) ||  ami-2ce8494f    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-northeast-1) ||  ami-64812005    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-east-1)      ||  ami-d04a17c7    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-east-1)      ||  ami-cf4419d8    ||  hvm           ||  gp2  

+ |}

+ 

+ 

+ Here's a chunk which is commonly used for most Test Days. Replace XX with whatever Fedora release is pending:

+ 

+ * An updated [https://www.happyassassin.net/nightlies.html Fedora 25 nightly Cloud/Atomic Host image]

+ 

+ == How to test? ==

+ 

+ * Boot the images on a cloud system, or on Vagrant or using testcloud on a local system.

+ * Execute the tests.

+ 

+ Note: For the service manipulation tests we are stopping/disabling '''chronyd''' service on Fedora 25 systems.

+ 

+ === Known Issues ===

+ 

+ For the Atomic qcow2 image we have a known docker storage issue. Use the following cloud-config to start an instance

+ in a cloud.

+ 

+     #cloud-config

+     bootcmd:

+     - sed s/After=cloud-final.service/After=cloud-init-local.service/ /usr/lib/systemd/system/docker-storage-setup.service > /etc/systemd/system/docker-storage-setup.service

+     - systemctl daemon-reload

+     - systemctl start --no-block docker.service

+ 

+ === Run the tests ===

+ 

+ Visit the [http://testdays.fedorainfracloud.org/events/15 result page] and click on the column title links to see the tests that need to be run: most column titles are links to a specific test case. Follow the instructions there, then enter your results by clicking the ''Enter result'' button for the test.

+ 

+ === Exploratory testing ===

+ 

+ If you run out of test cases, congratulations! But that's not the end! You can still help out by playing around with the tool in whatever ways you can think of: try out all the things you can find, and try writing different Fedora images with it. If you have systems with different Windows versions, try running the tool on all of them. Get creative! Any problems you find please file a bug, or report to the IRC channel.

+ 

+ == Reporting bugs ==

+ 

+ 

+ 

+ If you have problems with any of the tests, report a bug to [https://bugzilla.redhat.com Bugzilla]. If you are unsure about exactly how to file the report or what other information to include, just ask on IRC and we will help you.

+ 

+  IRC Channel name: #fedora-cloud, #fedora-test-day

+  Server: Freenode

+ 

+ == Test Results ==

+ 

+ Results transferred from the [http://testdays.fedorainfracloud.org/events/15 result page] 2018-02-06.

+ 

+ === Basic Tests ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/QA:Testcase_base_startup Base startup]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_base_system_logging System logging]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Services_start Services start]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_base_selinux SELinux]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_base_service_manipulation Service manipulation]

+ ! References

+ |-

+ | [[User:bowlofeggs|bowlofeggs]]

+ | Vagrant libvirt Cloud Base

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | qcow2 -- atomic -- libvirt

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}<ref>`systemctl --all --failed` reported no failed services, despite https://bugzilla.redhat.com/show_bug.cgi?id=1387934</ref>

+ | {{result|pass}}

+ | {{result|pass}}<ref group="long">Works, despite continual, maddening "service changed on disk. Run 'systemctl daemon-reload' to reload" bug: https://bugzilla.redhat.com/show_bug.cgi?id=1384150</ref>

+ | <references/>

+ |-

+ | [[User:jberkus|jberkus]]

+ | AWS, us-west-2, Fedora-Atomic-25-20161023.n.0.x86_64-us-west-2-HVM-gp2-0 (ami-2d4bef4d), T2 Small

+ | {{result|pass}}<ref>Booted on AWS</ref>

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|warn}}<ref>Got bogus "Warning: docker.service changed on disk. Run 'systemctl daemon-reload' to reload units." on status.

+ </ref>{{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | 

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}<ref>* With the userdata patch from Dusty</ref>

+ | {{result|pass}}

+ | {{result|pass}}{{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | AWS t2.medium us-west-1 ami-c4450ea4

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | Openstack

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:trishnag|trishnag]]

+ | 

+ | 

+ | {{result|pass}}

+ | 

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:trishnag|trishnag]]

+ | Openstack

+ | 

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ 

+ |-

+ |}

+ 

+ === Atomic Tests ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/User:Roshi/QA/AtomicTests/Atomic_Image_Boots Image boots]

+ ! [http://fedoraproject.org/wiki/User:Roshi/QA/AtomicTests/Atomic_Upgrade Upgrade works]

+ ! [http://fedoraproject.org/wiki/User:Roshi/QA/AtomicTests/Atomic_Rollback Rollback works]

+ ! References

+ |-

+ | [[User:dustymabe|dustymabe]]

+ | openstack newton

+ | {{result|pass}}

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:dustymabe|dustymabe]]

+ | openstack newton atomic qcow

+ | {{result|pass}}

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | qcow2 -- atomic -- libvirt

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | vagrant -- atomic -- libvirt

+ | {{result|pass}}

+ | {{result|pass}}<ref>I tested upgrade by first deploying a previous version: `atomic host deploy 25.32 && reboot`</ref>

+ | 

+ | <references/>

+ |-

+ | [[User:jberkus|jberkus]]

+ | AWS, us-west-2, Fedora-Atomic-25-20161023.n.0.x86_64-us-west-2-HVM-gp2-0 (ami-2d4bef4d), T2 Small

+ | {{result|pass}}

+ | {{result|pass}}<ref>Unable to upgrade because I installed the latest version.  However, "rpm-ostree upgrade" gave the correct result.</ref>

+ | 

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | AWS t2.medium us-west-1 ami-c4450ea4

+ | {{result|pass}}

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | Openstack

+ | {{result|pass}}

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:trishnag|trishnag]]

+ | Openstack

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ 

+ |-

+ |}

+ 

+ === Docker tests ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Docker_Install Docker install]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Docker_Storage_Setup Docker storage setup]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Docker_Daemon Docker daemon]

+ ! References

+ |-

+ | [[User:dustymabe|dustymabe]]

+ | openstack newton atomic qcow

+ | {{result|pass}}

+ | {{result|fail}}<ref group="long">{{bz|1387934}} Had to work around the bug with user data from https://lists.fedoraproject.org/archives/list/cloud@lists.fedoraproject.org/message/R7S4AMKFAZZBDROBS4D76PRSOK7OIW6O/</ref>

+ | {{result|pass}}<ref>passed with workaround from #1387934</ref>

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | qcow2 -- atomic -- libvirt

+ | 

+ | {{result|fail}}<ref group="long">{{bz|1387934}} Didn't run automatically, so docker started w/ loopback storage. I worked around w: `systemctl stop docker && rm -rf /var/lib/docker && docker-storage-setup && systemctl start docker`</ref>

+ | {{result|warn}}<ref>Started automatically, but w/ loopback storage, worked around by running docker-storage-setup manually.</ref>

+ | <references/>

+ |-

+ | [[User:jberkus|jberkus]]

+ | AWS, us-west-2, Fedora-Atomic-25-20161023.n.0.x86_64-us-west-2-HVM-gp2-0 (ami-2d4bef4d), T2 Small

+ | {{result|pass}}<ref>Docker 1.12 installed.</ref>

+ | {{result|fail}}<ref group="long">docker-storage-setup.service: Job docker-storage-setup.service/start deleted to break ordering cycle starting with multi

+ Starting Docker Storage Setup...

+ CHANGED: partition=2 start=616448 old: size=11966464 end=12582912 new: size=20355072,end=20971520

+   Physical volume "/dev/xvda2" changed

+   1 physical volume(s) resized / 0 physical volume(s) not resized

+   WARNING: D-Bus notification failed: The name com.redhat.lvmdbus1 was not provided by any .service files

+ ERROR: Docker has been previously configured for use with devicemapper graph driver. Not creating a new thin pool as exi

+ INFO: Docker state can be reset by stopping docker and by removing /var/lib/docker directory. This will destroy existing

+ docker-storage-setup.service: Main process exited, code=exited, status=1/FAILURE

+ Failed to start Docker Storage Setup.

+ docker-storage-setup.service: Unit entered failed state.

+ docker-storage-setup.service: Failed with result 'exit-code'.

+ 

+ Also, Docker Pool reports that it's using a loopback which is 100GB in size.  This VM only has 11GB RAM + Storage *total*.  This seems like an issue.</ref>

+ | {{result|pass}}<ref>Started with loopback storage; had to be manually torn down and docker-storage-setup rerun.</ref>

+ | <references/>

+ |-

+ | [[User:lnie|lnie]]

+ | openstack

+ | {{result|pass}}

+ | {{result|fail}}<ref>{{bz|1388000}}</ref>

+ | 

+ | <references/>

+ 

+ |-

+ |}

+ 

+ === More Atomic related tests ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Atomic_command Atomic command]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Atomic_read_only_mounts Atomic read only mounts]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Atomic_root_mount Atomic root mount]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Tmp_Mount Atomic tmp mount]

+ ! [http://fedoraproject.org/wiki/QA/Draft/Testcase_tmp_writable Atomic tmp writable]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Journal_Written Atomic journal written]

+ ! References

+ |-

+ | [[User:dustymabe|dustymabe]]

+ | openstack newton atomic qcow

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | qcow2 -- atomic -- libvirt

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jberkus|jberkus]]

+ | AWS, us-west-2, Fedora-Atomic-25-20161023.n.0.x86_64-us-west-2-HVM-gp2-0 (ami-2d4bef4d), T2 Small

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | AWS t2.medium us-west-1 ami-c4450ea4

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | Openstack

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ 

+ |-

+ |}

+ 

+ === Vagrant test ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/QA/Draft/Testcase_Vagrant_eth0_naming eth0 naming]

+ ! References

+ |-

+ | [[User:bowlofeggs|bowlofeggs]]

+ | Vagrant libvirt Cloud Base

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | vagrant -- atomic -- libvirt

+ | {{result|pass}}

+ | <references/>

+ 

+ |-

+ |}

+ 

+ == Long comments ==

+ <references group="long" />

+ 

+ [[Category:Fedora 25 Test Days]] 

\ No newline at end of file

@@ -0,0 +1,498 @@ 

+ {{Infobox_group

+ | name = Fedora Test Day

+ | image = [[File:Echo-testing-48px.png|link=QA/Test Days]]

+ | caption = [[Fedora Cloud Test day]]

+ | date = 2016-10-24

+ | time = all day

+ | website = [[QA/Test Days]]

+ | irc = [irc://irc.freenode.net/#fedora-test-day #fedora-test-day] ([http://webchat.freenode.net/?channels=fedora-test-day webirc])

+ | fedora_mailing_list = test

+ }}

+ 

+ {{admon/note | Can't make the date? | If you come to this page before or after the test day is completed, your testing is still valuable, and you can use the information on this page to test, file any bugs you find at [http://bugzilla.redhat.com Bugzilla], and add your results to the results section. If this page is more than a month old when you arrive here, please check the [[QA/Test_Days|current schedule]] and see if a similar but more recent Test Day is planned or has already happened.}}

+ 

+ == What to test? ==

+ 

+ Today's installment of Fedora Test Day will focus on '''Cloud, Atomic Host''' images for Fedora 25.

+ 

+ == Who's available ==

+ 

+ The following cast of characters will be available testing, workarounds, bug fixes, and general discussion ...

+ * Development - 

+ * Quality Assurance -

+ * Community Members - dustymabe, jasonbrooks, kushal, bowlofeggs

+ 

+ == Prerequisite for Test Day == 

+ 

+ You will require at least a system with libvirt/kvm installed. Having access to any Openstack or other Cloud IAAS system where you can upload an image will be even better.

+ 

+ Download the images from below links

+ 

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Atomic-25-20161023.n.0.x86_64.qcow2 Atomic Host QCOW2]

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Atomic-Vagrant-25-20161023.n.0.x86_64.vagrant-virtualbox.box Vagrant Virtualbox Atomic]

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Atomic-Vagrant-25-20161023.n.0.x86_64.vagrant-libvirt.box Vagrant libvirt Atomic]

+ 

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Cloud-Base-25-20161023.n.0.x86_64.qcow2 Cloud Base QCOW2]

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Cloud-Base-Vagrant-25-20161023.n.0.x86_64.vagrant-virtualbox.box Vagrant Virtualbox Cloud Base]

+ 

+ * [https://kojipkgs.fedoraproject.org/compose/branched/Fedora-25-20161023.n.0/compose/CloudImages/x86_64/images/Fedora-Cloud-Base-Vagrant-25-20161023.n.0.x86_64.vagrant-libvirt.box Vagrant libvirt Cloud Base]

+ 

+  

+ List of AMIs

+ 

+ {| class="wikitable"

+ |-

+ ! Image Name

+ ! AMI id

+ ! Destination

+ ! Virt type   

+ ! Vol type

+ |-

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-west-2)      || ami-2d4bef4d    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-west-2)      || ami-7d48ec1d    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-west-1)      || ami-c4450ea4    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (sa-east-1)      || ami-1bd64a77    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (eu-west-1)      || ami-1b327b68    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (eu-central-1)   || ami-2645bc49    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-southeast-2) || ami-100b3673    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-southeast-1) || ami-afe948cc    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-northeast-1) || ami-5abd1c3b    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-west-1)      || ami-6a400b0a    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (sa-east-1)      || ami-39d64a55    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (eu-west-1)      || ami-52337a21    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (eu-central-1)   || ami-9d44bdf2    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-southeast-2) || ami-7a0a3719    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-southeast-1) || ami-71e94812    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (ap-northeast-1) || ami-8fba1bee    || hvm           || gp2 

+ |-            

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-east-1)      || ami-a7461bb0    || hvm           || standard 

+ |-       

+ |  Fedora-Atomic-25-20161023.n.0.x86_64          || EC2 (us-east-1)      || ami-71471a66    || hvm           || gp2 

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-2)      ||  ami-a74befc7    ||  paravirtual   ||  gp2

+ |-            

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-2)      ||  ami-a54befc5    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-1)      ||  ami-bc470cdc    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (sa-east-1)      ||  ami-e2da468e    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-west-1)      ||  ami-05337a76    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-central-1)   ||  ami-2b45bc44    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-2) ||  ami-33053850    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-1) ||  ami-9be746f8    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-1)      ||  ami-07460d67    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (sa-east-1)      ||  ami-47d4482b    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-west-1)      ||  ami-51347d22    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-central-1)   ||  ami-7db84012    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-2) ||  ami-dc0a37bf    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-1) ||  ami-99e746fa    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-northeast-1) ||  ami-ffbf1e9e    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-northeast-1) ||  ami-37802156    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-east-1)      ||  ami-ed401dfa    ||  paravirtual   ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-east-1)      ||  ami-f1461be6    ||  paravirtual   ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-2)      ||  ami-194feb79    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (sa-east-1)      ||  ami-ce069ba2    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-1)      ||  ami-a1470cc1    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-west-1)      ||  ami-91337ae2    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-central-1)   ||  ami-fb46bf94    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-2) ||  ami-130b3670    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-1) ||  ami-a4e849c7    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-2)      ||  ami-114aee71    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-northeast-1) ||  ami-3cbe1f5d    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (sa-east-1)      ||  ami-cd069ba1    ||  hvm           ||  gp2  

+ |-          

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-west-1)      ||  ami-c6450ea6    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-west-1)      ||  ami-b1327bc2    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (eu-central-1)   ||  ami-f546bf9a    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-2) ||  ami-1a0b3679    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-southeast-1) ||  ami-2ce8494f    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (ap-northeast-1) ||  ami-64812005    ||  hvm           ||  gp2            

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-east-1)      ||  ami-d04a17c7    ||  hvm           ||  standard       

+ |-

+ |  Fedora-Cloud-Base-25-20161023.n.0.x86_64      ||  EC2 (us-east-1)      ||  ami-cf4419d8    ||  hvm           ||  gp2  

+ |}

+ 

+ 

+ Here's a chunk which is commonly used for most Test Days. Replace XX with whatever Fedora release is pending:

+ 

+ * An updated [https://www.happyassassin.net/nightlies.html Fedora 25 nightly Cloud/Atomic Host image]

+ 

+ == How to test? ==

+ 

+ * Boot the images on a cloud system, or on Vagrant or using testcloud on a local system.

+ * Execute the tests.

+ 

+ Note: For the service manipulation tests we are stopping/disabling '''chronyd''' service on Fedora 25 systems.

+ 

+ === Known Issues ===

+ 

+ For the Atomic qcow2 image we have a known docker storage issue. Use the following cloud-config to start an instance

+ in a cloud.

+ 

+     #cloud-config

+     bootcmd:

+     - sed s/After=cloud-final.service/After=cloud-init-local.service/ /usr/lib/systemd/system/docker-storage-setup.service > /etc/systemd/system/docker-storage-setup.service

+     - systemctl daemon-reload

+     - systemctl start --no-block docker.service

+ 

+ === Run the tests ===

+ 

+ Visit the [http://testdays.fedorainfracloud.org/events/15 result page] and click on the column title links to see the tests that need to be run: most column titles are links to a specific test case. Follow the instructions there, then enter your results by clicking the ''Enter result'' button for the test.

+ 

+ === Exploratory testing ===

+ 

+ If you run out of test cases, congratulations! But that's not the end! You can still help out by playing around with the tool in whatever ways you can think of: try out all the things you can find, and try writing different Fedora images with it. If you have systems with different Windows versions, try running the tool on all of them. Get creative! Any problems you find please file a bug, or report to the IRC channel.

+ 

+ == Reporting bugs ==

+ 

+ 

+ 

+ If you have problems with any of the tests, report a bug to [https://bugzilla.redhat.com Bugzilla]. If you are unsure about exactly how to file the report or what other information to include, just ask on IRC and we will help you.

+ 

+  IRC Channel name: #fedora-cloud, #fedora-test-day

+  Server: Freenode

+ 

+ == Test Results ==

+ 

+ Results transferred from the [http://testdays.fedorainfracloud.org/events/15 result page] 2018-02-06.

+ 

+ === Basic Tests ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/QA:Testcase_base_startup Base startup]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_base_system_logging System logging]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Services_start Services start]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_base_selinux SELinux]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_base_service_manipulation Service manipulation]

+ ! References

+ |-

+ | [[User:bowlofeggs|bowlofeggs]]

+ | Vagrant libvirt Cloud Base

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | qcow2 -- atomic -- libvirt

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}<ref>`systemctl --all --failed` reported no failed services, despite https://bugzilla.redhat.com/show_bug.cgi?id=1387934</ref>

+ | {{result|pass}}

+ | {{result|pass}}<ref>Works, despite continual, maddening "service changed on disk. Run 'systemctl daemon-reload' to reload" bug: https://bugzilla.redhat.com/show_bug.cgi?id=1384150</ref>

+ | <references/>

+ |-

+ | [[User:jberkus|jberkus]]

+ | AWS, us-west-2, Fedora-Atomic-25-20161023.n.0.x86_64-us-west-2-HVM-gp2-0 (ami-2d4bef4d), T2 Small

+ | {{result|pass}}<ref>Booted on AWS</ref>

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|warn}}<ref>Got bogus "Warning: docker.service changed on disk. Run 'systemctl daemon-reload' to reload units." on status.

+ </ref>{{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | 

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}<ref>* With the userdata patch from Dusty</ref>

+ | {{result|pass}}

+ | {{result|pass}}{{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | AWS t2.medium us-west-1 ami-c4450ea4

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | Openstack

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:trishnag|trishnag]]

+ | 

+ | 

+ | {{result|pass}}

+ | 

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:trishnag|trishnag]]

+ | Openstack

+ | 

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ 

+ |-

+ |}

+ 

+ === Atomic Tests ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/User:Roshi/QA/AtomicTests/Atomic_Image_Boots Image boots]

+ ! [http://fedoraproject.org/wiki/User:Roshi/QA/AtomicTests/Atomic_Upgrade Upgrade works]

+ ! [http://fedoraproject.org/wiki/User:Roshi/QA/AtomicTests/Atomic_Rollback Rollback works]

+ ! References

+ |-

+ | [[User:dustymabe|dustymabe]]

+ | openstack newton

+ | {{result|pass}}

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:dustymabe|dustymabe]]

+ | openstack newton atomic qcow

+ | {{result|pass}}

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | qcow2 -- atomic -- libvirt

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | vagrant -- atomic -- libvirt

+ | {{result|pass}}

+ | {{result|pass}}<ref>I tested upgrade by first deploying a previous version: `atomic host deploy 25.32 && reboot`</ref>

+ | 

+ | <references/>

+ |-

+ | [[User:jberkus|jberkus]]

+ | AWS, us-west-2, Fedora-Atomic-25-20161023.n.0.x86_64-us-west-2-HVM-gp2-0 (ami-2d4bef4d), T2 Small

+ | {{result|pass}}

+ | {{result|pass}}<ref>Unable to upgrade because I installed the latest version.  However, "rpm-ostree upgrade" gave the correct result.</ref>

+ | 

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | AWS t2.medium us-west-1 ami-c4450ea4

+ | {{result|pass}}

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | Openstack

+ | {{result|pass}}

+ | 

+ | 

+ | <references/>

+ |-

+ | [[User:trishnag|trishnag]]

+ | Openstack

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ 

+ |-

+ |}

+ 

+ === Docker tests ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Docker_Install Docker install]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Docker_Storage_Setup Docker storage setup]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Docker_Daemon Docker daemon]

+ ! References

+ |-

+ | [[User:dustymabe|dustymabe]]

+ | openstack newton atomic qcow

+ | {{result|pass}}

+ | {{result|fail}}<ref>{{bz|1387934}} Had to work around the bug with user data from https://lists.fedoraproject.org/archives/list/cloud@lists.fedoraproject.org/message/R7S4AMKFAZZBDROBS4D76PRSOK7OIW6O/</ref>

+ | {{result|pass}}<ref>passed with workaround from #1387934</ref>

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | qcow2 -- atomic -- libvirt

+ | 

+ | {{result|fail}}<ref>{{bz|1387934}} Didn't run automatically, so docker started w/ loopback storage. I worked around w: `systemctl stop docker && rm -rf /var/lib/docker && docker-storage-setup && systemctl start docker`</ref>

+ | {{result|warn}}<ref>Started automatically, but w/ loopback storage, worked around by running docker-storage-setup manually.</ref>

+ | <references/>

+ |-

+ | [[User:jberkus|jberkus]]

+ | AWS, us-west-2, Fedora-Atomic-25-20161023.n.0.x86_64-us-west-2-HVM-gp2-0 (ami-2d4bef4d), T2 Small

+ | {{result|pass}}<ref>Docker 1.12 installed.</ref>

+ | {{result|fail}}<ref>docker-storage-setup.service: Job docker-storage-setup.service/start deleted to break ordering cycle starting with multi

+ Starting Docker Storage Setup...

+ CHANGED: partition=2 start=616448 old: size=11966464 end=12582912 new: size=20355072,end=20971520

+   Physical volume "/dev/xvda2" changed

+   1 physical volume(s) resized / 0 physical volume(s) not resized

+   WARNING: D-Bus notification failed: The name com.redhat.lvmdbus1 was not provided by any .service files

+ ERROR: Docker has been previously configured for use with devicemapper graph driver. Not creating a new thin pool as exi

+ INFO: Docker state can be reset by stopping docker and by removing /var/lib/docker directory. This will destroy existing

+ docker-storage-setup.service: Main process exited, code=exited, status=1/FAILURE

+ Failed to start Docker Storage Setup.

+ docker-storage-setup.service: Unit entered failed state.

+ docker-storage-setup.service: Failed with result 'exit-code'.

+ 

+ Also, Docker Pool reports that it's using a loopback which is 100GB in size.  This VM only has 11GB RAM + Storage *total*.  This seems like an issue.</ref>

+ | {{result|pass}}<ref>Started with loopback storage; had to be manually torn down and docker-storage-setup rerun.</ref>

+ | <references/>

+ |-

+ | [[User:lnie|lnie]]

+ | openstack

+ | {{result|pass}}

+ | {{result|fail}}<ref>{{bz|1388000}}</ref>

+ | 

+ | <references/>

+ 

+ |-

+ |}

+ 

+ === More Atomic related tests ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Atomic_command Atomic command]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Atomic_read_only_mounts Atomic read only mounts]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Atomic_root_mount Atomic root mount]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Tmp_Mount Atomic tmp mount]

+ ! [http://fedoraproject.org/wiki/QA/Draft/Testcase_tmp_writable Atomic tmp writable]

+ ! [http://fedoraproject.org/wiki/QA:Testcase_Journal_Written Atomic journal written]

+ ! References

+ |-

+ | [[User:dustymabe|dustymabe]]

+ | openstack newton atomic qcow

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | qcow2 -- atomic -- libvirt

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jberkus|jberkus]]

+ | AWS, us-west-2, Fedora-Atomic-25-20161023.n.0.x86_64-us-west-2-HVM-gp2-0 (ami-2d4bef4d), T2 Small

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | AWS t2.medium us-west-1 ami-c4450ea4

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:kushal|kushal]]

+ | Openstack

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | {{result|pass}}

+ | <references/>

+ 

+ |-

+ |}

+ 

+ === Vagrant test ===

+ {| class="wikitable"

+ ! User

+ ! Profile

+ ! [http://fedoraproject.org/wiki/QA/Draft/Testcase_Vagrant_eth0_naming eth0 naming]

+ ! References

+ |-

+ | [[User:bowlofeggs|bowlofeggs]]

+ | Vagrant libvirt Cloud Base

+ | {{result|pass}}

+ | <references/>

+ |-

+ | [[User:jasonbrooks|jasonbrooks]]

+ | vagrant -- atomic -- libvirt

+ | {{result|pass}}

+ | <references/>

+ 

+ |-

+ |}

+ 

+ [[Category:Fedora 25 Test Days]]

+ 

@@ -0,0 +1,242 @@ 

+ [

+     {

+         "anchor": "Which_tests_to_run",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-1",

+         "level": "2",

+         "line": "Which tests to run",

+         "number": "1",

+         "toclevel": 1

+     },

+     {

+         "anchor": "How_to_test",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-2",

+         "level": "2",

+         "line": "How to test",

+         "number": "2",

+         "toclevel": 1

+     },

+     {

+         "anchor": "Results_summary_page",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-3",

+         "level": "2",

+         "line": "Results summary page",

+         "number": "3",

+         "toclevel": 1

+     },

+     {

+         "anchor": "Add.2C_Modify_or_Remove_a_Test_Case",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-4",

+         "level": "2",

+         "line": "Add, Modify or Remove a Test Case",

+         "number": "4",

+         "toclevel": 1

+     },

+     {

+         "anchor": "Key",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-5",

+         "level": "2",

+         "line": "Key",

+         "number": "5",

+         "toclevel": 1

+     },

+     {

+         "anchor": "Test_Matrix",

+         "byteoffset": 135,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "1",

+         "level": "2",

+         "line": "<b>Test Matrix</b>",

+         "number": "6",

+         "toclevel": 1

+     },

+     {

+         "anchor": "Image_sanity",

+         "byteoffset": 452,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "2",

+         "level": "4",

+         "line": "Image sanity",

+         "number": "6.1",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Default_boot_and_install",

+         "byteoffset": 2359,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "3",

+         "level": "4",

+         "line": "Default boot and install",

+         "number": "6.2",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Fedora_Media_Writer",

+         "byteoffset": 6177,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "4",

+         "level": "4",

+         "line": "Fedora Media Writer",

+         "number": "6.3",

+         "toclevel": 2

+     },

+     {

+         "anchor": "ARM_disk_images",

+         "byteoffset": 6809,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "5",

+         "level": "4",

+         "line": "ARM disk images",

+         "number": "6.4",

+         "toclevel": 2

+     },

+     {

+         "anchor": "PXE_boot",

+         "byteoffset": 7926,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "6",

+         "level": "4",

+         "line": "PXE boot",

+         "number": "6.5",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Virtualization",

+         "byteoffset": 8301,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "7",

+         "level": "4",

+         "line": "Virtualization",

+         "number": "6.6",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Storage_devices",

+         "byteoffset": 8936,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "8",

+         "level": "4",

+         "line": "Storage devices",

+         "number": "6.7",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Guided_storage_configuration",

+         "byteoffset": 10304,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "9",

+         "level": "4",

+         "line": "Guided storage configuration",

+         "number": "6.8",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Guided_storage_shrinking",

+         "byteoffset": 11735,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "10",

+         "level": "4",

+         "line": "Guided storage shrinking",

+         "number": "6.9",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Custom_storage_configuration",

+         "byteoffset": 12300,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "11",

+         "level": "4",

+         "line": "Custom storage configuration",

+         "number": "6.10",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Advanced_custom_storage_configuration",

+         "byteoffset": 13533,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "12",

+         "level": "4",

+         "line": "Advanced custom storage configuration",

+         "number": "6.11",

+         "toclevel": 2

+     },

+     {

+         "anchor": "User_interface",

+         "byteoffset": 14771,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "13",

+         "level": "4",

+         "line": "User interface",

+         "number": "6.12",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Installation_repositories",

+         "byteoffset": 15934,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "14",

+         "level": "4",

+         "line": "Installation repositories",

+         "number": "6.13",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Package_sets",

+         "byteoffset": 16912,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "15",

+         "level": "4",

+         "line": "Package sets",

+         "number": "6.14",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Kickstart",

+         "byteoffset": 17374,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "16",

+         "level": "4",

+         "line": "Kickstart",

+         "number": "6.15",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Upgrade",

+         "byteoffset": 18093,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "17",

+         "level": "4",

+         "line": "Upgrade",

+         "number": "6.16",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Internationalization_and_Localization",

+         "byteoffset": 20145,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "18",

+         "level": "4",

+         "line": "Internationalization and Localization",

+         "number": "6.17",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Miscellaneous",

+         "byteoffset": 20855,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Installation",

+         "index": "19",

+         "level": "4",

+         "line": "Miscellaneous",

+         "number": "6.18",

+         "toclevel": 2

+     }

+ ]

@@ -0,0 +1,833 @@ 

+ {{Release_validation_instructions|testtype=Installation|release=32|milestone=Branched|compose=|date=20200322.n.0}}

+ 

+ {{anchor|results}}

+ == '''Test Matrix''' ==

+ 

+ <onlyinclude>

+ Please click ''[show]'' in each table to view the tests of each media installation, and click ''[edit]'' to post your test results using the syntax in [[#Key|Key Section]]. 

+ 

+ <div style="background-color: darkgreen; text-align: center; border-style: solid; border-width: thin">

+ ==== Image sanity ====

+ </div>

+ {{admon/note|Details|The result column titles are ''variants'' in Pungi/productmd parlance. For each variant, the checksums for all images in that variant can be checked; the maximum size for all images in that variant which have a maximum size can be checked; and repoclosure and fileconflicts can be checked for any DVD image in that variant. Any failure for any tested image should be filed as a bug and reported as a failure here. Please provide the bug ID and a '''short''' note of exactly which image(s) failed as a comment.}}

+ 

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ !style="width:10em"|Milestone !! Test Case !! Everything !! Server !! Workstation !! Cloud !! Spins !! Labs

+ |-

+ | Basic / Final

+ | [[QA:Testcase_Mediakit_Checksums]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_Mediakit_ISO_Size]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|fail|coconut|bot=true}}<ref>LXQt live x86_64, size 1456766976, max 1400000000</ref><ref>SoaS live x86_64, size 1121878016, max 734003200</ref>

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_Mediakit_Repoclosure]]

+ | style="background:lightgrey;"| 

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"| 

+ | style="background:lightgrey;"| 

+ | style="background:lightgrey;"| 

+ | style="background:lightgrey;"| 

+ |-

+ | Final

+ | [[QA:Testcase_Mediakit_FileConflicts]]

+ | style="background:lightgrey;"| 

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"| 

+ | style="background:lightgrey;"| 

+ | style="background:lightgrey;"| 

+ | style="background:lightgrey;"| 

+ |-

+ |}

+ <references/>

+ <div style="background-color: lightgreen; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Default boot and install ====

+ </div>

+ {{admon/note|Single test table|In all of these tests, the test case used is [[QA:Testcase_Boot_default_install]]. That is where the links point. The same test needs to be run for multiple images, target platforms, and install media. Note that the non-installer-based ARM disk images are covered by the later [[#ARM disk images]] section. The '''VM''' columns are for results from testing in a virtual machine. The '''CD/DVD''' columns are for results from testing on a real system with the image written to a real CD or DVD. The '''USB''' columns are for results from testing on a real system with the image written to a USB stick.}}

+ 

+ {{admon/note|Expected coverage|For Beta, we expect a reasonable sampling of tests across the table, with at least some testing for VM and USB boot method, both firmware types, and each major class of deliverable (netinst, live and DVD). For Final, we expect full coverage for ''Basic / Final'' rows with VM and USB boot method. Optical boot testing from physical media in Final is optional (but blocking if issues are found) for [[Releases/{{FedoraVersionNumber|next|short}}/ReleaseBlocking|supported images]].}}

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ !style="width:10em"|Milestone !! Image !! VM BIOS !! VM UEFI !! USB BIOS !! USB UEFI !! CD/DVD BIOS (Optional) !! CD/DVD UEFI (Optional)

+ |-

+ | Basic / Final

+ | [[QA:Testcase_Boot_default_install|Workstation live]]

+ | {{result|none}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Basic / Final

+ | [[QA:Testcase_Boot_default_install|Everything netinst]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Basic / Final

+ | [[QA:Testcase_Boot_default_install|Server netinst]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Basic / Final

+ | [[QA:Testcase_Boot_default_install|Server dvd]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Basic / Final

+ | [[QA:Testcase_Boot_default_install|KDE live]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Boot_default_install|Silverblue dvd-ostree]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Boot_default_install|Xfce live]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Boot_default_install|LXDE live]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Boot_default_install|LXQt live]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Boot_default_install|MATE live]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Boot_default_install|Cinnamon live]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Boot_default_install|SoaS live]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ 

+ <div style="background-color: MediumSlateBlue; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Fedora Media Writer ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- <!-- this next line uses complex wiki magic, see https://www.mediawiki.org/wiki/Help:Substitution . please don't mess with it unless you understand it. -->

+ !style="width:10em"|Milestone !! Test Case !! Fedora 30 !! Fedora  31 !! Fedora  32 !! Windows 10 !! macOS

+ |-

+ | Beta / Optional

+ | [[QA:Testcase_USB_fmw]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|pass|alciregi}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ 

+ <div style="background-color: yellowgreen; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== ARM disk images ====

+ </div>

+ {{admon/note|Single test table|In all of these tests, the test case used is [[QA:Testcase_arm_image_deployment]]. That is where the links point. The same test needs to be run for multiple images and target platforms.}}

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ !style="width:10em"|Milestone !! Image !! Ext boot  

+ |-

+ | Basic

+ | [[QA:Testcase_arm_image_deployment|Minimal]]

+ | {{result|pass|pwhalen}}

+ |-

+ | Basic

+ | [[QA:Testcase_arm_image_deployment|Workstation]]

+ | {{result|pass|pwhalen}}

+ |-

+ | Optional

+ | [[QA:Testcase_arm_image_deployment|KDE]]

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_arm_image_deployment|Xfce]]

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_arm_image_deployment|LXDE]]

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_arm_image_deployment|LXQt]]

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_arm_image_deployment|MATE]]

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_arm_image_deployment|SoaS]]

+ | {{result|none}}

+ |-

+ |}

+ 

+ <references/>

+ <div style="background-color: dodgerblue; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== PXE boot ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! x86_64 !! ARM

+ |-

+ | Beta

+ | [[QA:Testcase_Boot_Methods_Pxeboot]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: teal; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Virtualization ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! x86_64 BIOS !! x86_64 UEFI !! ARM

+ |-

+ | Basic

+ | [[QA:Testcase_Install_to_Previous_KVM]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_Install_to_Current_KVM]]

+ | {{result|pass|alciregi}}

+ | {{result|none}}

+ | {{result|pass|pwhalen}}

+ |-

+ | Optional

+ | [[QA:Testcase_Boot_Methods_Xen_Para_Virt]]

+ | {{result|none}}

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: orange; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Storage devices ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ !style="width:10em"|Milestone !! Test Case !! x86_64 !! ARM

+ |-

+ | Basic

+ | [[QA:Testcase_install_to_SATA]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ | Basic

+ | [[QA:Testcase_install_to_PATA]]

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"| 

+ |-

+ | Basic

+ | [[QA:Testcase_install_to_SCSI]]

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"|

+ |-

+ | Basic

+ | [[QA:Testcase_install_to_SAS]]

+ | {{result|none}}

+ | style="background:lightgrey;"| 

+ |-

+ | Beta

+ | [[QA:Testcase_install_to_firmware_RAID]]

+ | {{result|none}}

+ | style="background:lightgrey;"| 

+ |-

+ | Beta

+ | [[QA:Testcase_install_to_hardware_RAID]]

+ | {{result|none}}

+ | style="background:lightgrey;"| 

+ |-

+ | Beta

+ | [[QA:Testcase_install_to_VirtIO]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ | Final

+ | [[QA:Testcase_install_to_iSCSI_no_authentication]]

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"| 

+ |-

+ | Final

+ | [[QA:Testcase_install_to_FCoE_target]]

+ | {{result|none}}

+ | style="background:lightgrey;"| 

+ |-

+ | Optional

+ | [[QA:Testcase_install_to_multipath]]

+ | {{result|none}}

+ | style="background:lightgrey;"| 

+ |-

+ |}

+ <references/>

+ <div style="background-color: darkgoldenrod; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Guided storage configuration ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! x86_64 BIOS !! x86_64 UEFI !! ARM

+ |-

+ | Basic

+ | [[QA:Testcase_partitioning_guided_empty]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ | Basic

+ | [[QA:Testcase_partitioning_guided_delete_all]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ | Basic

+ | [[QA:Testcase_partitioning_guided_multi_select]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"|

+ |-

+ | Beta

+ | [[QA:Testcase_partitioning_guided_delete_partial]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"|

+ |-

+ | Beta

+ | [[QA:Testcase_partitioning_guided_free_space]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"|

+ |-

+ | Beta

+ | [[QA:Testcase_partitioning_guided_encrypted]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ | Beta

+ | [[QA:Testcase_partitioning_guided_multi_empty_all]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"|

+ |-

+ |}

+ <references/>

+ <div style="background-color: burlywood; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Guided storage shrinking ====

+ </div>

+ {{admon/note|Environments|For this test, the column headings refer to the storage volume type ''to be shrunk'', not the one chosen to replace it for the new installation.}}

+ {| class="wikitable sortable" width = 100%

+ |-

+ !style="width:10em"|Milestone !! Test Case !! ext4 !! NTFS

+ |-

+ | Final

+ | [[QA:Testcase_partitioning_guided_shrink]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: gold; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Custom storage configuration ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ !style="width:10em"|Milestone !! Test Case !! x86_64 BIOS !! x86_64 UEFI !! ARM

+ |-

+ | Beta

+ | [[QA:Testcase_partitioning_custom_software_RAID]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"| 

+ |-

+ | Final

+ | [[QA:Testcase_partitioning_custom_btrfs]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Final

+ | [[QA:Testcase_partitioning_custom_lvmthin]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Final

+ | [[QA:Testcase_partitioning_custom_standard_partition_ext3]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Final

+ | [[QA:Testcase_partitioning_custom_standard_partition_xfs]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_partitioning_custom_no_swap]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: darkorange; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Advanced custom storage configuration ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ !style="width:10em"|Milestone !! Test Case !! x86_64 BIOS !! x86_64 UEFI !! ARM

+ |-

+ | Beta

+ | [[QA:Testcase_partitioning_blivet_software_RAID]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"| 

+ |-

+ | Final

+ | [[QA:Testcase_partitioning_blivet_btrfs]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Final

+ | [[QA:Testcase_partitioning_blivet_lvmthin]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Final

+ | [[QA:Testcase_partitioning_blivet_standard_partition_ext3]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Final

+ | [[QA:Testcase_partitioning_blivet_standard_partition_xfs]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_partitioning_blivet_no_swap]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: orchid; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== User interface ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! Result

+ |-

+ | Basic

+ | [[QA:Testcase_Anaconda_User_Interface_Graphical]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Basic

+ | [[QA:Testcase_Anaconda_User_Interface_Text]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Basic

+ | [[QA:Testcase_Anaconda_User_Interface_VNC]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Basic

+ | [[QA:Testcase_Anaconda_User_Interface_VNC_Vncconnect]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ |}

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! x86_64 BIOS !! x86_64 UEFI

+ |-

+ | Basic / Final

+ | [[QA:Testcase_Anaconda_User_Interface_Basic_Video_Driver]]

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ |}

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! x86_64 !! ARM

+ |-

+ | Beta

+ | [[QA:Testcase_Anaconda_User_Interface_serial_console]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: lavender; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Installation repositories ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! Result

+ |-

+ | Basic

+ | [[QA:Testcase_install_repository_Mirrorlist_graphical]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Basic

+ | [[QA:Testcase_install_repository_HTTP/FTP_graphical]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Basic

+ | [[QA:Testcase_install_repository_HTTP/FTP_variation]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Beta

+ | [[QA:Testcase_install_repository_NFS_graphical]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Beta

+ | [[QA:Testcase_install_repository_NFS_variation]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_install_repository_NFSISO_variation]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_install_repository_Hard_drive_variation]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: plum; text-align: center; border-style: solid; border-width: thin">

+ ==== Package sets ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! Result

+ |-

+ | Beta

+ | [[QA:Testcase_Package_Sets_Minimal_Package_Install]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Optional

+ | [[QA:Testcase_Package_Sets_KDE_Package_Install]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: coral; text-align: center; border-style: solid; border-width: thin">

+ ==== Kickstart ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! Result

+ |-

+ | Basic

+ | [[QA:Testcase_kickstart_user_creation]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Beta

+ | [[QA:Testcase_Kickstart_Http_Server_Ks_Cfg]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Beta

+ | [[QA:Testcase_Kickstart_File_Path_Ks_Cfg]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Beta

+ | [[QA:Testcase_Kickstart_Hd_Device_Path_Ks_Cfg]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Beta

+ | [[QA:Testcase_Kickstart_Nfs_Server_Path_Ks_Cfg]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: pink; text-align: center; border-style: solid; border-width: thin">

+ ==== Upgrade ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! x86_64 !! ARM

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_gnome-software_current_workstation]]

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_gnome-software_previous_workstation]]

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_current_workstation]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_previous_workstation]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_current_workstation_encrypted]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_previous_workstation_encrypted]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_current_server]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_previous_server]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_current_kde]]

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"|

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_previous_kde]]

+ | {{result|pass|coconut|bot=true}}

+ | style="background:lightgrey;"|

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_current_minimal]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_previous_minimal]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|pwhalen}}

+ |-

+ |}

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! x86_64 BIOS !! x86_64 UEFI

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_current_any]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_previous_any]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ |-

+ |}

+ <references/>

+ 

+ <div style="background-color: lightpink; text-align: center; border-style: solid; border-width: thin">

+ 

+ ==== Internationalization and Localization ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! Result

+ |-

+ | Final

+ | [[QA:Testcase_Arabic_Language_Install]]

+ | {{result|none}}

+ |-

+ | Final

+ | [[QA:Testcase_Asian_Language_Install]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_Cyrillic_Language_Install]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_Non-English_European_Language_Install]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Optional

+ | [[QA:Testcase_i18n_default_fonts]]

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ <div style="background-color: mistyrose; text-align: center; border-style: solid; border-width: thin">

+ ==== Miscellaneous ====

+ </div>

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! x86_64 !! ARM

+ |-

+ | Basic

+ | [[QA:Testcase_Anaconda_user_creation]]

+ | {{result|pass|coconut|bot=true}}{{result|pass|alciregi}}

+ | {{result|pass|pwhalen}}

+ |-

+ |}

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! Result

+ |-

+ | Basic

+ | [[QA:Testcase_Anaconda_save_traceback_to_bugzilla]] (GUI mode)

+ | {{result|none}}

+ |-

+ | Basic

+ | [[QA:Testcase_Anaconda_save_traceback_to_bugzilla]] (text mode)

+ | {{result|none}}

+ |-

+ | Basic

+ | [[QA:Testcase_Anaconda_updates.img_via_URL]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_Anaconda_updates.img_via_installation_source]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_Anaconda_updates.img_via_local_media]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_Anaconda_help]]

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Anaconda_traceback_debug_mode]] (GUI mode)

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Anaconda_traceback_debug_mode]] (text mode)

+ | {{result|none}}

+ |-

+ |}

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |- 

+ !style="width:10em"|Milestone !! Test Case !! x86_64 BIOS !! x86_64 UEFI

+ |-

+ | Beta

+ | [[QA:Testcase_Anaconda_rescue_mode]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_dualboot_with_windows]]

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ | Final

+ | [[QA:Testcase_dualboot_with_macOS]]

+ | style="background:lightgrey;"|

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_Install_Image_Boot_Local]]

+ | {{result|none}}

+ | style="background:lightgrey;"|

+ |-

+ | Optional

+ | [[QA:Testcase_Memtest86]]

+ | {{result|none}}

+ | style="background:lightgrey;"|

+ |-

+ |}

+ <references/></onlyinclude>

+ 

+ [[Category:Installation_validation_testing]]

+ {{#if:20200322.n.0|[[Category:Fedora_32_Nightly_Test_Results|Installation]]|[[Category:Fedora_32_Branched_Test_Results|Installation]]}}

+ 

@@ -0,0 +1,132 @@ 

+ [

+     {

+         "anchor": "Which_tests_to_run",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-1",

+         "level": "2",

+         "line": "Which tests to run",

+         "number": "1",

+         "toclevel": 1

+     },

+     {

+         "anchor": "How_to_test",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-2",

+         "level": "2",

+         "line": "How to test",

+         "number": "2",

+         "toclevel": 1

+     },

+     {

+         "anchor": "Results_summary_page",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-3",

+         "level": "2",

+         "line": "Results summary page",

+         "number": "3",

+         "toclevel": 1

+     },

+     {

+         "anchor": "Add.2C_Modify_or_Remove_a_Test_Case",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-4",

+         "level": "2",

+         "line": "Add, Modify or Remove a Test Case",

+         "number": "4",

+         "toclevel": 1

+     },

+     {

+         "anchor": "Key",

+         "byteoffset": null,

+         "fromtitle": "Template:Release_validation_instructions",

+         "index": "T-5",

+         "level": "2",

+         "line": "Key",

+         "number": "5",

+         "toclevel": 1

+     },

+     {

+         "anchor": "Test_Matrix",

+         "byteoffset": 129,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Server",

+         "index": "1",

+         "level": "2",

+         "line": "<b>Test Matrix</b>",

+         "number": "6",

+         "toclevel": 1

+     },

+     {

+         "anchor": "General_tests",

+         "byteoffset": 168,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Server",

+         "index": "2",

+         "level": "4",

+         "line": "General tests",

+         "number": "6.1",

+         "toclevel": 2

+     },

+     {

+         "anchor": "FreeIPA_server_installation_and_functionality_tests",

+         "byteoffset": 1058,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Server",

+         "index": "3",

+         "level": "4",

+         "line": "FreeIPA server installation and functionality tests",

+         "number": "6.2",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Domain_joining_tests:_FreeIPA",

+         "byteoffset": 1883,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Server",

+         "index": "4",

+         "level": "4",

+         "line": "Domain joining tests: <b>FreeIPA</b>",

+         "number": "6.3",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Domain_joining_tests:_Active_Directory",

+         "byteoffset": 2294,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Server",

+         "index": "5",

+         "level": "4",

+         "line": "Domain joining tests: <b>Active Directory</b>",

+         "number": "6.4",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Domain_client_tests",

+         "byteoffset": 2663,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Server",

+         "index": "6",

+         "level": "4",

+         "line": "Domain client tests",

+         "number": "6.5",

+         "toclevel": 2

+     },

+     {

+         "anchor": "PostgreSQL_server_installation_and_functionality_tests",

+         "byteoffset": 3086,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Server",

+         "index": "7",

+         "level": "4",

+         "line": "PostgreSQL server installation and functionality tests",

+         "number": "6.6",

+         "toclevel": 2

+     },

+     {

+         "anchor": "Upgrade_tests",

+         "byteoffset": 3501,

+         "fromtitle": "Test_Results:Fedora_32_Branched_20200322.n.0_Server",

+         "index": "8",

+         "level": "4",

+         "line": "Upgrade tests",

+         "number": "6.7",

+         "toclevel": 2

+     }

+ ]

@@ -0,0 +1,184 @@ 

+ {{Release_validation_instructions|testtype=Server|release=32|milestone=Branched|compose=|date=20200322.n.0}}

+ 

+ {{anchor|results}}

+ == '''Test Matrix''' ==

+ 

+ <onlyinclude>

+ ==== General tests ====

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ ! Milestone !! Test Case !! x86_64 !! aarch64

+ |-

+ | Basic

+ | [[QA:Testcase_kickstart_firewall_disabled]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Basic

+ | [[QA:Testcase_kickstart_firewall_configured]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Basic

+ | [[ QA:Testcase_Server_firewall_default]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Basic

+ | [[QA:Testcase_Server_cockpit_default]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Basic

+ | [[QA:Testcase_Remote_Logging]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_Server_cockpit_basic]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_Server_filesystem_default]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ 

+ ==== FreeIPA server installation and functionality tests ====

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ ! Milestone !! Test Case !! x86_64 !! aarch64

+ |-

+ | Basic

+ | [[QA:Testcase_freeipa_trust_server_installation]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Basic

+ | [[QA:Testcase_freeipa_trust_server_uninstallation]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_FreeIPA_web_ui]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_FreeIPA_password_change]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_freeipa_replication]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Optional

+ | [[QA:Testcase_freeipa_replication_advanced]]

+ | {{result|none}}

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ 

+ ==== Domain joining tests: '''FreeIPA''' ====

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ ! Milestone !! Test Case !! Result

+ |-

+ | Basic

+ | [[QA:Testcase_realmd_join_kickstart]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Basic

+ | [[QA:Testcase_realmd_join_sssd]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Beta

+ | [[QA:Testcase_realmd_join_cockpit]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ |}

+ <references/>

+ 

+ ==== Domain joining tests: '''Active Directory''' ====

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ ! Milestone !! Test Case !! Result

+ |-

+ | Basic

+ | [[QA:Testcase_realmd_join_kickstart]]

+ | {{result|none}}

+ |-

+ | Basic

+ | [[QA:Testcase_realmd_join_sssd]]

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_realmd_join_cockpit]]

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ 

+ ==== Domain client tests ====

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ ! Milestone !! Test Case !! Result

+ |-

+ | Beta

+ | [[QA:Testcase_domain_client_authenticate]] (FreeIPA)

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Final

+ | [[QA:Testcase_FreeIPA_realmd_login]]

+ | {{result|pass|coconut|bot=true}}

+ |-

+ | Optional

+ | [[QA:Testcase_domain_client_authenticate]] (Active Directory)

+ | {{result|none}}

+ |}

+ <references/>

+ 

+ ==== PostgreSQL server installation and functionality tests ====

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ ! Milestone !! Test Case !! x86_64 !! aarch64

+ |-

+ | Basic

+ | [[QA:Testcase_postgresql_server_installation]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_database_server_remote_client]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ |}

+ <references/>

+ 

+ ==== Upgrade tests ====

+ {| class="wikitable sortable mw-collapsible" width=100%

+ |-

+ ! Milestone !! Test Case !! x86_64 !! aarch64

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_current_server_domain_controller]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ | Beta

+ | [[QA:Testcase_upgrade_dnf_previous_server_domain_controller]]

+ | {{result|pass|coconut|bot=true}}

+ | {{result|none}}

+ |-

+ |}

+ <references/></onlyinclude>

+ 

+ [[Category:Server_validation_testing]]

+ {{#if:20200322.n.0|[[Category:Fedora_32_Nightly_Test_Results|Server]]|[[Category:Fedora_32_Branched_Test_Results|Server]]}}

file modified
+314 -100
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2016 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -18,41 +18,36 @@ 

  # Author: Adam Williamson <awilliam@redhat.com>

  

  # these are all kinda inappropriate for pytest patterns

- # pylint: disable=old-style-class, no-init, protected-access, no-self-use, unused-argument

+ # pylint: disable=no-init, protected-access, no-self-use, unused-argument, too-many-arguments

  # pylint: disable=invalid-name

  

  """Tests for event.py."""

  

- from __future__ import unicode_literals

- from __future__ import print_function

+ from unittest import mock

  

  import fedfind.release

- # note: we need the external 'mock', unittest.mock does not seem to

- # work correctly with the @patch decorator still

- import mock

+ import mwclient.errors

+ import mwclient.page

  import pytest

+ 

  import wikitcms.event

+ import wikitcms.listing

  import wikitcms.wiki as wk

  from wikitcms.exceptions import FedfindNotFoundError

  

- def fakemwpinit(self, site, name, *args, **kwargs):

-     """Stub init for mwclient.Page, we can't just mock it out as we

-     need to set site and name.

-     """

-     self.site = site

-     self.name = name

  

  class TestEventFedfind:

      """Tests related to fedfind release discovery from validation

      events.

      """

+ 

      # the 'fjajah' is just to make sure we're running offline; if I

      # screw up and add a test that hits the network it'll cause the

      # tests to hang/fail instead of succeeding a bit slower

-     site = wk.Wiki('fjajah', do_init=False, force_login=False)

+     site = wk.Wiki("fjajah", do_init=False, force_login=False)

  

-     @mock.patch('fedfind.release.Compose.exists', True)

-     @mock.patch('fedfind.release.Compose.all_images', ['foo'])

+     @mock.patch("fedfind.release.Compose.exists", True)

+     @mock.patch("fedfind.release.Compose.all_images", ["foo"])

      def test_candidate_ff_release_compose(self):

          """Straightforward ff_release test case for a candidate

          compose which is properly synced to stage. Should work the
@@ -60,17 +55,18 @@ 

          should exist and be the Compose instance.

          """

          event = wikitcms.event.ComposeEvent(

-             self.site, '27', 'RC', '1.6', cid='Fedora-27-20171105.0')

+             self.site, "27", "RC", "1.6", cid="Fedora-27-20171105.0"

+         )

          assert isinstance(event.ff_release, fedfind.release.Compose)

          assert isinstance(event.ff_release_images, fedfind.release.Compose)

-         event = wikitcms.event.ComposeEvent(self.site, '27', 'RC', '1.6')

+         event = wikitcms.event.ComposeEvent(self.site, "27", "RC", "1.6")

          assert isinstance(event.ff_release, fedfind.release.Compose)

          assert isinstance(event.ff_release_images, fedfind.release.Compose)

  

-     @mock.patch('fedfind.helpers.cid_from_label', return_value='')

-     @mock.patch('fedfind.release.Compose.exists', False)

-     @mock.patch('fedfind.release.Production.all_images', ['foo'])

-     @mock.patch('fedfind.release.Production.cid', 'Fedora-27-20171105.0')

+     @mock.patch("fedfind.helpers.cid_from_label", return_value="")

+     @mock.patch("fedfind.release.Compose.exists", False)

+     @mock.patch("fedfind.release.Production.all_images", ["foo"])

+     @mock.patch("fedfind.release.Production.cid", "Fedora-27-20171105.0")

      def test_candidate_ff_release_compose_gap(self, fakecidfromlabel):

          """Test the 'compose gap' case: this occurs when a candidate

          compose has just been created, but not yet synced to stage,
@@ -80,19 +76,20 @@ 

          find it in the non-synced location (as a fedfind Production).

          """

          event = wikitcms.event.ComposeEvent(

-             self.site, '27', 'RC', '1.6', cid='Fedora-27-20171105.0')

+             self.site, "27", "RC", "1.6", cid="Fedora-27-20171105.0"

+         )

          assert isinstance(event.ff_release, fedfind.release.Production)

          assert isinstance(event.ff_release_images, fedfind.release.Production)

-         event = wikitcms.event.ComposeEvent(self.site, '27', 'RC', '1.6')

+         event = wikitcms.event.ComposeEvent(self.site, "27", "RC", "1.6")

          with pytest.raises(FedfindNotFoundError):

              print(event.ff_release)

          with pytest.raises(FedfindNotFoundError):

              print(event.ff_release_images)

  

-     @mock.patch('fedfind.helpers.cid_from_label', return_value='Fedora-27-20171105.0')

-     @mock.patch('fedfind.release.Compose.exists', False)

-     @mock.patch('fedfind.release.Production.all_images', ['foo'])

-     @mock.patch('fedfind.release.Production.cid', 'Fedora-27-20171105.0')

+     @mock.patch("fedfind.helpers.cid_from_label", return_value="Fedora-27-20171105.0")

+     @mock.patch("fedfind.release.Compose.exists", False)

+     @mock.patch("fedfind.release.Production.all_images", ["foo"])

+     @mock.patch("fedfind.release.Production.cid", "Fedora-27-20171105.0")

      def test_candidate_ff_release_compose_gap_pdc(self, fakecidfromlabel):

          """Test the case where the candidate compose has not yet

          synced to stage, but has appeared in PDC. In this case, we
@@ -100,17 +97,18 @@ 

          fedfind Production) with or without the cid hint.

          """

          event = wikitcms.event.ComposeEvent(

-             self.site, '27', 'RC', '1.6', cid='Fedora-27-20171105.0')

+             self.site, "27", "RC", "1.6", cid="Fedora-27-20171105.0"

+         )

          assert isinstance(event.ff_release, fedfind.release.Production)

          assert isinstance(event.ff_release_images, fedfind.release.Production)

-         event = wikitcms.event.ComposeEvent(self.site, '27', 'RC', '1.6')

+         event = wikitcms.event.ComposeEvent(self.site, "27", "RC", "1.6")

          assert isinstance(event.ff_release, fedfind.release.Production)

          assert isinstance(event.ff_release_images, fedfind.release.Production)

  

-     @mock.patch('fedfind.release.Compose.exists', True)

-     @mock.patch('fedfind.release.Compose.all_images', [])

-     @mock.patch('fedfind.release.Production.all_images', ['foo'])

-     @mock.patch('fedfind.release.Production.cid', 'Fedora-27-20171105.0')

+     @mock.patch("fedfind.release.Compose.exists", True)

+     @mock.patch("fedfind.release.Compose.all_images", [])

+     @mock.patch("fedfind.release.Production.all_images", ["foo"])

+     @mock.patch("fedfind.release.Production.cid", "Fedora-27-20171105.0")

      def test_candidate_ff_release_compose_exists_no_images(self):

          """Test a potential tricky case where a candidate compose

          tree exists on stage but the images haven't shown up in it
@@ -119,80 +117,111 @@ 

          Production instance. Without the hint, we won't get images.

          """

          event = wikitcms.event.ComposeEvent(

-             self.site, '27', 'RC', '1.6', cid='Fedora-27-20171105.0')

+             self.site, "27", "RC", "1.6", cid="Fedora-27-20171105.0"

+         )

          assert isinstance(event.ff_release, fedfind.release.Compose)

          assert isinstance(event.ff_release_images, fedfind.release.Production)

-         event = wikitcms.event.ComposeEvent(self.site, '27', 'RC', '1.6')

+         event = wikitcms.event.ComposeEvent(self.site, "27", "RC", "1.6")

          assert isinstance(event.ff_release, fedfind.release.Compose)

          with pytest.raises(FedfindNotFoundError):

              assert event.ff_release_images

  

-     @mock.patch('fedfind.release.BranchedNightly.exists', True)

-     @mock.patch('fedfind.release.BranchedNightly.all_images', ['foo'])

+     @mock.patch("fedfind.release.BranchedNightly.exists", True)

+     @mock.patch("fedfind.release.BranchedNightly.all_images", ["foo"])

      def test_candidate_ff_release_nightly(self):

          """Straightforward ff_release test case for a nightly

          compose which exists and has images.

          """

-         event = wikitcms.event.NightlyEvent(self.site, '27', 'Branched', '20171104.n.0')

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

          assert isinstance(event.ff_release, fedfind.release.BranchedNightly)

          assert isinstance(event.ff_release_images, fedfind.release.BranchedNightly)

  

-     @mock.patch('fedfind.release.BranchedNightly.exists', False)

-     @mock.patch('fedfind.release.BranchedNightly.all_images', [])

+     @mock.patch("fedfind.release.BranchedNightly.exists", False)

+     @mock.patch("fedfind.release.BranchedNightly.all_images", [])

      def test_candidate_ff_release_nightly_no_images(self):

          """ff_release test case for a nightly compose which doesn't

          exist and has no images. We get ff_release (as fedfind doesn't

          do an existence check in this case), but not images.

          """

-         event = wikitcms.event.NightlyEvent(self.site, '27', 'Branched', '20171104.n.0')

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

          assert isinstance(event.ff_release, fedfind.release.BranchedNightly)

          with pytest.raises(FedfindNotFoundError):

              assert event.ff_release_images

  

- @mock.patch('wikitcms.page.mwp.Page.__init__', fakemwpinit)

- @mock.patch('wikitcms.page.Page.save', autospec=True)

- @mock.patch('wikitcms.page.SummaryPage.update_current', autospec=True)

- @mock.patch('wikitcms.page.ValidationPage.update_current', autospec=True)

- @mock.patch('wikitcms.event.ValidationEvent.update_current', autospec=True)

- @mock.patch('test_event.wk.Wiki.testtypes', ['Installation', 'Base', 'Server', 'Cloud', 'Desktop'])

- @mock.patch('fedfind.release.BranchedNightly.cid', 'Fedora-27-20171104.n.0')

- @mock.patch('fedfind.helpers.download_json', return_value={

-     'arguments': {},

-     'count': 1,

-     'pages': 1,

-     'total': 1,

-     'raw_messages': [{

-         'msg': {

-             'architecture': 'x86_64',

-             'compose': 'Fedora-27-20171104.n.0',

-             'destination': 'eu-west-2',

-             'extra': {

-                 'id': 'ami-085e29c4cd80e326d',

-                 'virt_type': 'hvm',

-                 'vol_type': 'gp2',

-             },

-         }

-     }]})

+     @mock.patch("fedfind.release.ModularCompose.exists", True)

+     @mock.patch("fedfind.release.ModularCompose.all_images", ["foo"])

+     def test_candidate_ff_release_modular(self):

+         """Straightforward ff_release test case for a modular

+         candidate compose which is properly synced to stage.

+         """

+         event = wikitcms.event.ComposeEvent(

+             self.site, "27", "Beta", "1.5", modular=True, cid="Fedora-Modular-27-20171108.2"

+         )

+         assert isinstance(event.ff_release, fedfind.release.ModularCompose)

+         assert isinstance(event.ff_release_images, fedfind.release.ModularCompose)

+         event = wikitcms.event.ComposeEvent(self.site, "27", "Beta", "1.5", modular=True)

+         assert isinstance(event.ff_release, fedfind.release.ModularCompose)

+         assert isinstance(event.ff_release_images, fedfind.release.ModularCompose)

+ 

+ 

+ @pytest.mark.usefixtures("fakemwp")

+ @mock.patch("wikitcms.page.Page.save", autospec=True)

+ @mock.patch("wikitcms.page.SummaryPage.update_current", autospec=True)

+ @mock.patch("wikitcms.page.ValidationPage.update_current", autospec=True)

+ @mock.patch("wikitcms.event.ValidationEvent.update_current", autospec=True)

+ @mock.patch("test_event.wk.Wiki.testtypes", ["Installation", "Base", "Server", "Cloud", "Desktop"])

+ @mock.patch("fedfind.release.BranchedNightly.cid", "Fedora-27-20171104.n.0")

+ @mock.patch(

+     "fedfind.helpers.download_json",

+     return_value={

+         "arguments": {},

+         "count": 1,

+         "pages": 1,

+         "total": 1,

+         "raw_messages": [

+             {

+                 "msg": {

+                     "architecture": "x86_64",

+                     "compose": "Fedora-27-20171104.n.0",

+                     "destination": "eu-west-2",

+                     "extra": {

+                         "id": "ami-085e29c4cd80e326d",

+                         "virt_type": "hvm",

+                         "vol_type": "gp2",

+                     },

+                 }

+             }

+         ],

+     },

+ )

  class TestEventCreate:

      """Tests related to event creation."""

-     site = wk.Wiki('fjajah', do_init=False, force_login=False)

- 

-     @mock.patch('fedfind.release.BranchedNightly.exists', True)

-     @mock.patch('fedfind.release.BranchedNightly.all_images',

-                 [{

-                     'arch': 'x86_64',

-                     'format': 'iso',

-                     'path': ('Workstation/x86_64/iso/'

-                              'Fedora-Workstation-Live-x86_64-27-2011104.n.0.iso'),

-                     'subvariant': 'Workstation',

-                     'type': 'live',

-                     'url': ('https://kojipkgs.fedoraproject.org/compose/branched/'

-                             'Fedora-27-20171104.n.0/Workstation/x86_64/iso/'

-                             'Fedora-Workstation-Live-x86_64-27-2011104.n.0.iso'),

-                 }])

+ 

+     site = wk.Wiki("fjajah", do_init=False, force_login=False)

+ 

+     @mock.patch("fedfind.release.BranchedNightly.exists", True)

+     @mock.patch(

+         "fedfind.release.BranchedNightly.all_images",

+         [

+             {

+                 "arch": "x86_64",

+                 "format": "iso",

+                 "path": (

+                     "Workstation/x86_64/iso/" "Fedora-Workstation-Live-x86_64-27-2011104.n.0.iso"

+                 ),

+                 "subvariant": "Workstation",

+                 "type": "live",

+                 "url": (

+                     "https://kojipkgs.fedoraproject.org/compose/branched/"

+                     "Fedora-27-20171104.n.0/Workstation/x86_64/iso/"

+                     "Fedora-Workstation-Live-x86_64-27-2011104.n.0.iso"

+                 ),

+             }

+         ],

+     )

      def test_event_create(self, fakejson, fakeevup, fakepageup, fakesumup, fakepagesave):

          """Test normal event creation."""

-         event = wikitcms.event.NightlyEvent(self.site, '27', 'Branched', '20171104.n.0')

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

          event.create()

          # we should save 5 test pages, plus the summary page,

          # download page, AMI page and two category pages
@@ -203,14 +232,17 @@ 

          assert fakesumup.call_count == 1

          # we should call update_current for the event itself

          assert fakeevup.call_count == 1

+         # verify we set createonly to True by default

+         for call in fakepagesave.call_args_list:

+             assert call[1]["createonly"] is True

  

-     @mock.patch('fedfind.release.BranchedNightly.exists', False)

-     @mock.patch('fedfind.release.BranchedNightly.all_images', [])

+     @mock.patch("fedfind.release.BranchedNightly.exists", False)

+     @mock.patch("fedfind.release.BranchedNightly.all_images", [])

      def test_event_create_no_images(self, fakejson, fakeevup, fakepageup, fakesumup, fakepagesave):

          """Test event creation where no images are available. This

          should succeed, but not create a download page.

          """

-         event = wikitcms.event.NightlyEvent(self.site, '27', 'Branched', '20171104.n.0')

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

          event.create()

          # we should save 5 test pages, plus the summary page and

          # two category pages and AMI page - but no download page
@@ -220,24 +252,32 @@ 

          # we should call update_current for the event itself

          assert fakeevup.call_count == 1

  

-     @mock.patch('wikitcms.page.mwp.Page.text', return_value="sometext")

-     @mock.patch('fedfind.release.BranchedNightly.exists', False)

-     @mock.patch('fedfind.release.BranchedNightly.all_images', [])

-     def test_event_create_check(self, fakepagetext, fakejson, fakeevup, fakepageup, fakesumup, fakepagesave):

-         """Test event creation when pages already exist and check is

-         True. We should raise an error in this case. Using the 'no

-         download page' case because the mocks are shorter.

-         """

-         event = wikitcms.event.NightlyEvent(self.site, '27', 'Branched', '20171104.n.0')

-         with pytest.raises(ValueError):

+     @mock.patch("fedfind.release.BranchedNightly.exists", False)

+     @mock.patch("fedfind.release.BranchedNightly.all_images", [])

+     def test_event_create_check(self, fakejson, fakeevup, fakepageup, fakesumup, fakepagesave):

+         """Test event creation with check=True."""

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

+         # first, let's say the pages had no content, so creation

+         # should go ahead...

+         with mock.patch("mwclient.page.Page.text", return_value=""):

              event.create(check=True)

+         # we should save 5 test pages, plus the summary page and

+         # two category pages and AMI page - but no download page

+         assert fakepagesave.call_count == 9

+         # now, let's say the pages *do* have content, we should get

+         # an exception now...

+         fakepagesave.reset_mock()

+         with mock.patch("mwclient.page.Page.text", return_value="foobar"):

+             with pytest.raises(ValueError):

+                 event.create(check=True)

+         assert fakepagesave.call_count == 0

  

-     @mock.patch('fedfind.release.BranchedNightly.exists', False)

-     @mock.patch('fedfind.release.BranchedNightly.all_images', [])

+     @mock.patch("fedfind.release.BranchedNightly.exists", False)

+     @mock.patch("fedfind.release.BranchedNightly.all_images", [])

      def test_event_create_testtypes(self, fakejson, fakeevup, fakepageup, fakesumup, fakepagesave):

          """Test event creation for a specified set of test types."""

-         event = wikitcms.event.NightlyEvent(self.site, '27', 'Branched', '20171104.n.0')

-         event.create(testtypes=['Installation', 'Server'])

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

+         event.create(testtypes=["Installation", "Server"])

          # we should save 2 test pages, plus the summary page and

          # two category pages and AMI page - but no download page

          assert fakepagesave.call_count == 6
@@ -245,3 +285,177 @@ 

          assert fakepageup.call_count == 2

          # we should call update_current for the event itself

          assert fakeevup.call_count == 1

+         # check correct handling of garbage list of testtypes

+         with pytest.raises(ValueError):

+             event.create(testtypes=["foo", "bar"])

+ 

+     @mock.patch("fedfind.release.BranchedNightly.exists", False)

+     @mock.patch("fedfind.release.BranchedNightly.all_images", [])

+     def test_event_create_force(self, fakejson, fakeevup, fakepageup, fakesumup, fakepagesave):

+         """Test event creation with force=True."""

+         # we only need to test that we properly set createonly=None

+         # here, we don't need to confirm or mock its effects

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

+         event.create(force=True)

+         for call in fakepagesave.call_args_list:

+             assert call[1]["createonly"] is None

+ 

+     @mock.patch("fedfind.release.BranchedNightly.exists", False)

+     @mock.patch("fedfind.release.BranchedNightly.all_images", [])

+     def test_event_create_existing(self, fakejson, fakeevup, fakepageup, fakesumup, fakepagesave):

+         """Test the handling when a page we try to create during

+         event creation already exists. The expected behaviour is that

+         we just log the issue and continue.

+         """

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

+         fakepagesave.side_effect = mwclient.errors.APIError("articleexists", "Article exists", {})

+         event.create()

+         # but if the error is something *else*, we should raise it

+         fakepagesave.side_effect = mwclient.errors.APIError("frobbled", "Article is frobbled", {})

+         with pytest.raises(mwclient.errors.APIError):

+             event.create()

+         fakepagesave.reset_mock()

+ 

+     @mock.patch("fedfind.release.BranchedNightly.exists", False)

+     @mock.patch("fedfind.release.BranchedNightly.all_images", [])

+     def test_event_create_nocurrent(self, fakejson, fakeevup, fakepageup, fakesumup, fakepagesave):

+         """Test that setting current=False correctly skips current

+         redirect updates.

+         """

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

+         event.create(current=False)

+         # we should save 5 test pages, plus the summary page and

+         # two category pages and AMI page - but no download page

+         assert fakepagesave.call_count == 9

+         # we should not call update_current for test pages

+         assert fakepageup.call_count == 0

+         # we should not call update_current for the event itself

+         assert fakeevup.call_count == 0

+ 

+ 

+ class TestEventOther:

+     """Tests for properties of Event instances and other methods."""

+ 

+     site = wk.Wiki("fjajah", do_init=False, force_login=False)

+ 

+     @mock.patch("mwclient.listing.GeneratorList.__next__")

+     def test_event_result_pages(self, fakenext, fakemwp):

+         """Test for result_pages property."""

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

+         # we need to mock the output of the generator the method uses

+         # it's an 'allresults' generator (so it uses the Test Results

+         # namespace) and the prefix should be "Fedora 27 Branched

+         # 20171104.n.0 ", so what we're expecting to get back is all

+         # the actual result pages plus the summary page, and we have

+         # to test the method correctly filters out the summary

+         evp1 = mwclient.page.Page(self.site, "Test Results:Fedora 27 Branched 20171104.n.0 Base")

+         evp2 = mwclient.page.Page(self.site, "Test Results:Fedora 27 Branched 20171104.n.0 Cloud")

+         evs = mwclient.page.Page(self.site, "Test Results:Fedora 27 Branched 20171104.n.0 Summary")

+         fakenext.side_effect = [evp1, evp2, evs, StopIteration]

+         rps = event.result_pages

+         assert len(rps) == 2

+         (rp1, rp2) = rps

+         assert isinstance(rp1, wikitcms.page.NightlyPage)

+         assert isinstance(rp2, wikitcms.page.NightlyPage)

+         assert rp1.testtype == "Base"

+         assert rp2.testtype == "Cloud"

+         assert rp1.modular is rp2.modular is False

+         # modular

+         event = wikitcms.event.NightlyEvent(

+             self.site, "27", "Branched", "20171123.n.1", modular=True

+         )

+         evp1 = mwclient.page.Page(

+             self.site, "Test Results:Fedora Modular 27 Branched 20171123.n.1 Base"

+         )

+         evp2 = mwclient.page.Page(

+             self.site, "Test Results:Fedora Modular 27 Branched 20171123.n.1 Server"

+         )

+         evs = mwclient.page.Page(

+             self.site, "Test Results:Fedora Modular 27 Branched 20171123.n.1 Summary"

+         )

+         fakenext.side_effect = [evp1, evp2, evs, StopIteration]

+         rps = event.result_pages

+         assert len(rps) == 2

+         (rp1, rp2) = rps

+         assert isinstance(rp1, wikitcms.page.NightlyPage)

+         assert isinstance(rp2, wikitcms.page.NightlyPage)

+         assert rp1.testtype == "Base"

+         assert rp2.testtype == "Server"

+         assert rp1.modular is rp2.modular is True

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_event_update_current(self, fakesave, fakemwp):

+         """Test for update_current() method."""

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

+         event.update_current()

+         assert fakesave.call_args[0][1] == (

+             "{{tempdoc}}\n<onlyinclude>{{#switch: {{{1|full}}}\n| full = 27 Branched 20171104.n.0"

+             "\n| release = 27\n| milestone = Branched\n| compose =\n| date = 20171104.n.0\n"

+             "}}</onlyinclude>\n[[Category: Fedora Templates]]"

+         )

+         assert fakesave.call_args[0][2] == "relval: update to current event"

+         assert fakesave.call_args[1]["createonly"] is None

+         assert fakesave.call_args[0][0].name == "Template:CurrentFedoraCompose"

+         # modular, non-nightly

+         event = wikitcms.event.ComposeEvent(self.site, "27", "Beta", "1.5", modular=True)

+         event.update_current()

+         assert fakesave.call_args[0][1] == (

+             "{{tempdoc}}\n<onlyinclude>{{#switch: {{{1|full}}}\n| full = 27 Beta 1.5"

+             "\n| release = 27\n| milestone = Beta\n| compose = 1.5\n| date =\n"

+             "}}</onlyinclude>\n[[Category: Fedora Templates]]"

+         )

+         assert fakesave.call_args[0][2] == "relval: update to current event"

+         assert fakesave.call_args[1]["createonly"] is None

+         assert fakesave.call_args[0][0].name == "Template:CurrentFedoraModularCompose"

+ 

+     def test_event_from_page(self, fakemwp):

+         """Test for from_page classmethod."""

+         pg = self.site.pages["Test Results:Fedora 27 Branched 20171104.n.0 Base"]

+         ev = wikitcms.event.NightlyEvent.from_page(pg)

+         assert ev.version == "27 Branched 20171104.n.0"

+         assert ev.modular is False

+         # modular

+         pg = self.site.pages["Test Results:Fedora Modular 27 Branched 20171123.n.1 Base"]

+         ev = wikitcms.event.NightlyEvent.from_page(pg)

+         assert ev.version == "27 Branched 20171123.n.1"

+         assert ev.modular is True

+ 

+     def test_event_category_page(self, fakemwp):

+         """Test for category_page property."""

+         # compose

+         event = wikitcms.event.ComposeEvent(self.site, "27", "Beta", "1.2")

+         cat = event.category_page

+         assert isinstance(cat, wikitcms.listing.ValidationCategory)

+         # this is a sufficient proxy that we got the right thing

+         assert cat.checkname == "Category:Fedora 27 Beta Test Results"

+         # modular compose

+         event = wikitcms.event.ComposeEvent(self.site, "27", "Beta", "1.5", modular=True)

+         cat = event.category_page

+         assert cat.checkname == "Category:Fedora Modular 27 Beta Test Results"

+         # nightly

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

+         cat = event.category_page

+         assert cat.checkname == "Category:Fedora 27 Nightly Test Results"

+         # modular nightly

+         event = wikitcms.event.NightlyEvent(

+             self.site, "27", "Branched", "20171123.n.1", modular=True

+         )

+         cat = event.category_page

+         assert cat.checkname == "Category:Fedora Modular 27 Nightly Test Results"

+ 

+     # yes, I looked up the correct date. don't make fun of me!

+     @mock.patch("wikitcms.page.ComposePage.creation_date", "20170922")

+     def test_event_creation_date(self, fakemwp):

+         """Test for creation_date property/attribute."""

+         # compose

+         evp = self.site.pages["Test Results:Fedora 27 Beta 1.2 Base"]

+         with mock.patch("wikitcms.event.ComposeEvent.result_pages", [evp]):

+             event = wikitcms.event.ComposeEvent(self.site, "27", "Beta", "1.2")

+             assert event.creation_date == "20170922"

+         with mock.patch("wikitcms.event.ComposeEvent.result_pages", []):

+             # this is when the event doesn't exist

+             event = wikitcms.event.ComposeEvent(self.site, "27", "Beta", "1.2")

+             assert event.creation_date == ""

+         # nightly - it's just an attribute here

+         event = wikitcms.event.NightlyEvent(self.site, "27", "Branched", "20171104.n.0")

+         assert event.creation_date == "20171104"

file modified
+77 -65
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2015 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -18,76 +18,82 @@ 

  # Author: Adam Williamson <awilliam@redhat.com>

  

  # these are all kinda inappropriate for pytest patterns

- # pylint: disable=old-style-class, no-init, protected-access, no-self-use, unused-argument

+ # pylint: disable=no-init, protected-access, no-self-use, unused-argument

  

- from __future__ import unicode_literals

+ """Tests for helpers.py."""

+ 

+ from unittest import mock

  

- import mock

- import os

  import pytest

+ 

  import wikitcms.helpers as hl

  

  # Only triplet_sort can handle these

  HARD_SORTS = [

-     ('9', 'Beta', 'RC1'),

-     ('10', 'Alpha', 'TC1'),

-     ('10', 'Alpha', 'TC2'),

-     ('10', 'Alpha', 'TC10'),

-     ('10', 'Alpha', 'RC1'),

+     ("9", "Beta", "RC1"),

+     ("10", "Alpha", "TC1"),

+     ("10", "Alpha", "TC2"),

+     ("10", "Alpha", "TC10"),

+     ("10", "Alpha", "RC1"),

  ]

  

  # Weird old ones

  OLD_SORTS = [

-     ('12', 'Alpha', 'TCRegression'),

-     ('12', 'Beta', 'PreBeta'),

-     ('12', 'Beta', 'TC'),

-     ('12', 'Final', 'Pre-RC'),

-     ('12', 'Final', 'RC1'),

-     ('21', 'Rawhide', '2014 06'),

-     ('21', 'Nightly', '2014 08'),

-     ('21', 'Alpha', 'TC1'),

+     ("12", "Alpha", "TCRegression"),

+     ("12", "Beta", "PreBeta"),

+     ("12", "Beta", "TC"),

+     ("12", "Final", "Pre-RC"),

+     ("12", "Final", "RC1"),

+     ("21", "Rawhide", "2014 06"),

+     ("21", "Nightly", "2014 08"),

+     ("21", "Alpha", "TC1"),

  ]

  

  # Standard stuff

  STANDARD_SORTS = [

-     ('22', 'Final', 'RC1'),

-     ('22', 'Postrelease', '20151015'),

-     ('23', 'Rawhide', '20150607'),

-     ('23', 'Branched', '20150717'),

-     ('23', 'Alpha', 'TC1'),

-     ('23', 'Alpha', 'TC2'),

-     ('23', 'Alpha', 'RC1'),

-     ('23', 'Beta', 'TC1'),

-     ('23', 'Beta', 'TC2'),

-     ('23', 'Final', 'RC1'),

-     ('23', 'Final', 'RC1.1'),

-     ('24', 'Rawhide', '20160314.n.0'),

-     ('24', 'Rawhide', '20160314.n.1'),

-     ('24', 'Branched', '20160314.n.0'),

-     ('24', 'Branched', '20160318.n.0'),

-     ('24', 'Alpha', '1.1'),

-     ('24', 'Alpha', '1.2'),

-     ('24', 'Alpha', '2.1'),

-     ('24', 'Beta', '1.1'),

+     ("22", "Final", "RC1"),

+     ("22", "Postrelease", "20151015"),

+     ("23", "Rawhide", "20150607"),

+     ("23", "Branched", "20150717"),

+     ("23", "Alpha", "TC1"),

+     ("23", "Alpha", "TC2"),

+     ("23", "Alpha", "RC1"),

+     ("23", "Beta", "TC1"),

+     ("23", "Beta", "TC2"),

+     ("23", "Final", "RC1"),

+     ("23", "Final", "RC1.1"),

+     ("24", "Rawhide", "20160314.n.0"),

+     ("24", "Rawhide", "20160314.n.1"),

+     ("24", "Branched", "20160314.n.0"),

+     ("24", "Branched", "20160318.n.0"),

+     ("24", "Alpha", "1.1"),

+     ("24", "Alpha", "1.2"),

+     ("24", "Alpha", "2.1"),

+     ("24", "Beta", "1.1"),

  ]

  

+ 

  class TestHelpers:

      """Tests for the functions in helpers.py."""

+ 

      def test_fedora_release_sort(self):

+         """Test for fedora_release_sort."""

          rels = OLD_SORTS + STANDARD_SORTS

          for (num, rel) in enumerate(rels[1:], 1):

-             prevrel = rels[num-1]

-             assert hl.fedora_release_sort(' '.join(rel)) > hl.fedora_release_sort(' '.join(prevrel))

+             prevrel = rels[num - 1]

+             assert hl.fedora_release_sort(" ".join(rel)) > hl.fedora_release_sort(" ".join(prevrel))

  

      def test_triplet_sort(self):

+         """Test for triplet_sort and triplet_unsort."""

          rels = HARD_SORTS + OLD_SORTS + STANDARD_SORTS

          for (num, rel) in enumerate(rels[1:], 1):

-             prevrel = rels[num-1]

+             prevrel = rels[num - 1]

              sorttup = hl.triplet_sort(*rel)

              assert sorttup > hl.triplet_sort(*prevrel)

              assert hl.triplet_unsort(*sorttup) == (rel)

  

      def test_rreplace(self):

+         """Tests for rreplace."""

          foos = "foofoofoo"

          bars = "barbarbar"

          assert hl.rreplace(bars, "bar", "foo", 1) == "barbarfoo"
@@ -98,12 +104,14 @@ 

          assert hl.rreplace(foos, "bar", "foo", 3) == "foofoofoo"

  

      def test_normalize(self):

+         """Tests for normalize."""

          wikitext = "Foo_Bar Moo"

          oktext = "foo_bar_moo"

          assert hl.normalize(wikitext) == oktext

          assert hl.normalize(oktext) == oktext

  

      def test_find_bugs(self):

+         """Test for find_bugs."""

          # text taken from a real Test Day page -

          # 2011-03-24_Power_Management - with a [[rhbug link

          # added as I couldn't find a page with all three. Some non-bug
@@ -117,41 +125,45 @@ 

  | {{result|warn}} <ref> [[rhbug:1239865|some rhbug link]]

  {{bz|123456}} example bug

  """

-         assert hl.find_bugs(text) == set(('690177', '690194', '1239865'))

+         assert hl.find_bugs(text) == set(("690177", "690194", "1239865"))

  

      # this avoids needing network access and the value changing

-     @mock.patch('fedfind.helpers.get_current_release', return_value=23, autospec=True)

+     @mock.patch("fedfind.helpers.get_current_release", return_value=23, autospec=True)

      # this avoids needing network access

-     @mock.patch('fedfind.release.Production.cid', 'Fedora-24-20160314.1')

-     @mock.patch('fedfind.release.Production.label', 'Alpha-1.2')

-     @mock.patch('fedfind.release.ModularProduction.cid', 'Fedora-Modular-27-20171108.2')

-     @mock.patch('fedfind.release.ModularProduction.label', 'Beta-1.5')

+     @mock.patch("fedfind.release.Production.cid", "Fedora-24-20160314.1")

+     @mock.patch("fedfind.release.Production.label", "Alpha-1.2")

+     @mock.patch("fedfind.release.ModularProduction.cid", "Fedora-Modular-27-20171108.2")

+     @mock.patch("fedfind.release.ModularProduction.label", "Beta-1.5")

      def test_cid_to_event(self, fakecurr):

-         bran = hl.cid_to_event('Fedora-24-20160314.n.2')

-         assert bran == ('Fedora', '24', 'Branched', '20160314.n.2')

-         modbran = hl.cid_to_event('Fedora-Modular-27-20171110.n.1')

-         assert modbran == ('Fedora-Modular', '27', 'Branched', '20171110.n.1')

-         rawh = hl.cid_to_event('Fedora-Rawhide-20160314.n.1')

-         assert rawh == ('Fedora', '24', 'Rawhide', '20160314.n.1')

-         prod = hl.cid_to_event('Fedora-24-20160314.1')

-         assert prod == ('Fedora', '24', 'Alpha', '1.2')

-         modprod = hl.cid_to_event('Fedora-Modular-27-20171108.2')

-         assert modprod == ('Fedora-Modular', '27', 'Beta', '1.5')

+         """Tests for cid_to_event."""

+         bran = hl.cid_to_event("Fedora-24-20160314.n.2")

+         assert bran == ("Fedora", "24", "Branched", "20160314.n.2")

+         modbran = hl.cid_to_event("Fedora-Modular-27-20171110.n.1")

+         assert modbran == ("Fedora-Modular", "27", "Branched", "20171110.n.1")

+         rawh = hl.cid_to_event("Fedora-Rawhide-20160314.n.1")

+         assert rawh == ("Fedora", "24", "Rawhide", "20160314.n.1")

+         prod = hl.cid_to_event("Fedora-24-20160314.1")

+         assert prod == ("Fedora", "24", "Alpha", "1.2")

+         modprod = hl.cid_to_event("Fedora-Modular-27-20171108.2")

+         assert modprod == ("Fedora-Modular", "27", "Beta", "1.5")

          # check that we fail intentionally on non-'Fedora' composes

          with pytest.raises(ValueError):

-             hl.cid_to_event('Fedora-Cloud-28-20180711.0')

+             hl.cid_to_event("Fedora-Cloud-28-20180711.0")

          # check that we fail intentionally on updates-ut composes

          with pytest.raises(ValueError):

-             hl.cid_to_event('Fedora-27-updates-testing-20180711.0')

+             hl.cid_to_event("Fedora-27-updates-testing-20180711.0")

          with pytest.raises(ValueError):

-             hl.cid_to_event('Fedora-27-updates-20180711.0')

+             hl.cid_to_event("Fedora-27-updates-20180711.0")

  

-     @mock.patch('fedfind.helpers.get_current_release', return_value=23, autospec=True)

-     @mock.patch('fedfind.release.Production.cid', 'Fedora-24-20160314.1')

-     @mock.patch('fedfind.release.Production.label', '')

+     @mock.patch("fedfind.helpers.get_current_release", return_value=23, autospec=True)

+     @mock.patch("fedfind.release.Production.cid", "Fedora-24-20160314.1")

+     @mock.patch("fedfind.release.Production.label", "")

      def test_cid_to_event_fail(self, fakecurr):

-         # check that we fail if we cannot determine label for production compose

+         """Test to check cid_to_event fails if we cannot determine

+         label for production compose.

+         """

          with pytest.raises(ValueError):

-             hl.cid_to_event('Fedora-24-20160314.1')

+             hl.cid_to_event("Fedora-24-20160314.1")

+ 

  

  # vim: set textwidth=100 ts=8 et sw=4:

file added
+350
@@ -0,0 +1,350 @@ 

+ # Copyright (C) 2020 Red Hat

+ #

+ # This file is part of python-wikitcms.

+ #

+ # python-wikitcms is free software; you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation, either version 3 of the License, or

+ # (at your option) any later version.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.

+ #

+ # Author: Adam Williamson <awilliam@redhat.com>

+ 

+ # these are all kinda inappropriate for pytest patterns

+ # pylint: disable=no-init, protected-access, no-self-use, unused-argument

+ # pylint: disable=invalid-name

+ 

+ """Tests for listing.py."""

+ 

+ from unittest import mock

+ 

+ import mwclient.image

+ import mwclient.page

+ import pytest

+ 

+ import wikitcms.listing

+ import wikitcms.page

+ import wikitcms.release

+ import wikitcms.wiki

+ 

+ 

+ @pytest.mark.usefixtures("fakemwp")

+ class TestListing:

+     """Tests for the generator magic in listing.py. This stuff is all

+     a bit odd, and because of that, the testing is a bit indirect. A

+     lot of the tests don't go directly through anything defined in

+     listing.py but through Wiki.pages, because ultimately backing that

+     is the most important thing listing.py does: Wiki.pages is an

+     instance of listing's TcmsPageList, which is a subclass of

+     TcmsGeneratorList, where most of the magic lives. Aside from that

+     we test the category stuff via the 'real' category classes that

+     use it.

+     """

+ 

+     # the "fjajah" is just to make sure we're running offline; if I

+     # screw up and add a test that hits the network it"ll cause the

+     # tests to hang/fail instead of succeeding a bit slower

+     site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+ 

+     @mock.patch("mwclient.image.Image.__init__", return_value=None)

+     def test_pages_nomatch(self, fakeimage):

+         """If we don't hit any of the special Wikitcms classes, we

+         should return whatever mwclient would return - a Page, Image,

+         or Category. We also should not hit re.compile for Page or

+         Image, we should catch them with early checks.

+         """

+         with mock.patch("re.compile"):

+             assert isinstance(self.site.pages["Somepage"], mwclient.page.Page)

+             assert isinstance(self.site.pages["Image:Someimage"], mwclient.image.Image)

+         # we do handle some categories, so we can't mock re.compile

+         assert isinstance(self.site.pages["Category:Something"], mwclient.listing.Category)

+ 

+     @mock.patch("re.compile")

+     def test_pages_knownbad_early(self, fakecompile):

+         """Some more cases where we should just fall through, and do

+         it before hitting re.compile. Mocking it out effectively tests

+         that: if we hit it, things will blow up because it's not doing

+         its job because it's a MagicMock.

+         """

+         # we don't handle the translated pages, for test days where

+         # translation happened

+         assert isinstance(self.site.pages["Test_Day:2010-09-29_Radeon/ru"], mwclient.page.Page)

+         # the metadata pages are special stuff for the testdays webapp

+         pg = self.site.pages["Test Day:2019-09-23 F31 Upgrade Test Day TestdayApp Metadata"]

+         assert isinstance(pg, mwclient.page.Page)

+         # these were subpages of the Test Day page proper where we

+         # put output from a specific test

+         pg = self.site.pages["Test Day:2011-02-22 Nouveau/rendercheck/red"]

+         assert isinstance(pg, mwclient.page.Page)

+ 

+     def test_pages_knownbad_late(self):

+         """Some more cases where we should just fall through, but

+         later in the process, because these pages look kinda like we

+         should handle them but we don't.

+         """

+         # a very early validation page style we don't handle yet

+         pg = self.site.pages["Test Results:Fedora 13 Pre-RC Acceptance Test 1"]

+         assert isinstance(pg, mwclient.page.Page)

+ 

+     def test_pages_nightly(self):

+         """Nightly validation pages."""

+         pg = self.site.pages["Test_Results:Fedora_32_Branched_20200322.n.0_Installation"]

+         assert isinstance(pg, wikitcms.page.NightlyPage)

+         assert pg.release == "32"

+         assert pg.testtype == "Installation"

+         assert pg.milestone == "Branched"

+         assert pg.compose == "20200322.n.0"

+         assert pg.modular is False

+         # yeah, the _ vs. space thing is intentional, we ought to

+         # handle both, may as well check it as we go

+         pg = self.site.pages["Test Results:Fedora 32 Rawhide 20191030.n.1 Base"]

+         assert isinstance(pg, wikitcms.page.NightlyPage)

+         assert pg.testtype == "Base"

+         assert pg.milestone == "Rawhide"

+         # modular

+         pg = self.site.pages["Test Results:Fedora Modular 27 Branched 20171123.n.1 Server"]

+         assert isinstance(pg, wikitcms.page.NightlyPage)

+         assert pg.modular is True

+         # summary should fall through to an mwclient Page

+         pg = self.site.pages["Test_Results:Fedora_32_Branched_20200322.n.0_Summary"]

+         assert not isinstance(pg, wikitcms.page.Page)

+         assert isinstance(pg, mwclient.page.Page)

+ 

+     def test_pages_f21_monthly(self):

+         """The old monthly pages we had around F21."""

+         pg = self.site.pages["Test Results:Fedora 21 Nightly 2014 08 Base"]

+         assert isinstance(pg, wikitcms.page.NightlyPage)

+         assert pg.release == "21"

+         assert pg.milestone == "Nightly"

+         assert pg.compose == "2014 08"

+         assert pg.testtype == "Base"

+         pg = self.site.pages["Test Results:Fedora 21 Rawhide 2014 04 Installation"]

+         assert isinstance(pg, wikitcms.page.NightlyPage)

+         assert pg.milestone == "Rawhide"

+         assert pg.testtype == "Installation"

+ 

+     def test_pages_milestone_f12(self):

+         """Milestone validation pages, weird cases from F12."""

+         pg = self.site.pages["Test Results:Fedora 12 Final Pre-RC Install"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.release == "12"

+         assert pg.milestone == "Final"

+         assert pg.compose == "Pre-RC"

+         pg = self.site.pages["Test Results:Fedora 12 Alpha TC Install"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.milestone == "Alpha"

+         assert pg.compose == "TC"

+         pg = self.site.pages["Test Results:Fedora 12 Alpha TCRegression Install"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.milestone == "Alpha"

+         assert pg.compose == "TCRegression"

+         pg = self.site.pages["Test Results:Fedora 12 Beta PreBeta Install"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.milestone == "Beta"

+         assert pg.compose == "PreBeta"

+ 

+     def test_pages_milestone_tcrc(self):

+         """Milestone validation pages, the pre-Pungi 4 "TCx / RCx"

+         style.

+         """

+         # also testing the old "Install" (not "Installation") testtype

+         pg = self.site.pages["Test Results:Fedora 20 Alpha RC1 Install"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.release == "20"

+         assert pg.milestone == "Alpha"

+         assert pg.compose == "RC1"

+         assert pg.testtype == "Install"

+         assert pg.modular is False

+         pg = self.site.pages["Test Results:Fedora 22 Beta TC1 Installation"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.release == "22"

+         assert pg.milestone == "Beta"

+         assert pg.compose == "TC1"

+         assert pg.testtype == "Installation"

+ 

+     def test_pages_milestone_pungi4(self):

+         """Milestone validation pages, current (2020-04) Pungi 4

+         style.

+         """

+         pg = self.site.pages["Test Results:Fedora 24 Alpha 1.1 Cloud"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.release == "24"

+         assert pg.milestone == "Alpha"

+         assert pg.compose == "1.1"

+         assert pg.testtype == "Cloud"

+         assert pg.modular is False

+         pg = self.site.pages["Test Results:Fedora 26 Beta 1.3 Security Lab"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.release == "26"

+         assert pg.milestone == "Beta"

+         assert pg.compose == "1.3"

+         assert pg.testtype == "Security Lab"

+         pg = self.site.pages["Test Results:Fedora 31 RC 1.9 Cloud"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.release == "31"

+         assert pg.milestone == "RC"

+         assert pg.compose == "1.9"

+         assert pg.testtype == "Cloud"

+         # modular

+         pg = self.site.pages["Test Results:Fedora Modular 27 Beta 1.5 Installation"]

+         assert isinstance(pg, wikitcms.page.ComposePage)

+         assert pg.release == "27"

+         assert pg.milestone == "Beta"

+         assert pg.compose == "1.5"

+         assert pg.testtype == "Installation"

+         assert pg.modular is True

+         # again, summary should fall through to an mwclient Page

+         pg = self.site.pages["Test Results:Fedora 31 RC 1.9 Summary"]

+         assert not isinstance(pg, wikitcms.page.Page)

+         assert isinstance(pg, mwclient.page.Page)

+ 

+     def test_pages_testday(self):

+         """Test Day pages."""

+         pg = self.site.pages["Test Day:2020-04-08 Fedora 32 IoT Edition"]

+         assert isinstance(pg, wikitcms.page.TestDayPage)

+         assert pg.date == "2020-04-08"

+         assert pg.subject == "Fedora 32 IoT Edition"

+         # why this exists I don't know, but it does

+         pg = self.site.pages["Test Day:2012-03-14"]

+         assert isinstance(pg, wikitcms.page.TestDayPage)

+         assert pg.date == "2012-03-14"

+         assert pg.subject == ""

+ 

+     # we're just mocking this so we can count the calls

+     @mock.patch("wikitcms.listing.PageCheckWarning.__init__", autospec=True, return_value=None)

+     def test_pages_checkname(self, fakepagecheck):

+         """There's a mechanism where we refuse to return a Wikitcms-y

+         instance if its expected name (which all Wikitcms Page classes

+         are required to define, as 'checkname') does not match the

+         actual page name we got the instance from. This checks that

+         still works.

+         """

+         # this should match the testday_patt regex, but the extra

+         # spaces should mean the checkname won't match

+         pg = self.site.pages["Test Day:2020-04-08  Foobar"]

+         assert isinstance(pg, mwclient.page.Page)

+         # just the above isn't sufficient, as our classes are subs of

+         # mwclient Page

+         assert not isinstance(pg, wikitcms.page.TestDayPage)

+         # to be super sure this worked as intended, check we hit the

+         # exception's __init__

+         assert fakepagecheck.call_count == 1

+ 

+     def test_pages_validation_category(self):

+         """Validation category pages."""

+         # top level

+         pg = self.site.pages["Category:Fedora 32 Test Results"]

+         assert isinstance(pg, wikitcms.listing.ValidationCategory)

+         assert pg.seedtext == "{{Validation results milestone category|release=32}}"

+         assert pg.summary == "Relval bot-created validation result category page for Fedora 32"

+         # top level, modular

+         pg = self.site.pages["Category:Fedora Modular 27 Test Results"]

+         assert isinstance(pg, wikitcms.listing.ValidationCategory)

+         assert pg.seedtext == "{{Validation results milestone category|release=27|modular=true}}"

+         assert pg.summary == (

+             "Relval bot-created validation result category page for Fedora Modular 27"

+         )

+         # milestone level

+         pg = self.site.pages["Category:Fedora 32 Beta Test Results"]

+         assert isinstance(pg, wikitcms.listing.ValidationCategory)

+         assert pg.seedtext == "{{Validation results milestone category|release=32|milestone=Beta}}"

+         assert pg.summary == (

+             "Relval bot-created validation result category page for Fedora 32 Beta"

+         )

+         # milestone level, modular

+         pg = self.site.pages["Category:Fedora Modular 27 Beta Test Results"]

+         assert isinstance(pg, wikitcms.listing.ValidationCategory)

+         assert pg.seedtext == (

+             "{{Validation results milestone category|release=27|milestone=Beta|modular=true}}"

+         )

+         assert pg.summary == (

+             "Relval bot-created validation result category page for Fedora Modular 27 Beta"

+         )

+         # nightly level

+         pg = self.site.pages["Category:Fedora 32 Nightly Test Results"]

+         assert isinstance(pg, wikitcms.listing.ValidationCategory)

+         assert pg.seedtext == "{{Validation results milestone category|release=32|nightly=true}}"

+         assert pg.summary == (

+             "Relval bot-created validation result category page for Fedora 32 nightly results"

+         )

+         # nightly level, modular

+         pg = self.site.pages["Category:Fedora Modular 27 Nightly Test Results"]

+         assert isinstance(pg, wikitcms.listing.ValidationCategory)

+         assert pg.seedtext == (

+             "{{Validation results milestone category|release=27|nightly=true|modular=true}}"

+         )

+         assert pg.summary == (

+             "Relval bot-created validation result category page for Fedora "

+             "Modular 27 nightly results"

+         )

+ 

+     @mock.patch("mwclient.listing.GeneratorList.__next__")

+     def test_pages_testday_category(self, fakenext):

+         """Test Day category pages."""

+         pg = self.site.pages["Category:Fedora 32 Test Days"]

+         assert isinstance(pg, wikitcms.listing.TestDayCategory)

+         assert pg.seedtext == (

+             "This category contains all the Fedora 32 [[QA/Test_Days|Test "

+             "Day]] pages. A calendar of the Test Days can be found ["

+             "https://apps.fedoraproject.org/calendar/list/QA/?subject=Test+Day"

+             " here].\n\n[[Category:Test Days]]"

+         )

+         assert pg.summary == "Created page (via wikitcms)"

+         # test the iterator capability

+         tdp = mwclient.page.Page(self.site, "Test Day:2019-12-09 Kernel 5.4 Test Week")

+         fakenext.return_value = tdp

+         assert isinstance(next(pg), wikitcms.page.TestDayPage)

+         # iterator when page isn't one of ours

+         fakenext.return_value = mwclient.page.Page(self.site, "Somepage")

+         nextpg = next(pg)

+         assert not isinstance(nextpg, wikitcms.page.Page)

+         assert isinstance(nextpg, mwclient.page.Page)

+ 

+     def test_pages_int_name(self):

+         """We handle getting an integer as the page 'name' - which

+         IIRC retrieves a page by some sort of unique ID - by doing a

+         wiki roundtrip to get the real string name.

+         """

+         pg = self.site.pages[80736]

+         assert isinstance(pg, wikitcms.page.TestDayPage)

+ 

+     def test_allresults(self):

+         """This is a test using the allresults generator, because that

+         exercises the namespace handling in TcmsPageList.

+         """

+         # this would usually be set up by a remote trip in

+         # site.__init__ but obviously not with do_init=False, so we

+         # have to just set it up manually

+         self.site.namespaces[116] = "Test Results"

+         pg = self.site.allresults()["Fedora_32_Branched_20200322.n.0_Installation"]

+         assert isinstance(pg, wikitcms.page.NightlyPage)

+ 

+     def test_alltestdays(self):

+         """This is a test using the alltestdays generator. We may as

+         well test it here too as it's just the same as allresults.

+         """

+         # this would usually be set up by a remote trip in

+         # site.__init__ but obviously not with do_init=False, so we

+         # have to just set it up manually

+         self.site.namespaces[114] = "Test Day"

+         pg = self.site.alltestdays()["2019-12-09 Kernel 5.4 Test Week"]

+         assert isinstance(pg, wikitcms.page.TestDayPage)

+ 

+     def test_nopagewarning(self):

+         """Test for the NoPageWarning exception."""

+         exc = wikitcms.listing.NoPageWarning("QA:Somepage")

+         assert str(exc) == "Could not produce a wikitcms page for: QA:Somepage"

+ 

+     def test_pagecheckwarning(self):

+         """Test for the PageCheckWarning exception."""

+         exc = wikitcms.listing.PageCheckWarning("QA:Somepage", "QA:Someotherpage")

+         assert str(exc) == (

+             "Expected page name QA:Somepage does not match source " "page name QA:Someotherpage"

+         )

file added
+795
@@ -0,0 +1,795 @@ 

+ # Copyright (C) 2020 Red Hat

+ #

+ # This file is part of python-wikitcms.

+ #

+ # python-wikitcms is free software; you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation, either version 3 of the License, or

+ # (at your option) any later version.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.

+ #

+ # Author: Adam Williamson <awilliam@redhat.com>

+ 

+ # these are all kinda inappropriate for pytest patterns

+ # pylint: disable=no-init, protected-access, no-self-use, unused-argument

+ # pylint: disable=invalid-name, too-few-public-methods

+ 

+ """Tests for page.py."""

+ 

+ import datetime

+ import difflib

+ from decimal import Decimal

+ import json

+ import os

+ from unittest import mock

+ 

+ import mwclient.errors

+ import pytest

+ 

+ import wikitcms.event

+ import wikitcms.exceptions

+ import wikitcms.page

+ import wikitcms.result

+ import wikitcms.wiki

+ 

+ DATAPATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data")

+ 

+ 

+ def _diff(old, new):

+     """Convenience function for diffing in add_results tests."""

+     diff = list(difflib.ndiff(old.splitlines(), new.splitlines()))

+     # we enumerate so we can check the changes are in the

+     # correct place; obviously this relies on the known

+     # content of the sample data

+     return [item for item in enumerate(diff) if item[1].startswith(("+", "-", "?"))]

+ 

+ 

+ @pytest.mark.usefixtures("fakemwp")

+ class TestPage:

+     """Tests for Page instances."""

+ 

+     # the "fjajah" is just to make sure we're running offline; if I

+     # screw up and add a test that hits the network it"ll cause the

+     # tests to hang/fail instead of succeeding a bit slower

+     site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+ 

+     def test_page_init(self):

+         """Tests for Page.__init__."""

+         pg = wikitcms.page.Page(self.site, "QA:Somepage")

+         # __init__ stuff

+         assert pg.checkname == "QA:Somepage"

+         assert pg._sections is None

+         assert pg.results_separators == []

+ 

+     def test_page_sections(self, fakeapisections):

+         """Tests for Page.sections."""

+         pg = wikitcms.page.Page(self.site, "QA:Somepage")

+         assert len(pg.sections) == 13

+         assert pg.sections[0]["line"] == "Which tests to run"

+         assert pg.sections[12]["line"] == "Upgrade tests"

+         assert fakeapisections.call_count == 1

+         # test caching

+         assert len(pg.sections) == 13

+         assert fakeapisections.call_count == 1

+         # test error case

+         pg._sections = None

+         fakeapisections.side_effect = mwclient.errors.APIError(1, "foo", {})

+         assert pg.sections == []

+         assert fakeapisections.call_count == 2

+         fakeapisections.side_effect = None

+         # test cache clear, using simple return value for convenience

+         fakeapisections.return_value = {"parse": {"sections": ["moo", "meep"]}}

+         # until we save, changed API return value shouldn"t affect us

+         assert pg.sections == []

+         assert fakeapisections.call_count == 2

+         with mock.patch("mwclient.page.Page.save", autospec=True):

+             pg.save("foo", summary="bar")

+         # now we saved, call count should increase and we should get

+         # new values

+         assert pg.sections == ["moo", "meep"]

+         assert fakeapisections.call_count == 3

+ 

+     @mock.patch("mwclient.page.Page.revisions")

+     def test_page_creation_date(self, fakerev):

+         """Tests for Page.creation_date."""

+         pg = wikitcms.page.Page(self.site, "QA:Somepage")

+         # the API gives us a timetuple, here

+         date = datetime.datetime(2020, 3, 4).timetuple()

+         fakerev.return_value = iter([{"timestamp": date}])

+         assert pg.creation_date == "20200304"

+         # this is a cached property, so we need to recreate the Page

+         # instance now

+         pg = wikitcms.page.Page(self.site, "QA:Somepage")

+         fakerev.return_value = iter([])

+         assert pg.creation_date == ""

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_page_write(self, fakesave):

+         """Tests for Page.write."""

+         pg = wikitcms.page.Page(self.site, "QA:Somepage")

+         # need seedtext and summary or else it should raise

+         with pytest.raises(ValueError):

+             pg.write()

+         pg.seedtext = "something"

+         with pytest.raises(ValueError):

+             pg.write()

+         pg.seedtext = None

+         pg.summary = "summary"

+         with pytest.raises(ValueError):

+             pg.write()

+         pg.seedtext = "seedtext"

+         pg.write()

+         assert fakesave.call_count == 1

+         # [0][0] is self

+         assert fakesave.call_args[0][1] == "seedtext"

+         assert fakesave.call_args[0][2] == "summary"

+         assert fakesave.call_args[1]["createonly"] is True

+         pg.write(createonly=False)

+         assert fakesave.call_count == 2

+         assert fakesave.call_args[1]["createonly"] is False

+         pg.write(createonly=None)

+         assert fakesave.call_count == 3

+         assert fakesave.call_args[1]["createonly"] is None

+ 

+     @mock.patch("time.sleep", autospec=True)

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_page_save(self, fakesave, faketime):

+         """Tests for Page.save."""

+         pg = wikitcms.page.Page(self.site, "QA:Somepage")

+         assert pg.save("foo", "summary", oldtext="foo") == {"nochange": ""}

+         assert fakesave.call_count == 0

+         pg.save("foo", "summary", oldtext="bar")

+         assert fakesave.call_count == 1

+         assert "oldtext" not in fakesave.call_args[1]

+         # test the retry-once-on-fail behaviour by having parent save

+         # raise EditError once, then go back to normal

+         fakesave.side_effect = [mwclient.errors.EditError(), None]

+         pg.save("foo", "summary")

+         assert faketime.call_count == 1

+         assert fakesave.call_count == 3

+ 

+ 

+ @pytest.mark.usefixtures("fakemwp")

+ class TestValidationPages:

+     """Tests for ValidationPage instances (all the child classes)."""

+ 

+     site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+ 

+     def test_init(self):

+         """Check all the various attributes of a ValidationPage set up

+         during __init__ are correct for the common page types.

+         """

+         # a typical nightly page (should *not* be modular)

+         nightly = wikitcms.page.NightlyPage(

+             self.site, "32", "Installation", "Rawhide", "20200122.n.0"

+         )

+         # modular nightly page (based on the only nightly modular event

+         # that ever existed...)

+         modnightly = wikitcms.page.NightlyPage(

+             self.site, "27", "Base", "Branched", "20171123.n.1", modular=True

+         )

+         # typical candidate page (should *not* be modular)

+         candidate = wikitcms.page.ComposePage(self.site, "32", "Desktop", "Final", "1.2")

+         # modular candidate page (based on the only modular compose we

+         # ever did...)

+         modcandidate = wikitcms.page.ComposePage(

+             self.site, "27", "Server", "Beta", "1.5", modular=True

+         )

+         assert nightly.release == "32"

+         assert nightly.milestone == "Rawhide"

+         assert nightly.compose == "20200122.n.0"

+         assert nightly.version == "32 Rawhide 20200122.n.0"

+         assert nightly.testtype == "Installation"

+         assert nightly.modular is False

+         assert nightly.checkname == "Test Results:Fedora 32 Rawhide 20200122.n.0 Installation"

+         assert nightly.summary == (

+             "Relval bot-created Installation validation results page for "

+             "Fedora 32 Rawhide 20200122.n.0"

+         )

+         assert nightly.results_separators == (

+             "Test Matri",

+             "Test Areas",

+             "An unsupported test or configuration.  No testing is required.",

+         )

+         assert nightly.sortname == "32 100 20200122.n.0 Installation"

+         assert nightly.sorttuple == (32, 100, Decimal("20200122.0"), "Installation")

+ 

+         assert modnightly.release == "27"

+         assert modnightly.milestone == "Branched"

+         assert modnightly.compose == "20171123.n.1"

+         assert modnightly.version == "27 Branched 20171123.n.1"

+         assert modnightly.testtype == "Base"

+         assert modnightly.modular is True

+         assert modnightly.checkname == "Test Results:Fedora Modular 27 Branched 20171123.n.1 Base"

+         assert modnightly.summary == (

+             "Relval bot-created Base validation results page for "

+             "Fedora Modular 27 Branched 20171123.n.1"

+         )

+         assert modnightly.results_separators == (

+             "Test Matri",

+             "Test Areas",

+             "An unsupported test or configuration.  No testing is required.",

+         )

+         assert modnightly.sortname == "27 150 20171123.n.1 Base"

+         assert modnightly.sorttuple == (27, 150, Decimal("20171123.1"), "Base")

+ 

+         assert candidate.release == "32"

+         assert candidate.milestone == "Final"

+         assert candidate.compose == "1.2"

+         assert candidate.version == "32 Final 1.2"

+         assert candidate.testtype == "Desktop"

+         assert candidate.modular is False

+         assert candidate.checkname == "Test Results:Fedora 32 Final 1.2 Desktop"

+         assert candidate.summary == (

+             "Relval bot-created Desktop validation results page for " "Fedora 32 Final 1.2"

+         )

+         assert candidate.results_separators == (

+             "Test Matri",

+             "Test Areas",

+             "An unsupported test or configuration.  No testing is required.",

+         )

+         assert candidate.sortname == "32 800 1.2 Desktop"

+         assert candidate.sorttuple == (32, 800, Decimal("1.2"), "Desktop")

+ 

+         assert modcandidate.release == "27"

+         assert modcandidate.milestone == "Beta"

+         assert modcandidate.compose == "1.5"

+         assert modcandidate.version == "27 Beta 1.5"

+         assert modcandidate.testtype == "Server"

+         assert modcandidate.modular is True

+         assert modcandidate.checkname == "Test Results:Fedora Modular 27 Beta 1.5 Server"

+         assert modcandidate.summary == (

+             "Relval bot-created Server validation results page for " "Fedora Modular 27 Beta 1.5"

+         )

+         assert modcandidate.results_separators == (

+             "Test Matri",

+             "Test Areas",

+             "An unsupported test or configuration.  No testing is required.",

+         )

+         assert modcandidate.sortname == "27 400 1.5 Server"

+         assert modcandidate.sorttuple == (27, 400, Decimal("1.5"), "Server")

+ 

+     def test_results_sections(self, fakeapisections):

+         """Test the results_sections property."""

+         # we aren"t using fakepages here so this doesn"t really matter

+         # but let's stay consistent. we use fakeapisections not

+         # fakepages as it's easier to adjust the return value

+         page = wikitcms.page.NightlyPage(self.site, "32", "Server", "Branched", "20200322.n.0")

+         assert [sec["line"] for sec in page.results_sections] == [

+             "<b>Test Matrix</b>",

+             "General tests",

+             "FreeIPA server installation and functionality tests",

+             "Domain joining tests: <b>FreeIPA</b>",

+             "Domain joining tests: <b>Active Directory</b>",

+             "Domain client tests",

+             "PostgreSQL server installation and functionality tests",

+             "Upgrade tests",

+         ]

+         # the code has a special case for handling a case where we

+         # don"t positively find a first result section but do find a

+         # section named "Key", but I don"t remember what that's for

+         # and can"t find a real example (the oldest page we handle,

+         # Test_Results:Fedora_12_Alpha_RC1_Install , has a

+         # "Test Matrix" section), so let's make one up by dropping

+         # the "Test Matrix" section from our test data

+         del fakeapisections.return_value["parse"]["sections"][5]

+         assert [sec["line"] for sec in page.results_sections] == [

+             "General tests",

+             "FreeIPA server installation and functionality tests",

+             "Domain joining tests: <b>FreeIPA</b>",

+             "Domain joining tests: <b>Active Directory</b>",

+             "Domain client tests",

+             "PostgreSQL server installation and functionality tests",

+             "Upgrade tests",

+         ]

+ 

+     def test_get_resultrows(self, fakepages):

+         """Test the get_resultrows() method."""

+         # use one of the pages supported by "fakepages"

+         page = wikitcms.page.NightlyPage(self.site, "32", "Server", "Branched", "20200322.n.0")

+         rrows = page.get_resultrows()

+         assert len(rrows) == 26

+         # just sample a couple of rows

+         assert rrows[0].testcase == "QA:Testcase_kickstart_firewall_disabled"

+         assert rrows[13].name == "QA:Testcase_realmd_join_kickstart"

+         assert rrows[25].secid == "8"

+ 

+     def test_find_resultrow(self, fakepages):

+         """Test find_resultrow() method, including various awkward

+         cases.

+         """

+         page = wikitcms.page.NightlyPage(self.site, "32", "Server", "Branched", "20200322.n.0")

+         # simple case: one row found by test case

+         row = page.find_resultrow(testcase="QA:Testcase_kickstart_firewall_disabled")

+         assert row.testcase == "QA:Testcase_kickstart_firewall_disabled"

+         assert row.secid == "2"

+         # should raise TooManyError for test case found more than once

+         # with different names

+         with pytest.raises(wikitcms.exceptions.TooManyError):

+             row = page.find_resultrow(testcase="QA:Testcase_domain_client_authenticate")

+         # ...but if we give a name, should be fine

+         row = page.find_resultrow(

+             testcase="QA:Testcase_domain_client_authenticate", testname="FreeIPA"

+         )

+         # and in fact just searching on the name should also work, if

+         # we give the *exact* name

+         rowagain = page.find_resultrow(testname="(FreeIPA)")

+         assert rowagain.matches(row)

+         # should raise TooManyError for test case found more than once

+         # with different sections

+         with pytest.raises(wikitcms.exceptions.TooManyError):

+             row = page.find_resultrow(testcase="QA:Testcase_realmd_join_kickstart")

+         # ...but if we give section, should be fine

+         row = page.find_resultrow(testcase="QA:Testcase_realmd_join_kickstart", section="FreeIPA")

+         # should raise NotFoundError if we find nothing

+         with pytest.raises(wikitcms.exceptions.NotFoundError):

+             row = page.find_resultrow(testcase="QA:Testcase_non_existent")

+         # test case where 2+ test cases have the search string in them

+         # but one matches it exactly; we should find that row and not

+         # raise TooManyError or anything

+         row = page.find_resultrow(testcase="QA:Testcase_freeipa_replication")

+         assert row.testcase == "QA:Testcase_freeipa_replication"

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_update_current(self, fakesave):

+         """Test for update_current() method."""

+         page = wikitcms.page.NightlyPage(self.site, "32", "Server", "Branched", "20200322.n.0")

+         page.update_current()

+         assert fakesave.call_count == 1

+         args = fakesave.call_args[0]

+         # this is the Page instance itself

+         assert args[0].name == "Test Results:Current Server Test"

+         assert args[1] == "#REDIRECT [[Test Results:Fedora 32 Branched 20200322.n.0 Server]]"

+         assert args[2] == "relval: update to current event"

+ 

+         # now check modular case

+         page = wikitcms.page.ComposePage(self.site, "32", "Server", "Beta", "1.1", modular=True)

+         page.update_current()

+         assert fakesave.call_count == 2

+         args = fakesave.call_args[0]

+         # this is the Page instance itself

+         assert args[0].name == "Test Results:Current Modular Server Test"

+         assert args[1] == "#REDIRECT [[Test Results:Fedora Modular 32 Beta 1.1 Server]]"

+         assert args[2] == "relval: update to current event"

+ 

+ 

+ @pytest.mark.usefixtures("fakemwp", "fakepages")

+ class TestAddResults:

+     """Tests for ValidationPage.add_results() method. Is its own class

+     so these tests can share some stuff.

+     """

+ 

+     @pytest.fixture(autouse=True)

+     def common_setup(self, request, fakemwp, fakepages):

+         """This seems like a kinda baroque way to do things, but the

+         problem is we need to use the fakemwp and fakepages fixtures

+         *in this code*, and I can"t figure a way to do that while just

+         setting these things up as class attributes as we ordinarily

+         would. This autouse fixture which sets attributes on the

+         request class seems to work instead...

+         """

+         us = request.cls

+         us.site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+         us.page = wikitcms.page.NightlyPage(us.site, "32", "Server", "Branched", "20200322.n.0")

+         us.text = us.page.text()

+         us.row = us.page.find_resultrow(testcase="QA:Testcase_kickstart_firewall_disabled")

+         us.row2 = us.page.find_resultrow(testcase="QA:Testcase_FreeIPA_realmd_login")

+         us.passed = wikitcms.result.Result("pass", "wikitcms")

+         us.failed = wikitcms.result.Result("fail", "wikitcms")

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_add_results_simple(self, fakesave, fakepages):

+         """Simple one result test."""

+         # pylint: disable=no-member

+         ret = self.page.add_results({self.row: [("x86_64", self.passed)]})

+         # should be no dupes

+         assert ret == []

+         assert fakesave.call_count == 1

+         # summary

+         expsumm = "Result(s) for test(s): QA:Testcase_kickstart_firewall_disabled filed via relval"

+         assert fakesave.call_args[0][2] == expsumm

+         expdiff = [

+             (13, "- | {{result|pass|coconut|bot=true}}"),

+             (14, "+ | {{result|pass|coconut|bot=true}}{{result|pass|wikitcms}}"),

+         ]

+         assert _diff(self.text, fakesave.call_args[0][1]) == expdiff

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_add_results_multiple(self, fakesave, fakepages):

+         """Multiple result test. Also tests overwriting

+         {{result|none}}.

+         """

+         # pylint: disable=no-member

+         ret = self.page.add_results({self.row: [("x86_64", self.passed), ("aarch64", self.failed)]})

+         assert ret == []

+         assert fakesave.call_count == 1

+         expsumm = "Result(s) for test(s): QA:Testcase_kickstart_firewall_disabled filed via relval"

+         assert fakesave.call_args[0][2] == expsumm

+         expdiff = [

+             (13, "- | {{result|pass|coconut|bot=true}}"),

+             (14, "- | {{result|none}}"),

+             (15, "+ | {{result|pass|coconut|bot=true}}{{result|pass|wikitcms}}"),

+             (16, "+ | {{result|fail|wikitcms}}"),

+         ]

+         assert _diff(self.text, fakesave.call_args[0][1]) == expdiff

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_add_results_multiple_row(self, fakesave, fakepages):

+         """Multiple row, different sections test."""

+         # pylint: disable=no-member

+         ret = self.page.add_results(

+             {self.row: [("x86_64", self.passed)], self.row2: [("Result", self.failed)]}

+         )

+         assert ret == []

+         assert fakesave.call_count == 1

+         # the ordering of test cases in the summary is reversed as we

+         # reverse the dict to ensure editing accuracy...

+         expsumm = (

+             "Result(s) for test(s): QA:Testcase_FreeIPA_realmd_login, "

+             "QA:Testcase_kickstart_firewall_disabled filed via relval"

+         )

+         assert fakesave.call_args[0][2] == expsumm

+         expdiff = [

+             (13, "- | {{result|pass|coconut|bot=true}}"),

+             (14, "+ | {{result|pass|coconut|bot=true}}{{result|pass|wikitcms}}"),

+             (139, "- | {{result|pass|coconut|bot=true}}"),

+             (140, "+ | {{result|pass|coconut|bot=true}}{{result|fail|wikitcms}}"),

+         ]

+         assert _diff(self.text, fakesave.call_args[0][1]) == expdiff

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_add_results_single_env(self, fakesave, fakepages):

+         """If there's only one env, we use it even if the passed env

+         doesn"t match at all (this is an arguable choice but it's what

+         we"ve done for years...)

+         """

+         # pylint: disable=no-member

+         self.page.add_results({self.row2: [("bananas", self.passed)]})

+         assert fakesave.call_count == 1

+         expsumm = "Result(s) for test(s): QA:Testcase_FreeIPA_realmd_login filed via relval"

+         assert fakesave.call_args[0][2] == expsumm

+         expdiff = [

+             (138, "- | {{result|pass|coconut|bot=true}}"),

+             (139, "+ | {{result|pass|coconut|bot=true}}{{result|pass|wikitcms}}"),

+         ]

+         assert _diff(self.text, fakesave.call_args[0][1]) == expdiff

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_add_results_incomplete_env(self, fakesave, fakepages):

+         """An incomplete env string match is expected to work if it is

+         unique.

+         """

+         # pylint: disable=no-member

+         ret = self.page.add_results({self.row: [("x86", self.passed)]})

+         assert ret == []

+         assert fakesave.call_count == 1

+         # summary

+         expsumm = "Result(s) for test(s): QA:Testcase_kickstart_firewall_disabled filed via relval"

+         assert fakesave.call_args[0][2] == expsumm

+         expdiff = [

+             (13, "- | {{result|pass|coconut|bot=true}}"),

+             (14, "+ | {{result|pass|coconut|bot=true}}{{result|pass|wikitcms}}"),

+         ]

+         assert _diff(self.text, fakesave.call_args[0][1]) == expdiff

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_add_results_incomplete_dupes(self, fakesave, fakepages):

+         """Duplicate submissions (when we have a result in same cell

+         from same reporter) should be returned, as a list of tuples

+         (row, env, Result) where the Result is the *submitted* result

+         not the existing one.

+         """

+         # pylint: disable=no-member

+         cocores = wikitcms.result.Result("fail", "coconut", bot=True)

+         ret = self.page.add_results(

+             {

+                 self.row: [("x86_64", cocores), ("x86_64", self.passed)],

+                 self.row2: [("Result", cocores)],

+             }

+         )

+         # reversed, as results dict is reversed by add_results...

+         assert ret == [(self.row2, "Result", cocores), (self.row, "x86_64", cocores)]

+         # the row we didn't touch as we only had dupe results should be

+         # left out of the summary text

+         expsumm = "Result(s) for test(s): QA:Testcase_kickstart_firewall_disabled filed via relval"

+         assert fakesave.call_args[0][2] == expsumm

+         # the dupe results should *not* be posted

+         expdiff = [

+             (13, "- | {{result|pass|coconut|bot=true}}"),

+             (14, "+ | {{result|pass|coconut|bot=true}}{{result|pass|wikitcms}}"),

+         ]

+         assert _diff(self.text, fakesave.call_args[0][1]) == expdiff

+ 

+         # now check with allowdupe=True

+         ret = self.page.add_results(

+             {

+                 self.row: [("x86_64", cocores), ("x86_64", self.passed)],

+                 self.row2: [("Result", cocores)],

+             },

+             allowdupe=True,

+         )

+         assert ret == []

+         expsumm = (

+             "Result(s) for test(s): QA:Testcase_FreeIPA_realmd_login, "

+             "QA:Testcase_kickstart_firewall_disabled filed via relval"

+         )

+         assert fakesave.call_args[0][2] == expsumm

+         expdiff = [

+             (13, "- | {{result|pass|coconut|bot=true}}"),

+             (

+                 14,

+                 "+ | {{result|pass|coconut|bot=true}}"

+                 "{{result|fail|coconut|bot=true}}{{result|pass|wikitcms}}",

+             ),

+             (139, "- | {{result|pass|coconut|bot=true}}"),

+             (140, "+ | {{result|pass|coconut|bot=true}}{{result|fail|coconut|bot=true}}"),

+         ]

+         assert _diff(self.text, fakesave.call_args[0][1]) == expdiff

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_add_results_ellipsization(self, fakesave, fakepages):

+         """Test the summary ellipsization when more than three test

+         cases have results.

+         """

+         # pylint: disable=no-member

+         row3 = self.page.find_resultrow(testcase="QA:Testcase_postgresql_server_installation")

+         row4 = self.page.find_resultrow(

+             testcase="QA:Testcase_upgrade_dnf_current_server_domain_controller"

+         )

+         ret = self.page.add_results(

+             {

+                 self.row: [("x86_64", self.passed)],

+                 self.row2: [("Result", self.passed)],

+                 row3: [("x86_64", self.passed)],

+                 row4: [("x86_64", self.passed)],

+             }

+         )

+         assert ret == []

+         # Reverse order again...

+         expsumm = (

+             "Result(s) for test(s): QA:Testcase_upgrade_dnf_current_server_domain_"

+             "controller, QA:Testcase_postgresql_server_installation, "

+             "QA:Testcase_FreeIPA_realmd_login... filed via relval"

+         )

+         assert fakesave.call_args[0][2] == expsumm

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_add_result(self, fakesave, fakepages):

+         """As add_result is just a thin wrapper for add_results (to

+         retain backward compatibility from before we added multiple

+         simultaneous result submission), let's test it here.

+         """

+         # pylint: disable=no-member

+         # this does the same as test_add_results_simple

+         ret = self.page.add_result(self.passed, self.row, "x86_64")

+         # should be no dupes

+         assert ret == []

+         assert fakesave.call_count == 1

+         # summary

+         expsumm = "Result(s) for test(s): QA:Testcase_kickstart_firewall_disabled filed via relval"

+         assert fakesave.call_args[0][2] == expsumm

+         expdiff = [

+             (13, "- | {{result|pass|coconut|bot=true}}"),

+             (14, "+ | {{result|pass|coconut|bot=true}}{{result|pass|wikitcms}}"),

+         ]

+         assert _diff(self.text, fakesave.call_args[0][1]) == expdiff

+ 

+ 

+ @pytest.mark.usefixtures("fakemwp")

+ class TestSummaryPage:

+     """Test for the SummaryPage class."""

+ 

+     @mock.patch("fedfind.release.Compose.exists", True)

+     @mock.patch("fedfind.release.Compose.all_images", ["foo"])

+     @mock.patch("wikitcms.wiki.Wiki.testtypes", ["Installation", "Base"])

+     @mock.patch("wikitcms.wiki.Wiki.modular_testtypes", ["Installation", "Base"])

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_summary_page(self, fakesave):

+         """General tests for SummaryPage. It's a simple class."""

+         site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+         event = wikitcms.event.ComposeEvent(site, "32", "Beta", "1.2", cid="Fedora-32-20200312.0")

+         summ = event.summary_page

+         assert isinstance(summ, wikitcms.page.SummaryPage)

+         assert summ.checkname == "Test Results:Fedora 32 Beta 1.2 Summary"

+         # checking the summary summary, like whoah, dude

+         assert summ.summary == (

+             "Relval bot-created validation results summary for " "Fedora 32 Beta 1.2"

+         )

+         assert summ.seedtext == (

+             "Fedora 32 Beta 1.2 [[QA:Release validation test plan|release "

+             "validation]] summary. This page shows the results from all the "

+             "individual result pages for this compose together. You can file "

+             "results directly from this page and they will be saved into the "

+             "correct individual result page. To see test instructions, visit "

+             "any of the individual pages (the section titles are links). You "

+             "can find download links below.\n\n__TOC__\n\n== Downloads ==\n{{"

+             "Fedora 32 Beta 1.2 Download}}\n\n== [[Test Results:Fedora 32 Beta 1.2 Installation|"

+             "Installation]] ==\n{{Test Results:Fedora 32 Beta 1.2 Installation}}\n\n== "

+             "[[Test Results:Fedora 32 Beta 1.2 Base|Base]] ==\n{{Test Results:Fedora 32 Beta 1.2 "

+             "Base}}"

+         )

+         summ.update_current()

+         assert fakesave.call_count == 1

+         args = fakesave.call_args[0]

+         # this is the Page instance itself

+         assert args[0].name == "Test Results:Current Summary"

+         assert args[1] == "#REDIRECT [[Test Results:Fedora 32 Beta 1.2 Summary]]"

+         assert args[2] == "relval: update to current event"

+         fakesave.reset_mock()

+ 

+         # modular checks

+         modevent = wikitcms.event.ComposeEvent(

+             site, "27", "Beta", "1.5", cid="Fedora-Modular-27-20171108.2", modular=True

+         )

+         summ = modevent.summary_page

+         assert isinstance(summ, wikitcms.page.SummaryPage)

+         assert summ.checkname == "Test Results:Fedora Modular 27 Beta 1.5 Summary"

+         assert summ.summary == (

+             "Relval bot-created validation results summary for " "Fedora Modular 27 Beta 1.5"

+         )

+         # let's not test the whole goddamned thing again

+         assert summ.seedtext.startswith("Fedora Modular 27 Beta 1.5 [[")

+         summ.update_current()

+         assert fakesave.call_count == 1

+         args = fakesave.call_args[0]

+         # this is the Page instance itself

+         assert args[0].name == "Test Results:Current Modular Summary"

+         assert args[1] == "#REDIRECT [[Test Results:Fedora Modular 27 Beta 1.5 Summary]]"

+         assert args[2] == "relval: update to current event"

+         fakesave.reset_mock()

+ 

+ 

+ @pytest.mark.usefixtures("fakemwp", "fakepages", "fakeimages")

+ class TestDownloadPage:

+     """Tests for the DownloadPage class."""

+ 

+     # we have to patch these to avoid network round trips in fedfind

+     @mock.patch("fedfind.release.Compose.cid", "Fedora-32-20200312.0")

+     @mock.patch("fedfind.release.Compose.exists", True)

+     def test_download_page(self):

+         """Test for DownloadPage. It's pretty hard to break this down

+         into unit tests, given how it works, so we just test it all at

+         once: we run it on the real data from a real compose, and

+         check the output matches what we expect.

+         """

+         site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+         event = wikitcms.event.ComposeEvent(site, "32", "Beta", "1.2", cid="Fedora-32-20200312.0")

+         page = event.download_page

+         assert page.checkname == "Template:Fedora 32 Beta 1.2 Download"

+         assert page.summary == "Relval bot-created download page for Fedora 32 Beta 1.2"

+         assert page.event is event

+         # seedtext is very long so we stash the expected value in a

+         # file and read it here

+         exptextpath = os.path.join(DATAPATH, "Template:Fedora 32 Beta 1.2 Download.seedtext")

+         with open(exptextpath, "r") as exptextfh:

+             exptext = exptextfh.read()

+         assert page.seedtext == exptext

+ 

+         # modular checks

+         modevent = wikitcms.event.ComposeEvent(

+             site, "27", "Beta", "1.5", cid="Fedora-Modular-27-20171108.2", modular=True

+         )

+         page = modevent.download_page

+         assert page.checkname == "Template:Fedora Modular 27 Beta 1.5 Download"

+         assert page.summary == "Relval bot-created download page for Fedora Modular 27 Beta 1.5"

+         # that's all we need to check for modular

+ 

+ 

+ @pytest.mark.usefixtures("fakemwp", "fakepages", "fakeimages")

+ class TestAMIPage:

+     """Tests for the AMIPage class."""

+ 

+     # we have to patch these to avoid network round trips in fedfind

+     @mock.patch("fedfind.release.Compose.cid", "Fedora-32-20200312.0")

+     @mock.patch("fedfind.release.Compose.exists", True)

+     def test_ami_page(self):

+         """Test for AMIPage. As with DownloadPage, it's hard to split

+         this into unit tests, so we feed in real world data (more or

+         less) and check the output is as expected.

+         """

+         site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+         event = wikitcms.event.ComposeEvent(site, "32", "Beta", "1.2", cid="Fedora-32-20200312.0")

+         page = event.ami_page

+         assert page.checkname == "Template:Fedora 32 Beta 1.2 AMI"

+         assert page.summary == "Relval bot-created AMI page for Fedora 32 Beta 1.2"

+         assert page.event is event

+ 

+         def fakedownloadjson(url):

+             """Mock for fedfind.helpers.download_json to fake up a

+             datagrepper query response used to find the AMIs. The data

+             that backs this is close to but not quite 'real' data -

+             it's the real message dicts only of the messages that were

+             actually for this compose, plus *one* message that wasn't

+             (to test that filtering those out works), with the headers

+             and most of the additional data in the query response

+             dropped. The *real* real data for the relevant two days

+             is like 25 pages long, so we had to filter it. We hack up

+             splitting it across two 'pages' to test the pagination

+             handling.

+             """

+             page = 1

+             if "page=2" in url:

+                 # dumb but enough

+                 page = 2

+             path = os.path.join(DATAPATH, "Fedora-32-20200312.0.ami.dgdata.{0}.json".format(page))

+             with open(path, "r") as amifh:

+                 amidata = json.load(amifh)

+             return amidata

+ 

+         # again, read expected seedtext from a file

+         exptextpath = os.path.join(DATAPATH, "Template:Fedora 32 Beta 1.2 AMI.seedtext")

+         with open(exptextpath, "r") as exptextfh:

+             exptext = exptextfh.read()

+         with mock.patch("fedfind.helpers.download_json", fakedownloadjson):

+             assert page.seedtext == exptext

+ 

+ 

+ @pytest.mark.usefixtures("fakemwp")

+ class TestTestDayPage:

+     """Tests for the TestDayPage class."""

+ 

+     def test_init(self):

+         """Tests for basic __init__ stuff."""

+         site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+         page = wikitcms.page.TestDayPage(site, "2020-03-20", "Cloud Testday")

+         assert page.checkname == "Test Day:2020-03-20 Cloud Testday"

+         assert page.date == "2020-03-20"

+         assert page.subject == "Cloud Testday"

+         assert page.results_separators == ("Test Results", "Results")

+ 

+     def test_bugs(self, fakepages):

+         """Tests for the bugs() property."""

+         # this page uses the bugs-in-results-template style

+         site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+         page = wikitcms.page.TestDayPage(site, "2013-11-05", "Printing")

+         assert page.bugs == ["1026909", "1026914", "1026928", "1026940", "1026949", "1027425"]

+         # this one uses the app-generated bugs-as-references style

+         # and has some direct Bugzilla URLs also, also has lots of

+         # duplication between entries

+         page = wikitcms.page.TestDayPage(site, "2016-10-24", "Cloud")

+         assert page.bugs == ["1384150", "1387934", "1388000"]

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_fix_app_results(self, fakesave, fakepages):

+         """Test for fix_app_results."""

+         site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+         page = wikitcms.page.TestDayPage(site, "2016-10-24", "Cloud")

+         page.fix_app_results()

+         assert fakesave.call_count == 1

+         args = fakesave.call_args[0]

+         kwargs = fakesave.call_args[1]

+         assert kwargs["summary"] == (

+             "Fix testday app-generated results to use " "{{result}} template for bug references"

+         )

+         # FIXME: we actually miss several conversions here as we do not

+         # handle {{bz}} and comment text mixed up in a single ref tag

+         # FIXME 2: I even had to cheat and edit the text of the page

+         # slightly as there's a stray space between the end of the

+         # template and the closing </ref>

+         assert args[1] == page.text().replace(

+             "{{result|fail}}<ref>{{bz|1388000}}</ref>", "{{result|fail||1388000}}"

+         )

+ 

+     @mock.patch("mwclient.page.Page.save", autospec=True)

+     def test_long_refs(self, fakesave, fakepages):

+         """Test for long_refs."""

+         site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+         page = wikitcms.page.TestDayPage(site, "2016-10-24", "Cloud")

+         page.long_refs()

+         # read expected  modified text from a file, it's too long to

+         # inline

+         exptextpath = os.path.join(DATAPATH, "Test_Day:2016-10-24_Cloud.longrefs.txt")

+         with open(exptextpath, "r") as exptextfh:

+             exptext = exptextfh.read()

+         assert fakesave.call_args[0][1] == exptext

+         # summary

+         assert fakesave.call_args[1]["summary"] == (

+             "Move long comments to a separate " "section at end of page"

+         )

@@ -0,0 +1,77 @@ 

+ # Copyright (C) 2020 Red Hat

+ #

+ # This file is part of python-wikitcms.

+ #

+ # python-wikitcms is free software; you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation, either version 3 of the License, or

+ # (at your option) any later version.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.

+ #

+ # Author: Adam Williamson <awilliam@redhat.com>

+ 

+ # these are all kinda inappropriate for pytest patterns

+ # pylint: disable=no-init, protected-access, no-self-use, unused-argument

+ # pylint: disable=invalid-name

+ 

+ """Tests for release.py."""

+ 

+ from unittest import mock

+ 

+ import wikitcms.release

+ import wikitcms.wiki

+ 

+ 

+ class TestRelease:

+     """Tests for Release instances."""

+ 

+     # the "fjajah" is just to make sure we're running offline; if I

+     # screw up and add a test that hits the network it"ll cause the

+     # tests to hang/fail instead of succeeding a bit slower

+     site = wikitcms.wiki.Wiki("fjajah", do_init=False, force_login=False)

+ 

+     def test_release_init(self):

+         """Tests for Release.__init__."""

+         rl = wikitcms.release.Release("32", self.site)

+         # __init__ stuff

+         assert rl.release == "32"

+         assert rl.modular is False

+         assert rl.category_name == "Category:Fedora 32 Test Results"

+         # modular

+         rl = wikitcms.release.Release("27", self.site, modular=True)

+         assert rl.release == "27"

+         assert rl.modular is True

+         assert rl.category_name == "Category:Fedora Modular 27 Test Results"

+ 

+     def test_testday_pages(self, fakemwp):

+         """Test for Release.testday_pages. There is actually quite a

+         lot going on in this small method, but most of the hard work

+         is tested in mwclient upstream and in the listing tests; here

+         we can just assume the generator magic works right.

+         """

+         rl = wikitcms.release.Release("32", self.site)

+         tdp = self.site.pages["Test Day:2016-10-24 Cloud"]

+         tdp2 = self.site.pages["Test Day:2013-11-05 Printing"]

+         other = self.site.pages["Somepage"]

+         with mock.patch.object(

+             self.site, "pages", {"Category:Fedora 32 Test Days": [tdp, tdp2, other]}

+         ):

+             assert rl.testday_pages == [tdp, tdp2]

+ 

+     def test_milestone_pages(self, fakemwp):

+         """Test for Release.milestone_pages. Similar to testday_pages,

+         we just trust the generator magic here and test it elsewhere.

+         """

+         rl = wikitcms.release.Release("32", self.site)

+         vp1 = self.site.pages["Test Results:Fedora 32 Branched 20200212.n.1 Desktop"]

+         vp2 = self.site.pages["Test Results:Fedora 32 Beta 1.2 Base"]

+         other = self.site.pages["Somepage"]

+         with mock.patch("wikitcms.wiki.Wiki.walk_category", return_value=[vp1, vp2, other]):

+             assert list(rl.milestone_pages()) == [vp1, vp2]

file modified
+179 -67
@@ -2,9 +2,9 @@ 

  

  # Copyright (C) 2016 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -20,16 +20,27 @@ 

  # Author: Adam Williamson <awilliam@redhat.com>

  

  # these are all kinda inappropriate for pytest patterns

- # pylint: disable=old-style-class, no-init, protected-access, no-self-use, unused-argument

+ # pylint: disable=no-init, protected-access, no-self-use, unused-argument, too-many-arguments

  

- from __future__ import unicode_literals

+ """Tests for result.py."""

  

  import wikitcms.result as rs

  

+ 

  class TestResults:

      """Tests for find_results()."""

  

-     def _check_result(self, res, status=None, user=None, bugs=None, comment="", bot=False, transferred=False, comment_bugs=None):

+     def _check_result(

+         self,

+         res,

+         status=None,

+         user=None,

+         bugs=None,

+         comment="",

+         bot=False,

+         transferred=False,

+         comment_bugs=None,

+     ):

          if bugs is None:

              bugs = []

          if comment_bugs is None:
@@ -57,17 +68,22 @@ 

  {{result |fail |  rené|372312 |9345671}}

  """

          results = rs.find_results(restext)

-         assert len(results) is 9

+         assert len(results) == 9

          self._check_result(results[0])

          self._check_result(results[1], status="pass")

          self._check_result(results[2], status="fail", user="adamwill")

-         self._check_result(results[3], status="warn", user="adamwill", bugs=['345234'])

-         self._check_result(results[4], status="pass", user="kparal", bugs=['6789032'], comment="<ref>Some comment.</ref>")

-         self._check_result(results[5], status="fail", bugs=['3456780'])

+         self._check_result(results[3], status="warn", user="adamwill", bugs=["345234"])

+         self._check_result(

+             results[4],

+             status="pass",

+             user="kparal",

+             bugs=["6789032"],

+             comment="<ref>Some comment.</ref>",

+         )

+         self._check_result(results[5], status="fail", bugs=["3456780"])

          self._check_result(results[6], status="pass", user="previous RC3 run", transferred=True)

          self._check_result(results[7], status="pass", user="coconut", bot=True)

-         self._check_result(results[8], status="fail", user="rené", bugs=['372312', '9345671'])

- 

+         self._check_result(results[8], status="fail", user="rené", bugs=["372312", "9345671"])

  

      def test_find_results_old_template(self):

          """Various common forms of the old testresult template."""
@@ -79,13 +95,22 @@ 

  {{testresult/warn|jlaska}}  <ref>NEEDS REVIEW - https://fedorahosted.org/pipermail/autoqa-results/2009-November/001966.html</ref>

  """

          results = rs.find_results(restext)

-         assert len(results) is 5

+         assert len(results) == 5

          self._check_result(results[0])

          self._check_result(results[1], status="pass", user="jlaska")

-         self._check_result(results[2], status="fail", user="jkeating", bugs=['517926'])

-         self._check_result(results[3], status="fail", user="jlaska", bugs=['533420'], comment="<ref>- broken dep: gnome-python2-gdl-2.25.3-12.fc12.i686 requires libgdl-1.so.2</ref>")

-         self._check_result(results[4], status="warn", user="jlaska", comment="<ref>NEEDS REVIEW - https://fedorahosted.org/pipermail/autoqa-results/2009-November/001966.html</ref>")

- 

+         self._check_result(results[2], status="fail", user="jkeating", bugs=["517926"])

+         res3comm = (

+             "<ref>- broken dep: gnome-python2-gdl-2.25.3-12.fc12.i686 requires "

+             "libgdl-1.so.2</ref>"

+         )

+         self._check_result(

+             results[3], status="fail", user="jlaska", bugs=["533420"], comment=res3comm

+         )

+         res4comm = (

+             "<ref>NEEDS REVIEW - https://fedorahosted.org/pipermail/autoqa-results/"

+             "2009-November/001966.html</ref>"

+         )

+         self._check_result(results[4], status="warn", user="jlaska", comment=res4comm)

  

      def test_find_results_exclusions(self):

          """Test excluding example, 'transferred' and 'bot' results."""
@@ -96,11 +121,25 @@ 

  {{result|pass|previous RC2 run}}

  {{result|pass|coconut|bot=true}}

  """

-         assert len(rs.find_results(restext)) is 3

-         assert len(rs.find_results(restext, transferred=False)) is 2

-         assert len(rs.find_results(restext, bot=False)) is 2

-         assert len(rs.find_results(restext, transferred=False, bot=False)) is 1

+         assert len(rs.find_results(restext)) == 3

+         assert len(rs.find_results(restext, transferred=False)) == 2

+         assert len(rs.find_results(restext, bot=False)) == 2

+         assert len(rs.find_results(restext, transferred=False, bot=False)) == 1

+ 

+     def test_find_results_statuses(self):

+         """Test filtering results by status."""

+         restext = """

+ {{result|none}}

+ {{result|pass}}

+ {{result|fail|adamwill}}

+ {{result|pass|coconut|bot=true}}

+ {{result|warn|kparal}}

+ {{result|warn|jlaska}}

+ """

  

+         assert len(rs.find_results(restext)) == 6

+         assert len(rs.find_results(restext, statuses=["pass", "fail"])) == 3

+         assert len(rs.find_results(restext, statuses=["pass", "warn"], bot=False)) == 3

  

      def test_find_results_page_extracts(self):

          """Various tests culled from real test pages, with messy text."""
@@ -159,7 +198,7 @@ 

  | Tier3

  """

          results = rs.find_results(f12text)

-         assert len(results) is 9

+         assert len(results) == 9

          for res in results[3:6]:

              self._check_result(res, status="pass", user="kparal")

          for res in results[6:8]:
@@ -184,11 +223,17 @@ 

  |-

  """

          results = rs.find_results(f16text)

-         assert len(results) is 6

-         self._check_result(results[0], status="pass", user="rmarko", comment="<ref>{{bz|744463}} Final blocker?\n</ref>", comment_bugs=set(['744463']))

-         for res in (results[1],results[2],results[4]):

+         assert len(results) == 6

+         self._check_result(

+             results[0],

+             status="pass",

+             user="rmarko",

+             comment="<ref>{{bz|744463}} Final blocker?\n</ref>",

+             comment_bugs=set(["744463"]),

+         )

+         for res in (results[1], results[2], results[4]):

              self._check_result(res, status="pass", user="rmarko")

-         for res in (results[3],results[5]):

+         for res in (results[3], results[5]):

              self._check_result(res, status="pass", user="robatino")

  

          # from Test_Results:Fedora_23_Final_RC3_Installation
@@ -216,8 +261,8 @@ 

  """

          results = rs.find_results(f23text)

          resexc = rs.find_results(f23text, bot=False)

-         assert len(results) is 11

-         assert len(resexc) is 5

+         assert len(results) == 11

+         assert len(resexc) == 5

          for res in (results[0], results[2], results[5], results[7], results[9], results[10]):

              self._check_result(res, status="pass", user="coconut", bot=True)

          for res in (results[3], results[8]):
@@ -227,8 +272,11 @@ 

          self._check_result(results[4], status="pass", user="roshi")

  

      def test_find_results_by_row(self):

+         """Tests for find_results_by_row."""

          # extracts from Test_Day:2011-02-23_Radeon

          # example results (should be filtered), various username styles

+         # one result with a username included added to make coverage

+         # happy

          restext = """

  === Main tests ===

  
@@ -310,21 +358,24 @@ 

  | {{result|pass}}

  | {{result|pass}}

  | {{result|fail}} <ref>System didn't suspend successfully, it just simply switched off</ref>

+ | {{result|warn|foobar}}

  | N/A

  | <references/>

  |-

  """

          results = rs.find_results_by_row(restext)

-         assert len(results) is 35

+         assert len(results) == 36

          for res in results[0:12]:

              assert res.user == "[[denis kurochkin]]"

          for res in results[12:24]:

              assert res.user == "madrouter"

          for res in results[24:34]:

              assert res.user == "mira"

+         assert results[35].user == "foobar"

  

- 

-     def _check_resultrow(self, row, testcase, milestone, columns, results, section='', secid=0, name=None):

+     def _check_resultrow(

+         self, row, testcase, milestone, columns, results, section="", secid=0, name=None

+     ):

          assert row.testcase == testcase

          assert row.milestone == milestone

          assert row.columns == columns
@@ -337,7 +388,6 @@ 

              name = testcase

          assert row.name == name

  

- 

      def test_find_resultrows_simple(self):

          """find_resultrows: simple table."""

          text = """
@@ -368,12 +418,29 @@ 

  |}

  """

          rows = rs.find_resultrows(text)

-         assert len(rows) is 3

+         assert len(rows) == 3

          cols = ["Milestone", "Test Case", "x86 BIOS", "x86 UEFI", "ARM"]

-         self._check_resultrow(rows[0], "QA:Testcase_Install_to_Previous_KVM", "Beta", cols, {'x86 BIOS': 2, 'x86 UEFI': 1, 'ARM': 1})

-         self._check_resultrow(rows[1], "QA:Testcase_Install_to_Current_KVM", "Beta", cols, {'x86 BIOS': 1, 'x86 UEFI': 1, 'ARM': 1})

-         self._check_resultrow(rows[2], "QA:Testcase_Boot_Methods_Xen_Para_Virt", "Final", cols, {'x86 BIOS': 1, 'x86 UEFI': 1, 'ARM': 1})

- 

+         self._check_resultrow(

+             rows[0],

+             "QA:Testcase_Install_to_Previous_KVM",

+             "Beta",

+             cols,

+             {"x86 BIOS": 2, "x86 UEFI": 1, "ARM": 1},

+         )

+         self._check_resultrow(

+             rows[1],

+             "QA:Testcase_Install_to_Current_KVM",

+             "Beta",

+             cols,

+             {"x86 BIOS": 1, "x86 UEFI": 1, "ARM": 1},

+         )

+         self._check_resultrow(

+             rows[2],

+             "QA:Testcase_Boot_Methods_Xen_Para_Virt",

+             "Final",

+             cols,

+             {"x86 BIOS": 1, "x86 UEFI": 1, "ARM": 1},

+         )

  

      def test_find_resultrows_names(self):

          """find_resultrows: test instances by name."""
@@ -402,15 +469,32 @@ 

  |-

  """

          rows = rs.find_resultrows(text)

-         assert len(rows) is 3

+         assert len(rows) == 3

          cols = ["Milestone", "Image", "i386", "x86_64", "UEFI"]

-         self._check_resultrow(rows[0], "QA:Testcase_Boot_default_install", "Alpha", cols,

-                               {'i386': 1, 'x86_64': 2, 'UEFI': 3}, name="Workstation live")

-         self._check_resultrow(rows[1], "QA:Testcase_Boot_default_install", "Alpha", cols,

-                               {'i386': 1, 'x86_64': 1, 'UEFI': 1}, name="Workstation netinst")

-         self._check_resultrow(rows[2], "QA:Testcase_Boot_default_install", "Alpha", cols,

-                               {'i386': 1, 'x86_64': 2, 'UEFI': 2}, name="Server netinst")

- 

+         self._check_resultrow(

+             rows[0],

+             "QA:Testcase_Boot_default_install",

+             "Alpha",

+             cols,

+             {"i386": 1, "x86_64": 2, "UEFI": 3},

+             name="Workstation live",

+         )

+         self._check_resultrow(

+             rows[1],

+             "QA:Testcase_Boot_default_install",

+             "Alpha",

+             cols,

+             {"i386": 1, "x86_64": 1, "UEFI": 1},

+             name="Workstation netinst",

+         )

+         self._check_resultrow(

+             rows[2],

+             "QA:Testcase_Boot_default_install",

+             "Alpha",

+             cols,

+             {"i386": 1, "x86_64": 2, "UEFI": 2},

+             name="Server netinst",

+         )

  

      def test_find_resultrows_multiline(self):

          """find_resultrows: results split across lines."""
@@ -428,11 +512,14 @@ 

  |}

  """

          rows = rs.find_resultrows(text)

-         assert len(rows) is 1

-         self._check_resultrow(rows[0], "QA:Testcase_base_startup", "Alpha",

-                               ["Milestone", "Test Case", "Local", "EC2", "Openstack"],

-                               {'Local': 2, 'EC2': 1, 'Openstack': 2})

- 

+         assert len(rows) == 1

+         self._check_resultrow(

+             rows[0],

+             "QA:Testcase_base_startup",

+             "Alpha",

+             ["Milestone", "Test Case", "Local", "EC2", "Openstack"],

+             {"Local": 2, "EC2": 1, "Openstack": 2},

+         )

  

      def test_find_resultrows_text_between_tables(self):

          """find_resultrows: check text containing test case names between
@@ -473,8 +560,7 @@ 

  |}

  """

          rows = rs.find_resultrows(text)

-         assert len(rows) is 2

- 

+         assert len(rows) == 2

  

      def test_find_resultrows_column_titles_no_separator(self):

          """Test find_resultrows works OK when there is no separator
@@ -494,8 +580,15 @@ 

  |-

  """

          rows = rs.find_resultrows(text)

-         assert rows[0].columns == ['Priority', 'Test Area', 'Test Case', 'i386', 'ppc', 'x86_64', 'References']

- 

+         assert rows[0].columns == [

+             "Priority",

+             "Test Area",

+             "Test Case",

+             "i386",

+             "ppc",

+             "x86_64",

+             "References",

+         ]

  

      def test_find_resultrows_column_titles_sanitize(self):

          """Test the cleanups find_resultrows does on column names."""
@@ -514,8 +607,15 @@ 

  |-

  """

          rows = rs.find_resultrows(text)

-         assert rows[0].columns == ['Priority', 'Test Area', 'Test Case', 'i386', 'ppc', 'x86_64', 'References']

- 

+         assert rows[0].columns == [

+             "Priority",

+             "Test Area",

+             "Test Case",

+             "i386",

+             "ppc",

+             "x86_64",

+             "References",

+         ]

  

      def test_result_string_template(self):

          """Test string representation and result_template of Result
@@ -532,12 +632,13 @@ 

          assert str(res) == "Result: Fail from adamwill"

          assert res.result_template == "{{result|fail|adamwill}}"

  

-         # FIXME: use rené when py3 is merged

-         res = rs.Result(status="warn", user="rene", bugs=['435678', '1340987'])

-         assert str(res) == "Result: Warn from rene, bugs: 435678, 1340987"

-         assert res.result_template == "{{result|warn|rene|435678|1340987}}"

+         res = rs.Result(status="warn", user="rené", bugs=["435678", "1340987"])

+         assert str(res) == "Result: Warn from rené, bugs: 435678, 1340987"

+         assert res.result_template == "{{result|warn|rené|435678|1340987}}"

  

-         res = rs.Result(status="pass", user="kparal", comment="some comment <ref>some reference</ref>")

+         res = rs.Result(

+             status="pass", user="kparal", comment="some comment <ref>some reference</ref>"

+         )

          assert str(res) == "Result: Pass from kparal, comment: some comment some reference"

          assert res.result_template == "{{result|pass|kparal}}some comment <ref>some reference</ref>"

  
@@ -549,10 +650,17 @@ 

          assert str(res) == "BOT Result: Pass from coconut"

          assert res.result_template == "{{result|pass|coconut|bot=true}}"

  

+     def test_from_result_template_bug_comment(self):

+         """Test from_result_template handling of bug numbers with

+         comment refs.

+         """

+         res = rs.Result.from_result_template("{{result|fail|bob|1034592#c8}}")

+         assert res.status == "fail"

+         assert res.user == "bob"

+         assert res.bugs == ["1034592"]

  

-     def test_resultrow_equality(self):

-         """Test the equality of ResultRows."""

-         # FIXME: on py3 this will be 'equals' method not a real ==

+     def test_resultrow_matches(self):

+         """Test the 'matches' method of ResultRows."""

          text = """

  {| class="wikitable sortable" border="1"

  |-
@@ -572,15 +680,19 @@ 

          row1.secid = row2.secid = 1

          assert row1.matches(row2)

          row2.name = "foo"

-         assert row1 != row2

+         assert not row1.matches(row2)

          row2.name = row1.name

          row2.secid = 2

-         assert row1 != row2

+         assert not row1.matches(row2)

          row2.secid = row1.secid

          row2.origtext = "foo"

-         assert row1 != row2

+         assert not row1.matches(row2)

          row2.origtext = row1.origtext

          row2.testcase = "foo"

-         assert row1 != row2

+         assert not row1.matches(row2)

+         # test with a different type of object

+         notarow = "foo"

+         assert not row1.matches(notarow)

+ 

  

  # vim: set textwidth=100 ts=8 et sw=4:

file modified
+478 -160
@@ -1,8 +1,8 @@ 

  # Copyright (C) 2016 Red Hat

  #

- # This file is part of wikitcms.

+ # This file is part of python-wikitcms.

  #

- # wikitcms is free software; you can redistribute it and/or modify

+ # python-wikitcms is free software; you can redistribute it and/or modify

  # it under the terms of the GNU General Public License as published by

  # the Free Software Foundation, either version 3 of the License, or

  # (at your option) any later version.
@@ -18,17 +18,17 @@ 

  # Author: Adam Williamson <awilliam@redhat.com>

  

  # these are all kinda inappropriate for pytest patterns

- # pylint: disable=old-style-class, no-init, protected-access, no-self-use, unused-argument

+ # pylint: disable=no-init, protected-access, no-self-use, unused-argument, too-many-arguments

+ # pylint: disable=too-few-public-methods

  

  """Tests for wiki.py."""

  

- from __future__ import unicode_literals

- from __future__ import print_function

+ from unittest import mock

  

- # note: we need the external 'mock', unittest.mock does not seem to

- # work correctly with the @patch decorator still

- import mock

+ import openidc_client

+ import openidc_client.requestsauth

  import pytest

+ 

  import wikitcms.wiki as wk

  import wikitcms.event

  import wikitcms.result
@@ -44,19 +44,23 @@ 

  [[Category: Fedora Templates]]

  """

  

- class FakeRow(object):

+ 

+ class FakeRow:

      """This is a very bare fake ResultRow class; we need it to fully

      test report_validation_results, can't really do it with Mocks. We

      use mock to replace the 'real' page.find_resultrow method with the

      fake_findrow method below, which returns instances of this class.

      """

+ 

      def __init__(self, name):

          self.name = name

  

      def matches(self, other):

+         """Simple matches replacement."""

          return self.name == other.name

  

- def fake_findrow(self, testcase='', section='', testname='', env=''):

+ 

+ def fake_findrow(self, testcase="", section="", testname="", env=""):

      """This is a fake find_resultrow function which just returns

      FakeRows based on the values it's given. We use mock to replace

      Page instances' find_resultrow methods with this, further down.
@@ -65,232 +69,546 @@ 

      there are two rows with equal case, section and name but each

      covering different envs, but we'll test that in its own test.

      """

-     name = '.'.join((testcase, section, testname))

+     name = ".".join((testcase, section, testname))

      return FakeRow(name)

  

+ 

+ def fake_pginit1(self, site, release, testtype, milestone, compose, info=None, modular=False):

+     """Fake page.__init__ for testing get_validation_page guessing.

+     This one gives us pages that 'exist' if they're Rawhide and don't

+     otherwise.

+     """

+     self.milestone = milestone

+     if milestone == "Rawhide":

+         self.exists = True

+     else:

+         self.exists = False

+ 

+ 

+ def fake_pginit2(self, site, release, testtype, milestone, compose, info=None, modular=False):

+     """Fake page.__init__ for testing get_validation_page guessing.

+     This one gives us pages that 'exist' if they're Branched and don't

+     otherwise.

+     """

+     self.milestone = milestone

+     if milestone == "Branched":

+         self.exists = True

+     else:

+         self.exists = False

+ 

+ 

+ def fake_pginit3(self, site, release, testtype, milestone, compose, info=None, modular=False):

+     """Fake page.__init__ for testing get_validation_page guessing.

+     This one just never exists.

+     """

+     self.exists = False

+ 

+ 

  class TestWiki:

      """Tests for the functions in wiki.py."""

+ 

      # the 'fjajah' is just to make sure we're running offline; if I

      # screw up and add a test that hits the network it'll cause the

      # tests to hang/fail instead of succeeding a bit slower

-     site = wk.Wiki('fjajah', do_init=False, force_login=False)

+     site = wk.Wiki("fjajah", do_init=False, force_login=False)

  

-     @mock.patch('mwclient.page.Page.__init__', return_value=None)

-     @mock.patch('mwclient.page.Page.text', return_value=FAKE_CURRENT_COMPOSE)

+     @mock.patch("mwclient.page.Page.__init__", return_value=None)

+     @mock.patch("mwclient.page.Page.text", return_value=FAKE_CURRENT_COMPOSE)

      def test_current_compose(self, faketext, fakeinit):

-         assert self.site.current_compose == {'full': '24 Alpha 1.1', 'release': '24', 'milestone': 'Alpha', 'compose': '1.1', 'date': ''}

-         assert self.site.current_modular_compose == {'full': '24 Alpha 1.1', 'release': '24', 'milestone': 'Alpha', 'compose': '1.1', 'date': ''}

+         """Tests for current_compose and current_modular_compose."""

+         assert self.site.current_compose == {

+             "full": "24 Alpha 1.1",

+             "release": "24",

+             "milestone": "Alpha",

+             "compose": "1.1",

+             "date": "",

+         }

+         assert self.site.current_modular_compose == {

+             "full": "24 Alpha 1.1",

+             "release": "24",

+             "milestone": "Alpha",

+             "compose": "1.1",

+             "date": "",

+         }

  

-     @mock.patch('mwclient.page.Page.__init__', return_value=None)

-     @mock.patch('mwclient.page.Page.text', return_value=FAKE_CURRENT_COMPOSE)

-     @mock.patch('wikitcms.wiki.Wiki.get_validation_event', autospec=True)

+     @mock.patch("mwclient.page.Page.__init__", return_value=None)

+     @mock.patch("mwclient.page.Page.text", return_value=FAKE_CURRENT_COMPOSE)

+     @mock.patch("wikitcms.wiki.Wiki.get_validation_event", autospec=True)

      def test_current_event(self, fakeget, faketext, fakeinit):

-         ret = self.site.current_event

-         fakeget.assert_called_with(mock.ANY, compose='1.1', milestone='Alpha', release='24')

+         """Tests for current_event and current_modular_event."""

+         _ = self.site.current_event

+         fakeget.assert_called_with(mock.ANY, compose="1.1", milestone="Alpha", release="24")

          fakeget.reset_mock()

-         ret = self.site.current_modular_event

-         fakeget.assert_called_with(mock.ANY, compose='1.1', milestone='Alpha', release='24', modular=True)

+         _ = self.site.current_modular_event

+         fakeget.assert_called_with(

+             mock.ANY, compose="1.1", milestone="Alpha", release="24", modular=True

+         )

  

-     @mock.patch('mwclient.page.Page.__init__', return_value=None)

-     @mock.patch('mwclient.page.Page.text', return_value='foobar')

-     @mock.patch('mwclient.page.Page.save')

+     @mock.patch("mwclient.page.Page.__init__", return_value=None)

+     @mock.patch("mwclient.page.Page.text", return_value="foobar")

+     @mock.patch("mwclient.page.Page.save")

      def test_add_to_category(self, fakesave, faketext, fakeinit):

-         self.site.add_to_category('Foobar', 'Category:Some category', 'summary')

-         fakesave.assert_called_with('foobar\n[[Category:Some category]]', 'summary', createonly=False)

- 

-     @mock.patch('fedfind.release.Compose.exists', return_value=True)

-     @mock.patch('fedfind.release.Production.label', 'RC-1.6')

-     @mock.patch('fedfind.release.Production.cid', 'Fedora-27-20171105.0')

-     @mock.patch('fedfind.helpers.get_current_release', autospec=True, return_value=27)

-     @mock.patch('mwclient.page.Page.__init__', return_value=None)

-     @mock.patch('mwclient.page.Page.text', return_value=FAKE_CURRENT_COMPOSE)

-     @mock.patch('wikitcms.event.NightlyEvent', autospec=True)

-     @mock.patch('wikitcms.event.ComposeEvent', autospec=True)

-     def test_get_validation_event(self, fakecompose, fakenightly, faketext, fakeinit,

-                                   fakegetcurr, fakecompexists):

+         """Test for add_to_category."""

+         self.site.add_to_category("Foobar", "Category:Some category", "summary")

+         fakesave.assert_called_with(

+             "foobar\n[[Category:Some category]]", "summary", createonly=False

+         )

+         # now check case where page is already in the category

+         faketext.return_value = "foobar\n[[Category:Some category]]"

+         fakesave.reset_mock()

+         self.site.add_to_category("Foobar", "Category:Some category", "summary")

+         # we should return without calling save

+         assert fakesave.call_count == 0

+ 

+     def test_walk_category(self, fakemwp):

+         """Test for walk_category."""

+         # we have to mock up a whole nested category structure here

+         # we're gonna walk 'fakecat1', which contains two regular

+         # pages and 'fakecat2', which contains another regular page

+         fakecat1 = self.site.pages["Category:Somecat"]

+         fakecat1.namespace = 14

+         fakecat2 = self.site.pages["Category:Someothercat"]

+         fakecat1.namespace = fakecat2.namespace = 14

+         fakepg1 = self.site.pages["Somepage1"]

+         fakepg2 = self.site.pages["Somepage2"]

+         fakepg3 = self.site.pages["Somepage3"]

+         fakepg1.namespace = fakepg2.namespace = fakepg3.namespace = 1

+         # this isn't really a perfect test, but it's the best I can

+         # come up with for now

+         with mock.patch("mwclient.listing.GeneratorList.__next__") as fakenext:

+             fakenext.side_effect = [fakepg1, fakepg2, fakecat2, fakepg3]

+             assert list(self.site.walk_category(fakecat1)) == [fakepg1, fakepg2, fakepg3]

+ 

+     @mock.patch("fedfind.release.Compose.exists", return_value=True)

+     @mock.patch("fedfind.release.Production.label", "RC-1.6")

+     @mock.patch("fedfind.release.Production.cid", "Fedora-27-20171105.0")

+     @mock.patch("fedfind.helpers.get_current_release", autospec=True, return_value=27)

+     @mock.patch("mwclient.page.Page.__init__", return_value=None)

+     @mock.patch("mwclient.page.Page.text", return_value=FAKE_CURRENT_COMPOSE)

+     @mock.patch("wikitcms.event.NightlyEvent", autospec=True)

+     @mock.patch("wikitcms.event.ComposeEvent", autospec=True)

+     def test_get_validation_event(

+         self, fakecompose, fakenightly, faketext, fakeinit, fakegetcurr, fakecompexists

+     ):

+         """Various tests for get_validation_event."""

          # current event

-         ret = self.site.get_validation_event()

-         fakecompose.assert_called_with(self.site, '24', 'Alpha', '1.1', modular=False, cid='')

-         ret = self.site.get_validation_event(modular=True)

-         fakecompose.assert_called_with(self.site, '24', 'Alpha', '1.1', modular=True, cid='')

+         self.site.get_validation_event()

+         fakecompose.assert_called_with(self.site, "24", "Alpha", "1.1", modular=False, cid="")

+         self.site.get_validation_event(modular=True)

+         fakecompose.assert_called_with(self.site, "24", "Alpha", "1.1", modular=True, cid="")

          # old-school TC/RC

-         ret = self.site.get_validation_event(23, 'Final', 'TC9')

-         fakecompose.assert_called_with(self.site, 23, 'Final', 'TC9', modular=False, cid='')

-         ret = self.site.get_validation_event(23, 'Beta', 'RC1')

-         fakecompose.assert_called_with(self.site, 23, 'Beta', 'RC1', modular=False, cid='')

+         self.site.get_validation_event(23, "Final", "TC9")

+         fakecompose.assert_called_with(self.site, 23, "Final", "TC9", modular=False, cid="")

+         self.site.get_validation_event(23, "Beta", "RC1")

+         fakecompose.assert_called_with(self.site, 23, "Beta", "RC1", modular=False, cid="")

          # old-school nightly

-         ret = self.site.get_validation_event(23, 'Rawhide', '20151112')

+         self.site.get_validation_event(23, "Rawhide", "20151112")

          fakenightly.assert_called_with(

-             self.site, release=23, milestone='Rawhide', compose='20151112', modular=False)

-         ret = self.site.get_validation_event(23, 'Branched', '20151211', modular=False)

+             self.site, release=23, milestone="Rawhide", compose="20151112", modular=False

+         )

+         self.site.get_validation_event(23, "Branched", "20151211", modular=False)

          fakenightly.assert_called_with(

-             self.site, release=23, milestone='Branched', compose='20151211', modular=False)

+             self.site, release=23, milestone="Branched", compose="20151211", modular=False

+         )

          # Pungi 4 production/candidate

-         ret = self.site.get_validation_event(24, 'Alpha', '1.1')

-         fakecompose.assert_called_with(self.site, 24, 'Alpha', '1.1', modular=False, cid='')

-         ret = self.site.get_validation_event(27, 'Beta', '1.5', modular=True)

-         fakecompose.assert_called_with(self.site, 27, 'Beta', '1.5', modular=True, cid='')

+         self.site.get_validation_event(24, "Alpha", "1.1")

+         fakecompose.assert_called_with(self.site, 24, "Alpha", "1.1", modular=False, cid="")

+         self.site.get_validation_event(27, "Beta", "1.5", modular=True)

+         fakecompose.assert_called_with(self.site, 27, "Beta", "1.5", modular=True, cid="")

          # Past 23, 'Final' milestone should be converted to 'RC'

-         ret = self.site.get_validation_event(25, 'Final', '1.1')

-         fakecompose.assert_called_with(self.site, 25, 'RC', '1.1', modular=False, cid='')

+         self.site.get_validation_event(25, "Final", "1.1")

+         fakecompose.assert_called_with(self.site, 25, "RC", "1.1", modular=False, cid="")

          # Pungi 4 nightly

-         ret = self.site.get_validation_event(24, 'Rawhide', '20160222.n.0')

+         self.site.get_validation_event(24, "Rawhide", "20160222.n.0")

          fakenightly.assert_called_with(

-             self.site, release=24, milestone='Rawhide', compose='20160222.n.0', modular=False)

-         ret = self.site.get_validation_event(24, 'Branched', '20160315.n.1')

+             self.site, release=24, milestone="Rawhide", compose="20160222.n.0", modular=False

+         )

+         self.site.get_validation_event(24, "Branched", "20160315.n.1")

          fakenightly.assert_called_with(

-             self.site, release=24, milestone='Branched', compose='20160315.n.1', modular=False)

-         ret = self.site.get_validation_event(27, 'Branched', '20171110.n.1', modular=True)

+             self.site, release=24, milestone="Branched", compose="20160315.n.1", modular=False

+         )

+         self.site.get_validation_event(27, "Branched", "20171110.n.1", modular=True)

          fakenightly.assert_called_with(

-             self.site, release=27, milestone='Branched', compose='20171110.n.1', modular=True)

+             self.site, release=27, milestone="Branched", compose="20171110.n.1", modular=True

+         )

          # Rawhide nightly compose ID

-         ret = self.site.get_validation_event(cid='Fedora-Rawhide-20180220.n.0', modular=False)

+         self.site.get_validation_event(cid="Fedora-Rawhide-20180220.n.0", modular=False)

          fakenightly.assert_called_with(

-             self.site, release='28', milestone='Rawhide', compose='20180220.n.0', modular=False)

+             self.site, release="28", milestone="Rawhide", compose="20180220.n.0", modular=False

+         )

          # Branched nightly compose ID

-         ret = self.site.get_validation_event(cid='Fedora-27-20171120.n.0', modular=False)

+         self.site.get_validation_event(cid="Fedora-27-20171120.n.0", modular=False)

          fakenightly.assert_called_with(

-             self.site, release='27', milestone='Branched', compose='20171120.n.0', modular=False)

+             self.site, release="27", milestone="Branched", compose="20171120.n.0", modular=False

+         )

          # Candidate compose ID (note compose ID passthrough)

-         ret = self.site.get_validation_event(cid='Fedora-27-20171105.0', modular=False)

+         self.site.get_validation_event(cid="Fedora-27-20171105.0", modular=False)

          fakecompose.assert_called_with(

-             self.site, '27', 'RC', '1.6', modular=False, cid='Fedora-27-20171105.0')

+             self.site, "27", "RC", "1.6", modular=False, cid="Fedora-27-20171105.0"

+         )

  

          with pytest.raises(ValueError):

              # Non-nightly compose but no milestone

-             self.site.get_validation_event(24, '', '1.1')

-             # Invalid composes

-             self.site.get_validation_event(24, 'Branched', 'foobar')

-             self.site.get_validation_event(24, 'Branched', 'TC1a')

-             self.site.get_validation_event(24, 'Branched', '1.1a')

-             # looks kinda like a date but is not one

-             self.site.get_validation_event(24, 'Branched', '20161356')

-             self.site.get_validation_event(24, 'Branched', '20161356.n.0')

+             self.site.get_validation_event(24, "", "1.1")

+         # Invalid composes

+         with pytest.raises(ValueError):

+             self.site.get_validation_event(24, "Branched", "foobar")

+         with pytest.raises(ValueError):

+             self.site.get_validation_event(24, "Branched", "TC1a")

+         with pytest.raises(ValueError):

+             self.site.get_validation_event(24, "Branched", "1.1a")

+         # looks kinda like a date but is not one

+         with pytest.raises(ValueError):

+             self.site.get_validation_event(24, "Branched", "20161356")

+         with pytest.raises(ValueError):

+             self.site.get_validation_event(24, "Branched", "20161356.n.0")

+         with pytest.raises(ValueError):

              # invalid type

-             self.site.get_validation_event(24, 'Branched', '20160314.x.0')

- 

-     @mock.patch('wikitcms.event.NightlyEvent.__init__', return_value=None, autospec=True)

-     @mock.patch('wikitcms.event.NightlyEvent.result_pages', [mock.Mock(**{'text.return_value': 'foobar'})])

-     def test_get_validation_event_guess_nightly(self, fakeinit):

-         # We can't quite test this fully as we can't patch the Branched

-         # event to not be found but the Rawhide event to be found

-         ret = self.site.get_validation_event(24, '', '20160314.n.0')

+             self.site.get_validation_event(24, "Branched", "20160314.x.0")

+ 

+     @mock.patch("wikitcms.wiki.Wiki.allresults")

+     @mock.patch("mwclient.page.Page.text", return_value="foobar")

+     def test_get_validation_event_guess_nightly(self, faketext, fakeresults, fakemwp):

+         """Test get_validation_event guessing milestone for nightly

+         events.

+         """

+         fakepage = self.site.pages["Test Results:Fedora 24 Rawhide 20160314.n.0 Base"]

+         otherpage = self.site.pages["Somepage"]

+         # this will test the Rawhide path, as the *first* fakepages

+         # call will hit

+         fakeresults.side_effect = ([fakepage],)

+         ret = self.site.get_validation_event(24, "", "20160314.n.0")

+         assert isinstance(ret, wikitcms.event.NightlyEvent)

+         assert ret.milestone == "Rawhide"

+         # this will test the Branched path, as the *second* fakepages

+         # call will hit

+         fakeresults.side_effect = ([otherpage], [fakepage])

+         ret = self.site.get_validation_event(24, "", "20160314.n.0")

          assert isinstance(ret, wikitcms.event.NightlyEvent)

+         assert ret.milestone == "Branched"

+         # ...and this won't hit anywhere and should raise

+         fakeresults.side_effect = ([otherpage], [otherpage])

+         with pytest.raises(ValueError) as err:

+             self.site.get_validation_event(24, "", "20160314.n.0")

+             assert "Could not find any event" in err

  

-     @mock.patch('mwclient.page.Page.__init__', return_value=None, autospec=True)

-     @mock.patch('mwclient.page.Page.text', return_value=FAKE_CURRENT_COMPOSE)

-     @mock.patch('wikitcms.page.NightlyPage', autospec=True)

-     @mock.patch('wikitcms.page.ComposePage', autospec=True)

-     def test_get_validation_page(self, fakecompose, fakenightly, faketext, fakeinit):

+     @mock.patch("wikitcms.event.ComposeEvent.__init__", return_value=None, autospec=True)

+     @mock.patch("wikitcms.wiki.Wiki.current_event")

+     def test_get_validation_event_guess_nomatch(self, fakecurr, fakeevent, fakemwp):

+         """Check that when we guess but specify a release or milestone

+         and the guessed release doesn't match the requested release or

+         milestone, we fail.

+         """

+         event = wikitcms.event.ComposeEvent(self.site, 24, "Beta", "1.1")

+         fakeevent.return_value = event

+         with pytest.raises(ValueError) as err:

+             self.site.get_validation_event(release=23)

+             assert "does not match requested" in err

+         with pytest.raises(ValueError) as err:

+             self.site.get_validation_event(milestone="Final")

+             assert "does not match requested" in err

+ 

+     @mock.patch("fedfind.release.Compose.exists", return_value=True)

+     @mock.patch("fedfind.release.Production.label", "RC-1.6")

+     @mock.patch("fedfind.release.Production.cid", "Fedora-27-20171105.0")

+     @mock.patch("fedfind.helpers.get_current_release", autospec=True, return_value=27)

+     @mock.patch("mwclient.page.Page.__init__", return_value=None, autospec=True)

+     @mock.patch("mwclient.page.Page.text", return_value=FAKE_CURRENT_COMPOSE)

+     @mock.patch("wikitcms.page.NightlyPage", autospec=True)

+     @mock.patch("wikitcms.page.ComposePage", autospec=True)

+     def test_get_validation_page(

+         self, fakecompose, fakenightly, faketext, fakeinit, fakegetcurr, fakecompexists

+     ):

+         """Various tests for get_validation_page."""

          # current event

-         ret = self.site.get_validation_page('Installation')

+         self.site.get_validation_page("Installation")

          fakecompose.assert_called_with(

-             self.site, '24', 'Installation', 'Alpha', '1.1', modular=False)

+             self.site, "24", "Installation", "Alpha", "1.1", modular=False

+         )

          # old-school TC/RC

-         ret = self.site.get_validation_page('Installation', 23, 'Final', 'TC9')

-         fakecompose.assert_called_with(self.site, 23, 'Installation', 'Final', 'TC9', modular=False)

-         ret = self.site.get_validation_page('Installation', 23, 'Beta', 'RC1')

-         fakecompose.assert_called_with(self.site, 23, 'Installation', 'Beta', 'RC1', modular=False)

+         self.site.get_validation_page("Installation", 23, "Final", "TC9")

+         fakecompose.assert_called_with(self.site, 23, "Installation", "Final", "TC9", modular=False)

+         self.site.get_validation_page("Installation", 23, "Beta", "RC1")

+         fakecompose.assert_called_with(self.site, 23, "Installation", "Beta", "RC1", modular=False)

          # old-school nightly

-         ret = self.site.get_validation_page('Installation', 23, 'Rawhide', '20151112')

+         self.site.get_validation_page("Installation", 23, "Rawhide", "20151112")

          fakenightly.assert_called_with(

-             self.site, 23, 'Installation', 'Rawhide', '20151112', modular=False)

-         ret = self.site.get_validation_page('Installation', 23, 'Branched', '20151211')

+             self.site, 23, "Installation", "Rawhide", "20151112", modular=False

+         )

+         self.site.get_validation_page("Installation", 23, "Branched", "20151211")

          fakenightly.assert_called_with(

-             self.site, 23, 'Installation', 'Branched', '20151211', modular=False)

+             self.site, 23, "Installation", "Branched", "20151211", modular=False

+         )

          # Pungi 4 production/candidate

-         ret = self.site.get_validation_page('Installation', 24, 'Alpha', '1.1')

-         fakecompose.assert_called_with(self.site, 24, 'Installation', 'Alpha', '1.1', modular=False)

-         ret = self.site.get_validation_page('Installation', 27, 'Beta', '1.5', modular=True)

-         fakecompose.assert_called_with(self.site, 27, 'Installation', 'Beta', '1.5', modular=True)

+         self.site.get_validation_page("Installation", 24, "Alpha", "1.1")

+         fakecompose.assert_called_with(self.site, 24, "Installation", "Alpha", "1.1", modular=False)

+         self.site.get_validation_page("Installation", 27, "Beta", "1.5", modular=True)

+         fakecompose.assert_called_with(self.site, 27, "Installation", "Beta", "1.5", modular=True)

          # Past 23, 'Final' milestone should be converted to 'RC'

-         ret = self.site.get_validation_page('Installation', 25, 'Final', '1.1')

-         fakecompose.assert_called_with(self.site, 25, 'Installation', 'RC', '1.1', modular=False)

+         self.site.get_validation_page("Installation", 25, "Final", "1.1")

+         fakecompose.assert_called_with(self.site, 25, "Installation", "RC", "1.1", modular=False)

          # Pungi 4 nightly

-         ret = self.site.get_validation_page('Installation', 24, 'Rawhide', '20160222.n.0')

+         self.site.get_validation_page("Installation", 24, "Rawhide", "20160222.n.0")

+         fakenightly.assert_called_with(

+             self.site, 24, "Installation", "Rawhide", "20160222.n.0", modular=False

+         )

+         self.site.get_validation_page("Installation", 24, "Branched", "20160315.n.1")

          fakenightly.assert_called_with(

-             self.site, 24, 'Installation', 'Rawhide', '20160222.n.0', modular=False)

-         ret = self.site.get_validation_page('Installation', 24, 'Branched', '20160315.n.1')

+             self.site, 24, "Installation", "Branched", "20160315.n.1", modular=False

+         )

+         self.site.get_validation_page("Installation", 27, "Branched", "20171110.n.1", modular=True)

          fakenightly.assert_called_with(

-             self.site, 24, 'Installation', 'Branched', '20160315.n.1', modular=False)

-         ret = self.site.get_validation_page(

-             'Installation', 27, 'Branched', '20171110.n.1', modular=True)

+             self.site, 27, "Installation", "Branched", "20171110.n.1", modular=True

+         )

+         # Rawhide nightly compose ID

+         self.site.get_validation_page("Installation", cid="Fedora-Rawhide-20180220.n.0")

          fakenightly.assert_called_with(

-             self.site, 27, 'Installation', 'Branched', '20171110.n.1', modular=True)

+             self.site, "28", "Installation", "Rawhide", "20180220.n.0", modular=False

+         )

+         # Branched nightly compose ID

+         self.site.get_validation_page("Installation", cid="Fedora-27-20171120.n.0")

+         fakenightly.assert_called_with(

+             self.site, "27", "Installation", "Branched", "20171120.n.0", modular=False

+         )

+         # Candidate compose ID

+         self.site.get_validation_page("Installation", cid="Fedora-27-20171105.0")

+         fakecompose.assert_called_with(self.site, "27", "Installation", "RC", "1.6", modular=False)

  

          with pytest.raises(ValueError):

              # Non-nightly compose but no milestone

-             self.site.get_validation_page('Installation', 24, '', '1.1')

-             # Invalid composes

-             self.site.get_validation_page('Installation', 24, 'Branched', 'foobar')

-             self.site.get_validation_page('Installation', 24, 'Branched', 'TC1a')

-             self.site.get_validation_page('Installation', 24, 'Branched', '1.1a')

-             # looks kinda like a date but is not one

-             self.site.get_validation_page('Installation', 24, 'Branched', '20161356')

-             self.site.get_validation_page('Installation', 24, 'Branched', '20161356.n.0')

+             self.site.get_validation_page("Installation", 24, "", "1.1")

+         # Invalid composes

+         with pytest.raises(ValueError):

+             self.site.get_validation_page("Installation", 24, "Branched", "foobar")

+         with pytest.raises(ValueError):

+             self.site.get_validation_page("Installation", 24, "Branched", "TC1a")

+         with pytest.raises(ValueError):

+             self.site.get_validation_page("Installation", 24, "Branched", "1.1a")

+         # looks kinda like a date but is not one

+         with pytest.raises(ValueError):

+             self.site.get_validation_page("Installation", 24, "Branched", "20161356")

+         with pytest.raises(ValueError):

+             self.site.get_validation_page("Installation", 24, "Branched", "20161356.n.0")

+         with pytest.raises(ValueError):

              # invalid type

-             self.site.get_validation_page('Installation', 24, 'Branched', '20160314.x.0')

- 

-     @mock.patch('wikitcms.page.NightlyPage.__init__', return_value=None, autospec=True)

-     @mock.patch('wikitcms.page.NightlyPage.exists', True, create=True)

-     def test_get_validation_page_guess_nightly(self, fakeinit):

-         # We can't quite test this fully as we can't patch the Branched

-         # page to not be found but the Rawhide page to be found

-         ret = self.site.get_validation_page('Installation', 24, '', '20160314.n.0')

+             self.site.get_validation_page("Installation", 24, "Branched", "20160314.x.0")

+ 

+     def test_get_validation_page_guess_nightly(self):

+         """Test get_validation_page guessing milestone for nightly

+         events.

+         """

+         # fake_pginit1 will test the Rawhide path

+         with mock.patch("wikitcms.page.NightlyPage.__init__", fake_pginit1):

+             ret = self.site.get_validation_page("Installation", 24, "", "20160314.n.0")

+         assert isinstance(ret, wikitcms.page.NightlyPage)

+         assert ret.milestone == "Rawhide"

+         # fake_pginit2 will test the Rawhide path

+         with mock.patch("wikitcms.page.NightlyPage.__init__", fake_pginit2):

+             ret = self.site.get_validation_page("Installation", 24, "", "20160314.n.0")

          assert isinstance(ret, wikitcms.page.NightlyPage)

+         assert ret.milestone == "Branched"

+         # ...and fake_pginit3 won't hit anywhere and should raise

+         with mock.patch("wikitcms.page.NightlyPage.__init__", fake_pginit3):

+             with pytest.raises(ValueError) as err:

+                 ret = self.site.get_validation_page("Installation", 24, "", "20160314.n.0")

+                 assert "Could not find any event" in err

+ 

+     def test_get_validation_page_guess_nomatch(self, fakemwp):

+         """Check that when we guess but specify a release or milestone

+         and the guessed release doesn't match the requested release or

+         milestone, we fail.

+         """

+         fakecurr = {

+             "full": "32 Branched 20200407.n.0",

+             "release": "32",

+             "milestone": "Branched",

+             "compose": "",

+             "date": "20200407.n.0",

+         }

+         with mock.patch("wikitcms.wiki.Wiki.current_compose", fakecurr):

+             with pytest.raises(ValueError) as err:

+                 self.site.get_validation_page("Installation", release=23)

+                 assert "does not match requested" in err

+             with pytest.raises(ValueError) as err:

+                 self.site.get_validation_page("Installation", milestone="Final")

+                 assert "does not match requested" in err

  

-     # we use the find_resultrow and ResultRow dummies from the top of the file here

-     @mock.patch('wikitcms.page.NightlyPage.__init__', return_value=None, autospec=True)

-     @mock.patch('wikitcms.page.ComposePage.__init__', return_value=None, autospec=True)

-     @mock.patch('wikitcms.page.NightlyPage.find_resultrow', fake_findrow)

-     @mock.patch('wikitcms.page.ComposePage.find_resultrow', fake_findrow)

-     @mock.patch('wikitcms.page.NightlyPage.add_results', autospec=True)

-     @mock.patch('wikitcms.page.ComposePage.add_results', autospec=True)

+     # we use the find_resultrow and ResultRow dummies from the top of

+     # the file here

+     @mock.patch("wikitcms.page.NightlyPage.__init__", return_value=None, autospec=True)

+     @mock.patch("wikitcms.page.ComposePage.__init__", return_value=None, autospec=True)

+     @mock.patch("wikitcms.page.NightlyPage.find_resultrow", fake_findrow)

+     @mock.patch("wikitcms.page.ComposePage.find_resultrow", fake_findrow)

+     @mock.patch("wikitcms.page.NightlyPage.add_results", autospec=True)

+     @mock.patch("wikitcms.page.ComposePage.add_results", autospec=True)

      def test_report_validation_results(self, fakecompose, fakenightly, fakecompinit, fakenightinit):

+         """Tests for report_validation_results."""

          restups = [

-             wk.ResTuple('Installation', '24', 'Alpha', '1.1', 'QA:Testcase_foo', status='pass',

-                         user='adamwill'),

-             wk.ResTuple('Installation', '24', 'Alpha', '1.1', 'QA:Testcase_bar', status='pass',

-                         user='adamwill', section='testsec', env='testenv1'),

-             wk.ResTuple('Installation', '24', 'Alpha', '1.1', 'QA:Testcase_bar', status='pass',

-                         user='adamwill', section='testsec', env='testenv2'),

-             wk.ResTuple('Installation', '24', 'Branched', '20160314.n.1', 'QA:Testcase_foo', status='pass',

-                         user='adamwill'),

-             wk.ResTuple('Installation', '24', 'Branched', '20160314.n.1', 'QA:Testcase_bar', status='pass',

-                         user='adamwill', section='testsec', env='testenv1'),

-             wk.ResTuple('Installation', '24', 'Branched', '20160314.n.1', 'QA:Testcase_bar', status='pass',

-                         user='adamwill', section='testsec', env='testenv2')

-             ]

+             wk.ResTuple(

+                 "Installation",

+                 "24",

+                 "Alpha",

+                 "1.1",

+                 "QA:Testcase_foo",

+                 status="pass",

+                 user="adamwill",

+             ),

+             wk.ResTuple(

+                 "Installation",

+                 "24",

+                 "Alpha",

+                 "1.1",

+                 "QA:Testcase_bar",

+                 status="pass",

+                 user="adamwill",

+                 section="testsec",

+                 env="testenv1",

+             ),

+             wk.ResTuple(

+                 "Installation",

+                 "24",

+                 "Alpha",

+                 "1.1",

+                 "QA:Testcase_bar",

+                 status="pass",

+                 user="adamwill",

+                 section="testsec",

+                 env="testenv2",

+             ),

+             wk.ResTuple(

+                 "Installation",

+                 "24",

+                 "Branched",

+                 "20160314.n.1",

+                 "QA:Testcase_foo",

+                 status="pass",

+                 user="adamwill",

+             ),

+             wk.ResTuple(

+                 "Installation",

+                 "24",

+                 "Branched",

+                 "20160314.n.1",

+                 "QA:Testcase_bar",

+                 status="pass",

+                 user="adamwill",

+                 section="testsec",

+                 env="testenv1",

+             ),

+             wk.ResTuple(

+                 "Installation",

+                 "24",

+                 "Branched",

+                 "20160314.n.1",

+                 "QA:Testcase_bar",

+                 status="pass",

+                 user="adamwill",

+                 section="testsec",

+                 env="testenv2",

+             ),

+         ]

          self.site.report_validation_results(restups)

          # this ought to call add_results once for each page.

          for fake in (fakecompose, fakenightly):

              assert len(fake.call_args_list) == 1

              compargs = fake.call_args

-             # compargs[0] is all args as a list, add_results takes a single arg which is the resdict

-             # first item is self (when autospec is used)

+             # compargs[0] is all args as a list, add_results takes a

+             # single arg which is the resdict first item is self (when

+             # autospec is used)

              resdict = compargs[0][1]

-             # resdict should have exactly two entries, one per test instance

+             # resdict should have exactly two entries, one per test

+             # instance

              assert len(resdict) == 2

              # we're going to sort the dict items for ease of testing.

              items = sorted(list(resdict.items()), key=lambda x: x[0].name, reverse=True)

              # first resdict entry: should be for the 'foo' test case...

              (key, value) = items[0]

-             assert key.name == 'QA:Testcase_foo..'

-             # ...and value should be a 1-item list of a single 2-tuple, env '', result a Result instance

+             assert key.name == "QA:Testcase_foo.."

+             # ...and value should be a 1-item list of a single 2-tuple,

+             # env '', result a Result instance

              assert len(value) == 1

              (env, res) = value[0]

-             assert env == ''

+             assert env == ""

              assert isinstance(res, wikitcms.result.Result)

              (key, value) = items[1]

-             # second resdict entry: should be for the 'bar' test case with section name...

-             assert key.name == 'QA:Testcase_bar.testsec.'

-             # ... and value should be a 2-item list of 2-tuples, differing in the environment

+             # second resdict entry: should be for the 'bar' test case

+             # with section name...

+             assert key.name == "QA:Testcase_bar.testsec."

+             # ... and value should be a 2-item list of 2-tuples,

+             # differing in the environment

              assert len(value) == 2

              (env1, res) = value[0]

-             assert env1 == 'testenv1'

+             assert env1 == "testenv1"

              assert isinstance(res, wikitcms.result.Result)

              (env1, res) = value[1]

-             assert env1 == 'testenv2'

+             assert env1 == "testenv2"

              assert isinstance(res, wikitcms.result.Result)

  

+     @mock.patch("mwclient.listing.Category.members")

+     def test_testtypes(self, fakemembers, fakemwp):

+         """Tests for testtypes and, incidentally, matrices."""

+         # this is more or less what the return of

+         # category.members(generator=False) looks like - it's an

+         # mwclient 'List' instance

+         instmx = {"title": "Template:Installation test matrix", "pageid": 54437, "ns": 10}

+         basemx = {"title": "Template:Base test matrix", "pageid": 54433, "ns": 10}

+         fakemembers.return_value = [instmx, basemx]

+         assert self.site.testtypes == ["Installation", "Base"]

+ 

+     @mock.patch("mwclient.listing.Category.members")

+     def test_modular_testtypes(self, fakemembers, fakemwp):

+         """Tests for modular testtypes and, incidentally, matrices."""

+         # this is more or less what the return of

+         # category.members(generator=False) looks like - it's an

+         # mwclient 'List' instance

+         instmx = {"title": "Template:Installation modular test matrix", "pageid": 77036, "ns": 10}

+         servmx = {"title": "Template:Server modular test matrix", "pageid": 77040, "ns": 10}

+         fakemembers.return_value = [instmx, servmx]

+         assert self.site.modular_testtypes == ["Installation", "Server"]

+ 

+     @mock.patch("mwclient.Site.login", autospec=True)

+     @mock.patch("mwclient.Site.site_init", autospec=True)

+     def test_login(self, fakeinit, fakelogin):

+         """Tests for login(), including all the OpenID stuff."""

+         # self.site should just wrap mwclient as it's not fp.o

+         self.site.login(username="foo", password="bar")

+         assert fakelogin.call_count == 1

+         assert fakelogin.call_args[1]["username"] == "foo"

+         assert fakelogin.call_args[1]["password"] == "bar"

+         # check host splitting

+         fakelogin.reset_mock()

+         site = wk.Wiki(["https", "fjajah"], do_init=False, force_login=False)

+         site.login(username="foo", password="bar")

+         assert fakelogin.call_count == 1

+ 

+         # now, activate the openid bits

+         fakelogin.reset_mock()

+         site = wk.Wiki("fedoraproject.org", do_init=False, force_login=False)

+         # have to fake this up

+         site.version = (1, 30)

+         site.login()

+         assert isinstance(site.connection.auth, openidc_client.requestsauth.OpenIDCClientAuther)

+         assert site.connection.auth.scopes == ["openid", "https://fedoraproject.org/wiki/api"]

+         assert isinstance(site.connection.auth.client, openidc_client.OpenIDCClient)

+         assert site.connection.auth.client.app_id == "wikitcms"

+         assert site.connection.auth.client.idp == "https://id.fedoraproject.org/openidc/"

+         assert site.connection.auth.client.idp_mapping == {

+             "Token": "Token",

+             "Authorization": "Authorization",

+         }

+         assert site.connection.auth.client.client_id == "wikitcms"

+         # <hacker voice>I'M IN</hacker voice>

+         assert site.connection.auth.client.client_secret == "notsecret"

+         assert site.connection.auth.client.useragent == "wikitcms"

+         # this is when we can't import the OpenID bits - the idea is

+         # we still work for non-OpenID paths in that case and only die

+         # here

+         with mock.patch("wikitcms.wiki.OpenIDCClient", None):

+             with pytest.raises(ImportError):

+                 site.login()

+         with mock.patch("wikitcms.wiki.OpenIDCClientAuther", None):

+             with pytest.raises(ImportError):

+                 site.login()

+ 

+ 

  # vim: set textwidth=100 ts=8 et sw=4:

file modified
+18 -10
@@ -1,12 +1,20 @@ 

  [tox]

- envlist = py27,py34,py35,py36,py37,py38

- skip_missing_interpreters=true

+ envlist = py{36,37,38}-ci

+ skip_missing_interpreters = true

+ isolated_build = true

+ 

  [testenv]

- deps=-r{toxinidir}/install.requires

-      -r{toxinidir}/tests.requires

-      -r{toxinidir}/tox.requires

- commands=py.test --cov-report term-missing --cov-report xml --cov wikitcms

-          diff-cover coverage.xml --fail-under=90

-          diff-quality --violations=pylint --fail-under=90 

- setenv =

-     PYTHONPATH = {toxinidir}

+ deps =

+     -r{toxinidir}/install.requires

+     -r{toxinidir}/tests.requires

+     ci: -r{toxinidir}/ci.requires

+ 

+ commands =

+     py.test

+     ci: coverage run -m pytest {posargs}

+     ci: coverage combine

+     ci: coverage report

+     ci: coverage xml

+     ci: diff-cover coverage.xml --fail-under=90

+     ci: diff-quality --violations=pylint --fail-under=90

+     ci: black src/ tests/ --check

no initial comment

Build succeeded.

Pull-Request has been merged by adamwill

3 years ago
Changes Summary 40
+2 -0
file changed
.pylintrc
+8
file added
.zuul.yaml
+4 -2
file changed
MANIFEST.in
+4 -4
file changed
README.md
+2 -1
file renamed
tox.requires
ci.requires
+14
file added
ci/tox.yaml
+18
file added
pyproject.toml
+3 -7
file changed
release.sh
+9 -29
file changed
setup.py
+0 -3
file renamed
wikitcms/__init__.py
src/wikitcms/__init__.py
+95 -75
file renamed
wikitcms/event.py
src/wikitcms/event.py
+3 -9
file renamed
wikitcms/exceptions.py
src/wikitcms/exceptions.py
+59 -57
file renamed
wikitcms/helpers.py
src/wikitcms/helpers.py
+204 -163
file renamed
wikitcms/listing.py
src/wikitcms/listing.py
+253 -224
file renamed
wikitcms/page.py
src/wikitcms/page.py
+14 -14
file renamed
wikitcms/release.py
src/wikitcms/release.py
+147 -212
file renamed
wikitcms/result.py
src/wikitcms/result.py
+282 -188
file renamed
wikitcms/wiki.py
src/wikitcms/wiki.py
+1 -1
file changed
tests.requires
+118
file added
tests/conftest.py
+815
file added
tests/data/Fedora-32-20200312.0.ami.dgdata.1.json
+50
file added
tests/data/Fedora-32-20200312.0.ami.dgdata.2.json
+1342
file added
tests/data/Fedora-32-20200312.0.images.json
+256
file added
tests/data/Template:Fedora 32 Beta 1.2 AMI.seedtext
+264
file added
tests/data/Template:Fedora 32 Beta 1.2 Download.seedtext
+199
file added
tests/data/Test_Day:2013-11-05_Printing.txt
+500
file added
tests/data/Test_Day:2016-10-24_Cloud.longrefs.txt
+498
file added
tests/data/Test_Day:2016-10-24_Cloud.txt
+242
file added
tests/data/Test_Results:Fedora_32_Branched_20200322.n.0_Installation.sections.json
+833
file added
tests/data/Test_Results:Fedora_32_Branched_20200322.n.0_Installation.txt
+132
file added
tests/data/Test_Results:Fedora_32_Branched_20200322.n.0_Server.sections.json
+184
file added
tests/data/Test_Results:Fedora_32_Branched_20200322.n.0_Server.txt
+314 -100
file changed
tests/test_event.py
+77 -65
file changed
tests/test_helpers.py
+350
file added
tests/test_listing.py
+795
file added
tests/test_page.py
+77
file added
tests/test_release.py
+179 -67
file changed
tests/test_result.py
+478 -160
file changed
tests/test_wiki.py
+18 -10
file changed
tox.ini