]> git.apps.os.sepia.ceph.com Git - teuthology.git/commitdiff
describe-tests: add options to show the combinations of a suite
authorJosh Durgin <jdurgin@redhat.com>
Wed, 2 Dec 2015 08:12:18 +0000 (00:12 -0800)
committerJosh Durgin <jdurgin@redhat.com>
Wed, 9 Dec 2015 21:04:54 +0000 (13:04 -0800)
Match the behavior of teuthology-suite and generate the full product
of all fragments by default. Include the same filtering and subset
options as well. Just print an asii table for now.

Signed-off-by: Josh Durgin <jdurgin@redhat.com>
scripts/describe_tests.py
teuthology/describe_tests.py
teuthology/test/test_describe_tests.py

index b59ff6258a57e4260533a38eae9d0096078aa402..09924353f02b85c402f619178aec89f8cff54b4d 100644 (file)
@@ -12,7 +12,7 @@ Describe the contents of a qa suite by reading 'description' elements
 from yaml files in the suite.
 
 The 'description' element should contain a list with a dictionary
-of fields, e.g.:
+of key/value pairs for entries, i.e.:
 
 description:
 - field1: value1
@@ -31,6 +31,27 @@ optional arguments:
                                      include [default: desc]
   --show-facet [yes|no]              List the facet of each file
                                      [default: yes]
+
+options only for describing combinations represented by a suite:
+  -c, --combinations                 Describe test combinations rather than
+                                     individual yaml fragments
+  --filter <keywords>                Only list jobs whose filenames contain at
+                                     least one of the keywords in the comma
+                                     separated keyword string specified.
+  --filter-out <keywords>            Do not list jobs whose filenames contain
+                                     any of the keywords in the comma separated
+                                     keyword string specified.
+  -l <jobs>, --limit <jobs>          List at most this many jobs
+                                     [default: 0]
+  --subset <index/outof>             Instead of listing the entire
+                                     suite, break the set of jobs into
+                                     <outof> pieces (each of which
+                                     will contain each facet at least
+                                     once) and list piece <index>.
+                                     Listing 0/<outof>, 1/<outof>,
+                                     2/<outof> ... <outof>-1/<outof>
+                                     will list all jobs in the
+                                     suite (many more than once).
 """
 
 
index bae834f239648efc1bd59a86b3da641170126f40..bdb8bf02c5339ccfc0d81fbe95597bb6786a67e7 100644 (file)
 # -*- coding: utf-8 -*-
+
 from prettytable import PrettyTable, FRAME, ALL
 import os
 import yaml
 
 from teuthology.exceptions import ParseError
+from teuthology.suite import build_matrix, combine_path
 
 def main(args):
     suite_dir = os.path.abspath(args["<suite_dir>"])
     fields = args["--fields"].split(',')
     include_facet = args['--show-facet'] == 'yes'
 
+    if args['--combinations']:
+        limit = int(args['--limit'])
+        filter_in = None
+        if args['--filter']:
+            filter_in = [f.strip() for f in args['--filter'].split(',')]
+        filter_out = None
+        if args['--filter-out']:
+            filter_out = [f.strip() for f in args['--filter-out'].split(',')]
+        subset = None
+        if args['--subset']:
+            subset = map(int, args['--subset'].split('/'))
+        describe_combinations(suite_dir, fields, subset,
+                              limit, filter_in, filter_out,
+                              include_facet)
+    else:
+        describe_suite(suite_dir, fields, include_facet)
+
+def get_combinations(suite_dir, fields, subset,
+                     limit, filter_in, filter_out,
+                     include_facet, _isdir=os.path.isdir, _open=open,
+                     _isfile=os.path.isfile, _listdir=os.listdir):
+    configs = [(combine_path(suite_dir, item[0]), item[1]) for item in
+               build_matrix(suite_dir, _isfile, _isdir, _listdir, subset)]
+
+    num_listed = 0
+    rows = []
+
+    facet_headers = set()
+
+    for _, fragment_paths in configs:
+        if limit > 0 and num_listed >= limit:
+            break
+        if filter_in and not any([f in path for f in filter_in
+                                  for path in fragment_paths]):
+            continue
+        if filter_out and any([f in path for f in filter_out
+                               for path in fragment_paths]):
+            continue
+
+        fragment_fields = [extract_info(path, fields, _isdir, _open)
+                           for path in fragment_paths]
+
+        # merge fields from multiple fragments by joining their values with \n
+        metadata = {}
+        for fragment_meta in fragment_fields:
+            for field, value in fragment_meta.items():
+                if value == '':
+                    continue
+                if field in metadata:
+                    metadata[field] += '\n' + str(value)
+                else:
+                    metadata[field] = str(value)
+
+        if include_facet:
+            # map final dir (facet) -> filename without the .yaml suffix
+            for path in fragment_paths:
+                facet = os.path.basename(os.path.dirname(path))
+                metadata[facet] = os.path.basename(path)[:-5]
+                facet_headers.add(facet)
+
+        rows.append(metadata)
+        num_listed += 1
+
+    headers = sorted(facet_headers) + fields
+    return headers, [[row.get(field, '') for field in headers] for row in rows]
+
+def describe_combinations(suite_dir, fields, subset,
+                          limit, filter_in, filter_out,
+                          include_facet):
+    headers, rows = get_combinations(suite_dir, fields, subset,
+                                     limit, filter_in, filter_out,
+                                     include_facet)
+
+    table = PrettyTable(headers)
+    table.align = 'l'
+    table.vrules = ALL
+    table.hrules = ALL
+
+    for row in rows:
+        table.add_row(row)
+
+    print(table)
+
+def describe_suite(suite_dir, fields, include_facet):
     try:
         rows = tree_with_info(suite_dir, fields, include_facet, '', [])
     except ParseError:
index 178080174310a799ea22fd4060938c5a15a25c3a..fcd9adc224712574a5829332727223c36b09de2b 100644 (file)
@@ -2,7 +2,8 @@
 import pytest
 
 from fake_fs import make_fake_fstools
-from teuthology.describe_tests import tree_with_info, extract_info
+from teuthology.describe_tests import (tree_with_info, extract_info,
+                                       get_combinations)
 from teuthology.exceptions import ParseError
 
 realistic_fs = {
@@ -105,7 +106,7 @@ expected_rbd_features = [
 class TestDescribeTests(object):
 
     def setup(self):
-        self.fake_listdir, _, self.fake_isdir, self.fake_open = \
+        self.fake_listdir, self.fake_isfile, self.fake_isdir, self.fake_open = \
             make_fake_fstools(realistic_fs)
 
     def test_no_filters(self):
@@ -183,6 +184,38 @@ class TestDescribeTests(object):
                                      expected_rbd_features,
                                      expected_desc))
 
+    def test_combinations_only_facets(self):
+        headers, rows = get_combinations('basic', [], None, 1, None, None, True,
+                                         self.fake_isdir, self.fake_open,
+                                         self.fake_isfile, self.fake_listdir)
+        assert headers == sorted(set(filter(bool, expected_facets)))
+        assert rows == [['install', 'fixed-1', 'rbd_api_tests']]
+
+    def test_combinations_desc_features(self):
+        headers, rows = get_combinations('basic', ['desc', 'rbd_features'],
+                                         None, 1, None, None, False,
+                                         self.fake_isdir, self.fake_open,
+                                         self.fake_isfile, self.fake_listdir)
+        assert headers == ['desc', 'rbd_features']
+        assert rows == [['install ceph\nsingle node cluster\nc/c++ librbd api tests with default settings',
+                         'default']]
+
+    def test_combinations_filter_in(self):
+        headers, rows = get_combinations('basic', [], None, 0, ['old_format'],
+                                         None, True,
+                                         self.fake_isdir, self.fake_open,
+                                         self.fake_isfile, self.fake_listdir)
+        assert headers == sorted(set(filter(bool, expected_facets)))
+        assert rows == [['install', 'fixed-1', 'rbd_api_tests_old_format']]
+
+    def test_combinations_filter_out(self):
+        headers, rows = get_combinations('basic', [], None, 0, None,
+                                         ['old_format'], True,
+                                         self.fake_isdir, self.fake_open,
+                                         self.fake_isfile, self.fake_listdir)
+        assert headers == sorted(set(filter(bool, expected_facets)))
+        assert rows == [['install', 'fixed-1', 'rbd_api_tests']]
+
 
 def test_extract_info_dir():
     simple_fs = {'a': {'b.yaml': 'description: [{foo: c}]'}}