1# -*- coding: utf-8 -*-
2
3########################################################################
4#
5# License: BSD
6# Created: 2006-10-19
7# Author: Ivan Vilata i Balaguer - ivan@selidor.net
8#
9# $Id$
10#
11########################################################################
12
13"""Test module for queries on datasets."""
14
15import re
16import sys
17import types
18import warnings
19import functools
20
21import numpy
22
23import tables
24from tables.utils import SizeType
25from tables.tests import common
26from tables.tests.common import unittest
27from tables.tests.common import verbosePrint as vprint
28from tables.tests.common import PyTablesTestCase as TestCase
29
30
31from numpy import (log10, exp, log, abs, sqrt, sin, cos, tan,
32                   arcsin, arccos, arctan)
33
34
35# Data parameters
36# ---------------
37row_period = 50
38"""Maximum number of unique rows before they start cycling."""
39md_shape = (2, 2)
40"""Shape of multidimensional fields."""
41
42_maxnvalue = row_period + numpy.prod(md_shape, dtype=SizeType) - 1
43_strlen = int(numpy.log10(_maxnvalue-1)) + 1
44
45str_format = '%%0%dd' % _strlen
46"""Format of string values."""
47
48small_blocksizes = (300, 60, 20, 5)
49# small_blocksizes = (512, 128, 32, 4)   # for manual testing only
50"""Sensible parameters for indexing with small blocksizes."""
51
52
53# Type information
54# ----------------
55type_info = {
56    'bool': (numpy.bool_, bool),
57    'int8': (numpy.int8, int),
58    'uint8': (numpy.uint8, int),
59    'int16': (numpy.int16, int),
60    'uint16': (numpy.uint16, int),
61    'int32': (numpy.int32, int),
62    'uint32': (numpy.uint32, int),
63    'int64': (numpy.int64, int),
64    'uint64': (numpy.uint64, int),
65    'float32': (numpy.float32, float),
66    'float64': (numpy.float64, float),
67    'complex64': (numpy.complex64, complex),
68    'complex128': (numpy.complex128, complex),
69    'time32': (numpy.int32, int),
70    'time64': (numpy.float64, float),
71    'enum': (numpy.uint8, int),  # just for these tests
72    'string': ('S%s' % _strlen, numpy.string_),  # just for these tests
73}
74"""NumPy and Numexpr type for each PyTables type that will be tested."""
75
76# globals dict for eval()
77func_info = {'log10': log10, 'log': log, 'exp': exp,
78             'abs': abs, 'sqrt': sqrt,
79             'sin': sin, 'cos': cos, 'tan': tan,
80             'arcsin': arcsin, 'arccos': arccos, 'arctan': arctan}
81"""functions and NumPy.ufunc() for each function that will be tested."""
82
83
84if hasattr(numpy, 'float16'):
85    type_info['float16'] = (numpy.float16, float)
86# if hasattr(numpy, 'float96'):
87#    type_info['float96'] = (numpy.float96, float)
88# if hasattr(numpy, 'float128'):
89#    type_info['float128'] = (numpy.float128, float)
90# if hasattr(numpy, 'complex192'):
91#    type_info['complex192'] = (numpy.complex192, complex)
92# if hasattr(numpy, 'complex256'):
93#    type_info['complex256'] = (numpy.complex256, complex)
94
95sctype_from_type = dict((type_, info[0])
96                        for (type_, info) in type_info.items())
97"""Maps PyTables types to NumPy scalar types."""
98nxtype_from_type = dict((type_, info[1])
99                        for (type_, info) in type_info.items())
100"""Maps PyTables types to Numexpr types."""
101
102heavy_types = frozenset(['uint8', 'int16', 'uint16', 'float32', 'complex64'])
103"""PyTables types to be tested only in heavy mode."""
104
105enum = tables.Enum(dict(('n%d' % i, i) for i in range(_maxnvalue)))
106"""Enumerated type to be used in tests."""
107
108
109# Table description
110# -----------------
111def append_columns(classdict, shape=()):
112    """Append a ``Col`` of each PyTables data type to the `classdict`.
113
114    A column of a certain TYPE gets called ``c_TYPE``.  The number of
115    added columns is returned.
116
117    """
118    heavy = common.heavy
119    for (itype, type_) in enumerate(sorted(type_info.keys())):
120        if not heavy and type_ in heavy_types:
121            continue  # skip heavy type in non-heavy mode
122        colpos = itype + 1
123        colname = 'c_%s' % type_
124        if type_ == 'enum':
125            base = tables.Atom.from_sctype(sctype_from_type[type_])
126            col = tables.EnumCol(enum, enum(0), base, shape=shape, pos=colpos)
127        else:
128            sctype = sctype_from_type[type_]
129            dtype = numpy.dtype((sctype, shape))
130            col = tables.Col.from_dtype(dtype, pos=colpos)
131        classdict[colname] = col
132    ncols = colpos
133    return ncols
134
135
136def nested_description(classname, pos, shape=()):
137    """Return a nested column description with all PyTables data types.
138
139    A column of a certain TYPE gets called ``c_TYPE``.  The nested
140    column will be placed in the position indicated by `pos`.
141
142    """
143    classdict = {}
144    append_columns(classdict, shape=shape)
145    classdict['_v_pos'] = pos
146    return type(classname, (tables.IsDescription,), classdict)
147
148
149def table_description(classname, nclassname, shape=()):
150    """Return a table description for testing queries.
151
152    The description consists of all PyTables data types, both in the
153    top level and in the ``c_nested`` nested column.  A column of a
154    certain TYPE gets called ``c_TYPE``.  An extra integer column
155    ``c_extra`` is also provided.  If a `shape` is given, it will be
156    used for all columns.  Finally, an extra indexed column
157    ``c_idxextra`` is added as well in order to provide some basic
158    tests for multi-index queries.
159
160    """
161    classdict = {}
162    colpos = append_columns(classdict, shape)
163
164    ndescr = nested_description(nclassname, colpos, shape=shape)
165    classdict['c_nested'] = ndescr
166    colpos += 1
167
168    extracol = tables.IntCol(shape=shape, pos=colpos)
169    classdict['c_extra'] = extracol
170    colpos += 1
171
172    idxextracol = tables.IntCol(shape=shape, pos=colpos)
173    classdict['c_idxextra'] = idxextracol
174    colpos += 1
175
176    return type(classname, (tables.IsDescription,), classdict)
177
178TableDescription = table_description(
179    'TableDescription', 'NestedDescription')
180"""Unidimensional table description for testing queries."""
181
182MDTableDescription = table_description(
183    'MDTableDescription', 'MDNestedDescription', shape=md_shape)
184"""Multidimensional table description for testing queries."""
185
186
187# Table data
188# ----------
189table_data = {}
190"""Cached table data for a given shape and number of rows."""
191# Data is cached because computing it row by row is quite slow.  Hop!
192
193
194def fill_table(table, shape, nrows):
195    """Fill the given `table` with `nrows` rows of data.
196
197    Values in the i-th row (where 0 <= i < `row_period`) for a
198    multidimensional field with M elements span from i to i + M-1.  For
199    subsequent rows, values repeat cyclically.
200
201    The same goes for the ``c_extra`` column, but values range from
202    -`row_period`/2 to +`row_period`/2.
203
204    """
205    # Reuse already computed data if possible.
206    tdata = table_data.get((shape, nrows))
207    if tdata is not None:
208        table.append(tdata)
209        table.flush()
210        return
211
212    heavy = common.heavy
213    size = int(numpy.prod(shape, dtype=SizeType))
214
215    row, value = table.row, 0
216    for nrow in range(nrows):
217        data = numpy.arange(value, value + size).reshape(shape)
218        for (type_, sctype) in sctype_from_type.items():
219            if not heavy and type_ in heavy_types:
220                continue  # skip heavy type in non-heavy mode
221            colname = 'c_%s' % type_
222            ncolname = 'c_nested/%s' % colname
223            if type_ == 'bool':
224                coldata = data > (row_period // 2)
225            elif type_ == 'string':
226                sdata = [str_format % x for x in range(value, value + size)]
227                coldata = numpy.array(sdata, dtype=sctype).reshape(shape)
228            else:
229                coldata = numpy.asarray(data, dtype=sctype)
230            row[ncolname] = row[colname] = coldata
231            row['c_extra'] = data - (row_period // 2)
232            row['c_idxextra'] = data - (row_period // 2)
233        row.append()
234        value += 1
235        if value == row_period:
236            value = 0
237    table.flush()
238
239    # Make computed data reusable.
240    tdata = table.read()
241    table_data[(shape, nrows)] = tdata
242
243
244class SilentlySkipTest(unittest.SkipTest):
245    pass
246
247
248# Base test cases
249# ---------------
250class BaseTableQueryTestCase(common.TempFileMixin, TestCase):
251    """Base test case for querying tables.
252
253    Sub-classes must define the following attributes:
254
255    ``tableDescription``
256        The description of the table to be created.
257    ``shape``
258        The shape of data fields in the table.
259    ``nrows``
260        The number of data rows to be generated for the table.
261
262    Sub-classes may redefine the following attributes:
263
264    ``indexed``
265        Whether columns shall be indexed, if possible.  Default is not
266        to index them.
267    ``optlevel``
268        The level of optimisation of column indexes.  Default is 0.
269
270    """
271
272    indexed = False
273    optlevel = 0
274
275    colNotIndexable_re = re.compile(r"\bcan not be indexed\b")
276    condNotBoolean_re = re.compile(r"\bdoes not have a boolean type\b")
277
278    def create_indexes(self, colname, ncolname, extracolname):
279        if not self.indexed:
280            return
281        try:
282            kind = self.kind
283            vprint("* Indexing ``%s`` columns. Type: %s." % (colname, kind))
284            for acolname in [colname, ncolname, extracolname]:
285                acolumn = self.table.colinstances[acolname]
286                acolumn.create_index(
287                    kind=self.kind, optlevel=self.optlevel,
288                    _blocksizes=small_blocksizes, _testmode=True)
289
290        except TypeError as te:
291            if self.colNotIndexable_re.search(str(te)):
292                raise SilentlySkipTest(
293                    "Columns of this type can not be indexed.")
294            raise
295        except NotImplementedError:
296            raise SilentlySkipTest(
297                "Indexing columns of this type is not supported yet.")
298
299    def setUp(self):
300        super(BaseTableQueryTestCase, self).setUp()
301        self.table = self.h5file.create_table(
302            '/', 'test', self.tableDescription, expectedrows=self.nrows)
303        fill_table(self.table, self.shape, self.nrows)
304
305
306class ScalarTableMixin:
307    tableDescription = TableDescription
308    shape = ()
309
310
311class MDTableMixin:
312    tableDescription = MDTableDescription
313    shape = md_shape
314
315
316# Test cases on query data
317# ------------------------
318operators = [
319    None, '~',
320    '<', '<=', '==', '!=', '>=', '>',
321    ('<', '<='), ('>', '>=')]
322"""Comparison operators to check with different types."""
323heavy_operators = frozenset(['~', '<=', '>=', '>', ('>', '>=')])
324"""Comparison operators to be tested only in heavy mode."""
325left_bound = row_period // 4
326"""Operand of left side operator in comparisons with operator pairs."""
327right_bound = row_period * 3 // 4
328"""Operand of right side operator in comparisons with operator pairs."""
329func_bound = 0.8  # must be <1 for trig functions to be able to fail
330"""Operand of right side operator in comparisons with functions. """
331extra_conditions = [
332    '',                     # uses one index
333    '& ((c_extra + 1) < 0)',  # uses one index
334    '| (c_idxextra > 0)',   # uses two indexes
335    '| ((c_idxextra > 0) | ((c_extra + 1) > 0))',  # can't use indexes
336]
337"""Extra conditions to append to comparison conditions."""
338
339
340class TableDataTestCase(BaseTableQueryTestCase):
341    """Base test case for querying table data.
342
343    Automatically created test method names have the format
344    ``test_XNNNN``, where ``NNNN`` is the zero-padded test number and
345    ``X`` indicates whether the test belongs to the light (``l``) or
346    heavy (``h``) set.
347
348    """
349    _testfmt_light = 'test_l%04d'
350    _testfmt_heavy = 'test_h%04d'
351
352
353def create_test_method(type_, op, extracond, func=None):
354    sctype = sctype_from_type[type_]
355
356    # Compute the value of bounds.
357    condvars = {'bound': right_bound,
358                'lbound': left_bound,
359                'rbound': right_bound,
360                'func_bound': func_bound}
361    for (bname, bvalue) in condvars.items():
362        if type_ == 'string':
363            bvalue = str_format % bvalue
364        bvalue = nxtype_from_type[type_](bvalue)
365        condvars[bname] = bvalue
366
367    # Compute the name of columns.
368    colname = 'c_%s' % type_
369    ncolname = 'c_nested/%s' % colname
370
371    # Compute the query condition.
372    if not op:  # as is
373        cond = colname
374    elif op == '~':  # unary
375        cond = '~(%s)' % colname
376    elif op == '<' and func is None:  # binary variable-constant
377        cond = '%s %s %s' % (colname, op, repr(condvars['bound']))
378    elif isinstance(op, tuple):  # double binary variable-constant
379        cond = ('(lbound %s %s) & (%s %s rbound)'
380                % (op[0], colname, colname, op[1]))
381    elif func is not None:
382        cond = '%s(%s) %s func_bound' % (func, colname, op)
383    else:  # function or binary variable-variable
384        cond = '%s %s bound' % (colname, op)
385    if extracond:
386        cond = '(%s) %s' % (cond, extracond)
387
388    def ignore_skipped(oldmethod):
389        @functools.wraps(oldmethod)
390        def newmethod(self, *args, **kwargs):
391            self._verboseHeader()
392            try:
393                return oldmethod(self, *args, **kwargs)
394            except SilentlySkipTest as se:
395                if se.args:
396                    msg = se.args[0]
397                else:
398                    msg = "<skipped>"
399                common.verbosePrint("\nSkipped test: %s" % msg)
400            finally:
401                common.verbosePrint('')  # separator line between tests
402        return newmethod
403
404    @ignore_skipped
405    def test_method(self):
406        vprint("* Condition is ``%s``." % cond)
407        # Replace bitwise operators with their logical counterparts.
408        pycond = cond
409        for (ptop, pyop) in [('&', 'and'), ('|', 'or'), ('~', 'not')]:
410            pycond = pycond.replace(ptop, pyop)
411        pycond = compile(pycond, '<string>', 'eval')
412
413        table = self.table
414        self.create_indexes(colname, ncolname, 'c_idxextra')
415
416        table_slice = dict(start=1, stop=table.nrows - 5, step=3)
417        rownos, fvalues = None, None
418        # Test that both simple and nested columns work as expected.
419        # Knowing how the table is filled, results must be the same.
420        for acolname in [colname, ncolname]:
421            # First the reference Python version.
422            pyrownos, pyfvalues, pyvars = [], [], condvars.copy()
423            for row in table.iterrows(**table_slice):
424                pyvars[colname] = row[acolname]
425                pyvars['c_extra'] = row['c_extra']
426                pyvars['c_idxextra'] = row['c_idxextra']
427                try:
428                    with warnings.catch_warnings():
429                        warnings.filterwarnings(
430                            'ignore',
431                            'invalid value encountered in arc(cos|sin)',
432                            RuntimeWarning)
433                        isvalidrow = eval(pycond, func_info, pyvars)
434                except TypeError:
435                    raise SilentlySkipTest(
436                        "The Python type does not support the operation.")
437                if isvalidrow:
438                    pyrownos.append(row.nrow)
439                    pyfvalues.append(row[acolname])
440            pyrownos = numpy.array(pyrownos)  # row numbers already sorted
441            pyfvalues = numpy.array(pyfvalues, dtype=sctype)
442            pyfvalues.sort()
443            vprint("* %d rows selected by Python from ``%s``."
444                   % (len(pyrownos), acolname))
445            if rownos is None:
446                rownos = pyrownos  # initialise reference results
447                fvalues = pyfvalues
448            else:
449                self.assertTrue(numpy.all(pyrownos == rownos))  # check
450                self.assertTrue(numpy.all(pyfvalues == fvalues))
451
452            # Then the in-kernel or indexed version.
453            ptvars = condvars.copy()
454            ptvars[colname] = table.colinstances[acolname]
455            ptvars['c_extra'] = table.colinstances['c_extra']
456            ptvars['c_idxextra'] = table.colinstances['c_idxextra']
457            try:
458                isidxq = table.will_query_use_indexing(cond, ptvars)
459                # Query twice to trigger possible query result caching.
460                ptrownos = [table.get_where_list(cond, condvars, sort=True,
461                                                 **table_slice)
462                            for _ in range(2)]
463                ptfvalues = [
464                    table.read_where(cond, condvars, field=acolname,
465                                     **table_slice)
466                    for _ in range(2)
467                ]
468            except TypeError as te:
469                if self.condNotBoolean_re.search(str(te)):
470                    raise SilentlySkipTest("The condition is not boolean.")
471                raise
472            except NotImplementedError:
473                raise SilentlySkipTest(
474                    "The PyTables type does not support the operation.")
475            for ptfvals in ptfvalues:  # row numbers already sorted
476                ptfvals.sort()
477            vprint("* %d rows selected by PyTables from ``%s``"
478                   % (len(ptrownos[0]), acolname), nonl=True)
479            vprint("(indexing: %s)." % ["no", "yes"][bool(isidxq)])
480            self.assertTrue(numpy.all(ptrownos[0] == rownos))
481            self.assertTrue(numpy.all(ptfvalues[0] == fvalues))
482            # The following test possible caching of query results.
483            self.assertTrue(numpy.all(ptrownos[0] == ptrownos[1]))
484            self.assertTrue(numpy.all(ptfvalues[0] == ptfvalues[1]))
485
486    test_method.__doc__ = "Testing ``%s``." % cond
487    return test_method
488
489
490def add_test_method(type_, op, extracond='', func=None):
491    global testn
492    # Decide to which set the test belongs.
493    heavy = type_ in heavy_types or op in heavy_operators
494    if heavy:
495        testfmt = TableDataTestCase._testfmt_heavy
496        numfmt = ' [#H%d]'
497    else:
498        testfmt = TableDataTestCase._testfmt_light
499        numfmt = ' [#L%d]'
500    tmethod = create_test_method(type_, op, extracond, func)
501    # The test number is appended to the docstring to help
502    # identify failing methods in non-verbose mode.
503    tmethod.__name__ = testfmt % testn
504    # tmethod.__doc__ += numfmt % testn
505    tmethod.__doc__ += testfmt % testn
506    setattr(TableDataTestCase, tmethod.__name__, tmethod)
507    testn += 1
508
509# Create individual tests.  You may restrict which tests are generated
510# by replacing the sequences in the ``for`` statements.  For instance:
511testn = 0
512for type_ in type_info:  # for type_ in ['string']:
513    for op in operators:  # for op in ['!=']:
514        for extracond in extra_conditions:  # for extracond in ['']:
515            add_test_method(type_, op, extracond)
516
517for type_ in ['float32', 'float64']:
518    for func in func_info:  # i for func in ['log10']:
519        for op in operators:
520            add_test_method(type_, op, func=func)
521
522# Base classes for non-indexed queries.
523NX_BLOCK_SIZE1 = 128  # from ``interpreter.c`` in Numexpr
524NX_BLOCK_SIZE2 = 8  # from ``interpreter.c`` in Numexpr
525
526
527class SmallNITableMixin:
528    nrows = row_period * 2
529    assert NX_BLOCK_SIZE2 < nrows < NX_BLOCK_SIZE1
530    assert nrows % NX_BLOCK_SIZE2 != 0  # to have some residual rows
531
532
533class BigNITableMixin:
534    nrows = row_period * 3
535    assert nrows > NX_BLOCK_SIZE1 + NX_BLOCK_SIZE2
536    assert nrows % NX_BLOCK_SIZE1 != 0
537    assert nrows % NX_BLOCK_SIZE2 != 0  # to have some residual rows
538
539# Parameters for non-indexed queries.
540table_sizes = ['Small', 'Big']
541heavy_table_sizes = frozenset(['Big'])
542table_ndims = ['Scalar']  # to enable multidimensional testing, include 'MD'
543
544# Non-indexed queries: ``[SB][SM]TDTestCase``, where:
545#
546# 1. S is for small and B is for big size table.
547#    Sizes are listed in `table_sizes`.
548# 2. S is for scalar and M for multidimensional columns.
549#    Dimensionalities are listed in `table_ndims`.
550
551
552def niclassdata():
553    for size in table_sizes:
554        heavy = size in heavy_table_sizes
555        for ndim in table_ndims:
556            classname = '%s%sTDTestCase' % (size[0], ndim[0])
557            cbasenames = ('%sNITableMixin' % size, '%sTableMixin' % ndim,
558                          'TableDataTestCase')
559            classdict = dict(heavy=heavy)
560            yield (classname, cbasenames, classdict)
561
562
563# Base classes for the different type index.
564class UltraLightITableMixin:
565    kind = "ultralight"
566
567
568class LightITableMixin:
569    kind = "light"
570
571
572class MediumITableMixin:
573    kind = "medium"
574
575
576class FullITableMixin:
577    kind = "full"
578
579# Base classes for indexed queries.
580
581
582class SmallSTableMixin:
583    nrows = 50
584
585
586class MediumSTableMixin:
587    nrows = 100
588
589
590class BigSTableMixin:
591    nrows = 500
592
593# Parameters for indexed queries.
594ckinds = ['UltraLight', 'Light', 'Medium', 'Full']
595itable_sizes = ['Small', 'Medium', 'Big']
596heavy_itable_sizes = frozenset(['Medium', 'Big'])
597itable_optvalues = [0, 1, 3, 7, 9]
598heavy_itable_optvalues = frozenset([0, 1, 7, 9])
599
600# Indexed queries: ``[SMB]I[ulmf]O[01379]TDTestCase``, where:
601#
602# 1. S is for small, M for medium and B for big size table.
603#    Sizes are listed in `itable_sizes`.
604# 2. U is for 'ultraLight', L for 'light', M for 'medium', F for 'Full' indexes
605#    Index types are listed in `ckinds`.
606# 3. 0 to 9 is the desired index optimization level.
607#    Optimizations are listed in `itable_optvalues`.
608
609
610def iclassdata():
611    for ckind in ckinds:
612        for size in itable_sizes:
613            for optlevel in itable_optvalues:
614                heavy = (optlevel in heavy_itable_optvalues
615                         or size in heavy_itable_sizes)
616                classname = '%sI%sO%dTDTestCase' % (
617                    size[0], ckind[0], optlevel)
618                cbasenames = ('%sSTableMixin' % size,
619                              '%sITableMixin' % ckind,
620                              'ScalarTableMixin',
621                              'TableDataTestCase')
622                classdict = dict(heavy=heavy, optlevel=optlevel, indexed=True)
623                yield (classname, cbasenames, classdict)
624
625
626# Create test classes.
627for cdatafunc in [niclassdata, iclassdata]:
628    for (cname, cbasenames, cdict) in cdatafunc():
629        cbases = tuple(eval(cbase) for cbase in cbasenames)
630        class_ = type(cname, cbases, cdict)
631        exec('%s = class_' % cname)
632
633
634# Test cases on query usage
635# -------------------------
636class BaseTableUsageTestCase(BaseTableQueryTestCase):
637    nrows = row_period
638
639_gvar = None
640"""Use this when a global variable is needed."""
641
642
643class ScalarTableUsageTestCase(ScalarTableMixin, BaseTableUsageTestCase):
644    """Test case for query usage on scalar tables.
645
646    This also tests for most usage errors and situations.
647
648    """
649
650    def test_empty_condition(self):
651        """Using an empty condition."""
652
653        self.assertRaises(SyntaxError, self.table.where, '')
654
655    def test_syntax_error(self):
656        """Using a condition with a syntax error."""
657
658        self.assertRaises(SyntaxError, self.table.where, 'foo bar')
659
660    def test_unsupported_object(self):
661        """Using a condition with an unsupported object."""
662
663        self.assertRaises(TypeError, self.table.where, '[]')
664        self.assertRaises(TypeError, self.table.where, 'obj', {'obj': {}})
665        self.assertRaises(TypeError, self.table.where, 'c_bool < []')
666
667    def test_unsupported_syntax(self):
668        """Using a condition with unsupported syntax."""
669
670        self.assertRaises(TypeError, self.table.where, 'c_bool[0]')
671        self.assertRaises(TypeError, self.table.where, 'c_bool()')
672        self.assertRaises(NameError, self.table.where, 'c_bool.__init__')
673
674    def test_no_column(self):
675        """Using a condition with no participating columns."""
676
677        self.assertRaises(ValueError, self.table.where, 'True')
678
679    def test_foreign_column(self):
680        """Using a condition with a column from other table."""
681
682        table2 = self.h5file.create_table('/', 'other', self.tableDescription)
683        self.assertRaises(ValueError, self.table.where,
684                          'c_int32_a + c_int32_b > 0',
685                          {'c_int32_a': self.table.cols.c_int32,
686                           'c_int32_b': table2.cols.c_int32})
687
688    def test_unsupported_op(self):
689        """Using a condition with unsupported operations on types."""
690
691        NIE = NotImplementedError
692        self.assertRaises(NIE, self.table.where, 'c_complex128 > 0j')
693        self.assertRaises(NIE, self.table.where, 'c_string + b"a" > b"abc"')
694
695    def test_not_boolean(self):
696        """Using a non-boolean condition."""
697
698        self.assertRaises(TypeError, self.table.where, 'c_int32')
699
700    def test_nested_col(self):
701        """Using a condition with nested columns."""
702
703        self.assertRaises(TypeError, self.table.where, 'c_nested')
704
705    def test_implicit_col(self):
706        """Using implicit column names in conditions."""
707
708        # If implicit columns didn't work, a ``NameError`` would be raised.
709        self.assertRaises(TypeError, self.table.where, 'c_int32')
710        # If overriding didn't work, no exception would be raised.
711        self.assertRaises(TypeError, self.table.where,
712                          'c_bool', {'c_bool': self.table.cols.c_int32})
713        # External variables do not override implicit columns.
714
715        def where_with_locals():
716            c_int32 = self.table.cols.c_bool  # this wouldn't cause an error
717            self.assertIsNotNone(c_int32)
718            self.table.where('c_int32')
719        self.assertRaises(TypeError, where_with_locals)
720
721    def test_condition_vars(self):
722        """Using condition variables in conditions."""
723
724        # If condition variables didn't work, a ``NameError`` would be raised.
725        self.assertRaises(NotImplementedError, self.table.where,
726                          'c_string > bound', {'bound': 0})
727
728        def where_with_locals():
729            bound = 'foo'  # this wouldn't cause an error
730            # silence pyflakes warnings
731            self.assertIsInstance(bound, str)
732            self.table.where('c_string > bound', {'bound': 0})
733        self.assertRaises(NotImplementedError, where_with_locals)
734
735        def where_with_globals():
736            global _gvar
737            _gvar = 'foo'  # this wouldn't cause an error
738            # silence pyflakes warnings
739            self.assertIsInstance(_gvar, str)
740            try:
741                self.table.where('c_string > _gvar', {'_gvar': 0})
742            finally:
743                del _gvar  # to keep global namespace clean
744        self.assertRaises(NotImplementedError, where_with_globals)
745
746    def test_scopes(self):
747        """Looking up different scopes for variables."""
748
749        # Make sure the variable is not implicit.
750        self.assertRaises(NameError, self.table.where, 'col')
751
752        # First scope: dictionary of condition variables.
753        self.assertRaises(TypeError, self.table.where,
754                          'col', {'col': self.table.cols.c_int32})
755
756        # Second scope: local variables.
757        def where_whith_locals():
758            col = self.table.cols.c_int32
759            self.assertIsNotNone(col)
760            self.table.where('col')
761        self.assertRaises(TypeError, where_whith_locals)
762
763        # Third scope: global variables.
764        def where_with_globals():
765            global _gvar
766            _gvar = self.table.cols.c_int32
767            # silence pyflakes warnings
768            self.assertIsNotNone(_gvar)
769            try:
770                self.table.where('_gvar')
771            finally:
772                del _gvar  # to keep global namespace clean
773
774        self.assertRaises(TypeError, where_with_globals)
775
776
777class MDTableUsageTestCase(MDTableMixin, BaseTableUsageTestCase):
778    """Test case for query usage on multidimensional tables."""
779
780    def test(self):
781        """Using a condition on a multidimensional table."""
782
783        # Easy: queries on multidimensional tables are not implemented yet!
784        self.assertRaises(NotImplementedError, self.table.where, 'c_bool')
785
786
787class IndexedTableUsage(ScalarTableMixin, BaseTableUsageTestCase):
788    """Test case for query usage on indexed tables.
789
790    Indexing could be used in more cases, but it is expected to kick in
791    at least in the cases tested here.
792
793    """
794    nrows = 50
795    indexed = True
796
797    def setUp(self):
798        super(IndexedTableUsage, self).setUp()
799        self.table.cols.c_bool.create_index(_blocksizes=small_blocksizes)
800        self.table.cols.c_int32.create_index(_blocksizes=small_blocksizes)
801        self.will_query_use_indexing = self.table.will_query_use_indexing
802        self.compileCondition = self.table._compile_condition
803        self.requiredExprVars = self.table._required_expr_vars
804        usable_idxs = set()
805        for expr in self.idx_expr:
806            idxvar = expr[0]
807            if idxvar not in usable_idxs:
808                usable_idxs.add(idxvar)
809        self.usable_idxs = frozenset(usable_idxs)
810
811    def test(self):
812        for condition in self.conditions:
813            c_usable_idxs = self.will_query_use_indexing(condition, {})
814            self.assertEqual(c_usable_idxs, self.usable_idxs,
815                             "\nQuery with condition: ``%s``\n"
816                             "Computed usable indexes are: ``%s``\n"
817                             "and should be: ``%s``" %
818                            (condition, c_usable_idxs, self.usable_idxs))
819            condvars = self.requiredExprVars(condition, None)
820            compiled = self.compileCondition(condition, condvars)
821            c_idx_expr = compiled.index_expressions
822            self.assertEqual(c_idx_expr, self.idx_expr,
823                             "\nWrong index expression in condition:\n``%s``\n"
824                             "Compiled index expression is:\n``%s``\n"
825                             "and should be:\n``%s``" %
826                            (condition, c_idx_expr, self.idx_expr))
827            c_str_expr = compiled.string_expression
828            self.assertEqual(c_str_expr, self.str_expr,
829                             "\nWrong index operations in condition:\n``%s``\n"
830                             "Computed index operations are:\n``%s``\n"
831                             "and should be:\n``%s``" %
832                            (condition, c_str_expr, self.str_expr))
833            vprint("* Query with condition ``%s`` will use "
834                   "variables ``%s`` for indexing."
835                   % (condition, compiled.index_variables))
836
837
838class IndexedTableUsage1(IndexedTableUsage):
839    conditions = [
840        '(c_int32 > 0)',
841        '(c_int32 > 0) & (c_extra > 0)',
842        '(c_int32 > 0) & ((~c_bool) | (c_extra > 0))',
843        '(c_int32 > 0) & ((c_extra < 3) & (c_extra > 0))',
844    ]
845    idx_expr = [('c_int32', ('gt',), (0,))]
846    str_expr = 'e0'
847
848
849class IndexedTableUsage2(IndexedTableUsage):
850    conditions = [
851        '(c_int32 > 0) & (c_int32 < 5)',
852        '(c_int32 > 0) & (c_int32 < 5) & (c_extra > 0)',
853        '(c_int32 > 0) & (c_int32 < 5) & ((c_bool == True) | (c_extra > 0))',
854        '(c_int32 > 0) & (c_int32 < 5) & ((c_extra > 0) | (c_bool == True))',
855    ]
856    idx_expr = [('c_int32', ('gt', 'lt'), (0, 5))]
857    str_expr = 'e0'
858
859
860class IndexedTableUsage3(IndexedTableUsage):
861    conditions = [
862        '(c_bool == True)',
863        '(c_bool == True) & (c_extra > 0)',
864        '(c_extra > 0) & (c_bool == True)',
865        '((c_extra > 0) & (c_extra < 4)) & (c_bool == True)',
866        '(c_bool == True) & ((c_extra > 0) & (c_extra < 4))',
867    ]
868    idx_expr = [('c_bool', ('eq',), (True,))]
869    str_expr = 'e0'
870
871
872class IndexedTableUsage4(IndexedTableUsage):
873    conditions = [
874        '((c_int32 > 0) & (c_bool == True)) & (c_extra > 0)',
875        '((c_int32 > 0) & (c_bool == True)) & ((c_extra > 0)' +
876        ' & (c_extra < 4))',
877    ]
878    idx_expr = [('c_int32', ('gt',), (0,)),
879                ('c_bool', ('eq',), (True,)),
880                ]
881    str_expr = '(e0 & e1)'
882
883
884class IndexedTableUsage5(IndexedTableUsage):
885    conditions = [
886        '(c_int32 >= 1) & (c_int32 < 2) & (c_bool == True)',
887        '(c_int32 >= 1) & (c_int32 < 2) & (c_bool == True)' +
888        ' & (c_extra > 0)',
889    ]
890    idx_expr = [('c_int32', ('ge', 'lt'), (1, 2)),
891                ('c_bool', ('eq',), (True,)),
892                ]
893    str_expr = '(e0 & e1)'
894
895
896class IndexedTableUsage6(IndexedTableUsage):
897    conditions = [
898        '(c_int32 >= 1) & (c_int32 < 2) & (c_int32 > 0) & (c_int32 < 5)',
899        '(c_int32 >= 1) & (c_int32 < 2) & (c_int32 > 0) & (c_int32 < 5)' +
900        ' & (c_extra > 0)',
901    ]
902    idx_expr = [('c_int32', ('ge', 'lt'), (1, 2)),
903                ('c_int32', ('gt',), (0,)),
904                ('c_int32', ('lt',), (5,)),
905                ]
906    str_expr = '((e0 & e1) & e2)'
907
908
909class IndexedTableUsage7(IndexedTableUsage):
910    conditions = [
911        '(c_int32 >= 1) & (c_int32 < 2) & ((c_int32 > 0) & (c_int32 < 5))',
912        '((c_int32 >= 1) & (c_int32 < 2)) & ((c_int32 > 0) & (c_int32 < 5))',
913        '((c_int32 >= 1) & (c_int32 < 2)) & ((c_int32 > 0) & (c_int32 < 5))' +
914        ' & (c_extra > 0)',
915    ]
916    idx_expr = [('c_int32', ('ge', 'lt'), (1, 2)),
917                ('c_int32', ('gt', 'lt'), (0, 5)),
918                ]
919    str_expr = '(e0 & e1)'
920
921
922class IndexedTableUsage8(IndexedTableUsage):
923    conditions = [
924        '(c_extra > 0) & ((c_int32 > 0) & (c_int32 < 5))',
925    ]
926    idx_expr = [('c_int32', ('gt', 'lt'), (0, 5)),
927                ]
928    str_expr = 'e0'
929
930
931class IndexedTableUsage9(IndexedTableUsage):
932    conditions = [
933        '(c_extra > 0) & (c_int32 > 0) & (c_int32 < 5)',
934        '((c_extra > 0) & (c_int32 > 0)) & (c_int32 < 5)',
935        '(c_extra > 0) & (c_int32 > 0) & (c_int32 < 5) & (c_extra > 3)',
936    ]
937    idx_expr = [('c_int32', ('gt',), (0,)),
938                ('c_int32', ('lt',), (5,))]
939    str_expr = '(e0 & e1)'
940
941
942class IndexedTableUsage10(IndexedTableUsage):
943    conditions = [
944        '(c_int32 < 5) & (c_extra > 0) & (c_bool == True)',
945        '(c_int32 < 5) & (c_extra > 2) & c_bool',
946        '(c_int32 < 5) & (c_bool == True) & (c_extra > 0) & (c_extra < 4)',
947        '(c_int32 < 5) & (c_extra > 0) & (c_bool == True) & (c_extra < 4)',
948    ]
949    idx_expr = [('c_int32', ('lt',), (5,)),
950                ('c_bool', ('eq',), (True,))]
951    str_expr = '(e0 & e1)'
952
953
954class IndexedTableUsage11(IndexedTableUsage):
955    """Complex operations are not eligible for indexing."""
956
957    conditions = [
958        'sin(c_int32) > 0',
959        '(c_int32 * 2.4) > 0',
960        '(c_int32 + c_int32) > 0',
961        'c_int32**2 > 0',
962    ]
963    idx_expr = []
964    str_expr = ''
965
966
967class IndexedTableUsage12(IndexedTableUsage):
968    conditions = [
969        '~c_bool',
970        '~(c_bool)',
971        '~c_bool & (c_extra > 0)',
972        '~(c_bool) & (c_extra > 0)',
973    ]
974    idx_expr = [('c_bool', ('eq',), (False,))]
975    str_expr = 'e0'
976
977
978class IndexedTableUsage13(IndexedTableUsage):
979    conditions = [
980        '~(c_bool == True)',
981        '~((c_bool == True))',
982        '~(c_bool == True) & (c_extra > 0)',
983        '~((c_bool == True)) & (c_extra > 0)',
984    ]
985    idx_expr = [('c_bool', ('eq',), (False,))]
986    str_expr = 'e0'
987
988
989class IndexedTableUsage14(IndexedTableUsage):
990    conditions = [
991        '~(c_int32 > 0)',
992        '~((c_int32 > 0)) & (c_extra > 0)',
993        '~(c_int32 > 0) & ((~c_bool) | (c_extra > 0))',
994        '~(c_int32 > 0) & ((c_extra < 3) & (c_extra > 0))',
995    ]
996    idx_expr = [('c_int32', ('le',), (0,))]
997    str_expr = 'e0'
998
999
1000class IndexedTableUsage15(IndexedTableUsage):
1001    conditions = [
1002        '(~(c_int32 > 0) | ~c_bool)',
1003        '(~(c_int32 > 0) | ~(c_bool)) & (c_extra > 0)',
1004        '(~(c_int32 > 0) | ~(c_bool == True)) & ((c_extra > 0)' +
1005        ' & (c_extra < 4))',
1006    ]
1007    idx_expr = [('c_int32', ('le',), (0,)),
1008                ('c_bool', ('eq',), (False,)),
1009                ]
1010    str_expr = '(e0 | e1)'
1011
1012
1013class IndexedTableUsage16(IndexedTableUsage):
1014    conditions = [
1015        '(~(c_int32 > 0) & ~(c_int32 < 2))',
1016        '(~(c_int32 > 0) & ~(c_int32 < 2)) & (c_extra > 0)',
1017        '(~(c_int32 > 0) & ~(c_int32 < 2)) & ((c_extra > 0)' +
1018        ' & (c_extra < 4))',
1019    ]
1020    idx_expr = [('c_int32', ('le',), (0,)),
1021                ('c_int32', ('ge',), (2,)),
1022                ]
1023    str_expr = '(e0 & e1)'
1024
1025
1026class IndexedTableUsage17(IndexedTableUsage):
1027    conditions = [
1028        '(~(c_int32 > 0) & ~(c_int32 < 2))',
1029        '(~(c_int32 > 0) & ~(c_int32 < 2)) & (c_extra > 0)',
1030        '(~(c_int32 > 0) & ~(c_int32 < 2)) & ((c_extra > 0)' +
1031        ' & (c_extra < 4))',
1032    ]
1033    idx_expr = [('c_int32', ('le',), (0,)),
1034                ('c_int32', ('ge',), (2,)),
1035                ]
1036    str_expr = '(e0 & e1)'
1037
1038# Negations of complex conditions are not supported yet
1039
1040
1041class IndexedTableUsage18(IndexedTableUsage):
1042    conditions = [
1043        '~((c_int32 > 0) & (c_bool))',
1044        '~((c_int32 > 0) & (c_bool)) & (c_extra > 0)',
1045        '~((c_int32 > 0) & (c_bool)) & ((c_extra > 0)' +
1046        ' & (c_extra < 4))',
1047    ]
1048    idx_expr = []
1049    str_expr = ''
1050
1051
1052class IndexedTableUsage19(IndexedTableUsage):
1053    conditions = [
1054        '~((c_int32 > 0) & (c_bool)) & ((c_bool == False)' +
1055        ' & (c_extra < 4))',
1056    ]
1057    idx_expr = [('c_bool', ('eq',), (False,)),
1058                ]
1059    str_expr = 'e0'
1060
1061
1062class IndexedTableUsage20(IndexedTableUsage):
1063    conditions = [
1064        '((c_int32 > 0) & ~(c_bool))',
1065        '((c_int32 > 0) & ~(c_bool)) & (c_extra > 0)',
1066        '((c_int32 > 0) & ~(c_bool == True)) & ((c_extra > 0) & (c_extra < 4))'
1067    ]
1068    idx_expr = [
1069        ('c_int32', ('gt',), (0,)),
1070        ('c_bool', ('eq',), (False,)),
1071    ]
1072    str_expr = '(e0 & e1)'
1073
1074
1075class IndexedTableUsage21(IndexedTableUsage):
1076    conditions = [
1077        '(~(c_int32 > 0) & (c_bool))',
1078        '(~(c_int32 > 0) & (c_bool)) & (c_extra > 0)',
1079        '(~(c_int32 > 0) & (c_bool == True)) & ((c_extra > 0)' +
1080        ' & (c_extra < 4))',
1081    ]
1082    idx_expr = [('c_int32', ('le',), (0,)),
1083                ('c_bool', ('eq',), (True,)),
1084                ]
1085    str_expr = '(e0 & e1)'
1086
1087
1088class IndexedTableUsage22(IndexedTableUsage):
1089    conditions = [
1090        '~((c_int32 >= 1) & (c_int32 < 2)) & ~(c_bool == True)',
1091        '~(c_bool == True) & (c_extra > 0)',
1092        '~((c_int32 >= 1) & (c_int32 < 2)) & (~(c_bool == True)' +
1093        ' & (c_extra > 0))',
1094    ]
1095    idx_expr = [('c_bool', ('eq',), (False,)),
1096                ]
1097    str_expr = 'e0'
1098
1099
1100class IndexedTableUsage23(IndexedTableUsage):
1101    conditions = [
1102        'c_int32 != 1',
1103        'c_bool != False',
1104        '~(c_int32 != 1)',
1105        '~(c_bool != False)',
1106        '(c_int32 != 1) & (c_extra != 2)',
1107    ]
1108    idx_expr = []
1109    str_expr = ''
1110
1111
1112class IndexedTableUsage24(IndexedTableUsage):
1113    conditions = [
1114        'c_bool',
1115        'c_bool == True',
1116        'True == c_bool',
1117        '~(~c_bool)',
1118        '~~c_bool',
1119        '~~~~c_bool',
1120        '~(~c_bool) & (c_extra != 2)',
1121    ]
1122    idx_expr = [('c_bool', ('eq',), (True,)),
1123                ]
1124    str_expr = 'e0'
1125
1126
1127class IndexedTableUsage25(IndexedTableUsage):
1128    conditions = [
1129        '~c_bool',
1130        'c_bool == False',
1131        'False == c_bool',
1132        '~(c_bool)',
1133        '~((c_bool))',
1134        '~~~c_bool',
1135        '~~(~c_bool) & (c_extra != 2)',
1136    ]
1137    idx_expr = [
1138        ('c_bool', ('eq',), (False,)),
1139    ]
1140    str_expr = 'e0'
1141
1142
1143class IndexedTableUsage26(IndexedTableUsage):
1144    conditions = [
1145        'c_bool != True',
1146        'True != c_bool',
1147        'c_bool != False',
1148        'False != c_bool',
1149        ]
1150    idx_expr = []
1151    str_expr = ''
1152
1153
1154class IndexedTableUsage27(IndexedTableUsage):
1155    conditions = [
1156        '(c_int32 == 3) | c_bool | (c_int32 == 5)',
1157        '(((c_int32 == 3) | (c_bool == True)) | (c_int32 == 5))' +
1158        ' & (c_extra > 0)',
1159        ]
1160    idx_expr = [
1161        ('c_int32', ('eq',), (3,)),
1162        ('c_bool', ('eq',), (True,)),
1163        ('c_int32', ('eq',), (5,)),
1164    ]
1165    str_expr = '((e0 | e1) | e2)'
1166
1167
1168class IndexedTableUsage28(IndexedTableUsage):
1169    conditions = [
1170        '((c_int32 == 3) | c_bool) & (c_int32 == 5)',
1171        '(((c_int32 == 3) | (c_bool == True)) & (c_int32 == 5))' +
1172        ' & (c_extra > 0)',
1173        ]
1174    idx_expr = [
1175        ('c_int32', ('eq',), (3,)),
1176        ('c_bool', ('eq',), (True,)),
1177        ('c_int32', ('eq',), (5,)),
1178    ]
1179    str_expr = '((e0 | e1) & e2)'
1180
1181
1182class IndexedTableUsage29(IndexedTableUsage):
1183    conditions = [
1184        '(c_int32 == 3) | ((c_int32 == 4) & (c_int32 == 5))',
1185        '((c_int32 == 3) | ((c_int32 == 4) & (c_int32 == 5)))' +
1186        ' & (c_extra > 0)',
1187        ]
1188    idx_expr = [
1189        ('c_int32', ('eq',), (4,)),
1190        ('c_int32', ('eq',), (5,)),
1191        ('c_int32', ('eq',), (3,)),
1192    ]
1193    str_expr = '((e0 & e1) | e2)'
1194
1195
1196class IndexedTableUsage30(IndexedTableUsage):
1197    conditions = [
1198        '((c_int32 == 3) | (c_int32 == 4)) & (c_int32 == 5)',
1199        '((c_int32 == 3) | (c_int32 == 4)) & (c_int32 == 5)' +
1200        ' & (c_extra > 0)',
1201        ]
1202    idx_expr = [
1203        ('c_int32', ('eq',), (3,)),
1204        ('c_int32', ('eq',), (4,)),
1205        ('c_int32', ('eq',), (5,)),
1206    ]
1207    str_expr = '((e0 | e1) & e2)'
1208
1209
1210class IndexedTableUsage31(IndexedTableUsage):
1211    conditions = [
1212        '(c_extra > 0) & ((c_extra < 4) & (c_bool == True))',
1213        '(c_extra > 0) & ((c_bool == True) & (c_extra < 5))',
1214        '((c_int32 > 0) | (c_extra > 0)) & (c_bool == True)',
1215        ]
1216    idx_expr = [
1217        ('c_bool', ('eq',), (True,)),
1218    ]
1219    str_expr = 'e0'
1220
1221
1222class IndexedTableUsage32(IndexedTableUsage):
1223    conditions = [
1224        '(c_int32 < 5) & (c_extra > 0) & (c_bool == True) | (c_extra < 4)',
1225        ]
1226    idx_expr = []
1227    str_expr = ''
1228
1229
1230# Main part
1231# ---------
1232def suite():
1233    """Return a test suite consisting of all the test cases in the module."""
1234
1235    testSuite = unittest.TestSuite()
1236
1237    cdatafuncs = [niclassdata]  # non-indexing data tests
1238    cdatafuncs.append(iclassdata)  # indexing data tests
1239
1240    heavy = common.heavy
1241    # Choose which tests to run in classes with autogenerated tests.
1242    if heavy:
1243        autoprefix = 'test'  # all tests
1244    else:
1245        autoprefix = 'test_l'  # only light tests
1246
1247    niter = 1
1248    for i in range(niter):
1249        # Tests on query data.
1250        for cdatafunc in cdatafuncs:
1251            for cdata in cdatafunc():
1252                class_ = eval(cdata[0])
1253                if heavy or not class_.heavy:
1254                    suite_ = unittest.makeSuite(class_, prefix=autoprefix)
1255                    testSuite.addTest(suite_)
1256        # Tests on query usage.
1257        testSuite.addTest(unittest.makeSuite(ScalarTableUsageTestCase))
1258        testSuite.addTest(unittest.makeSuite(MDTableUsageTestCase))
1259        testSuite.addTest(unittest.makeSuite(IndexedTableUsage1))
1260        testSuite.addTest(unittest.makeSuite(IndexedTableUsage2))
1261        testSuite.addTest(unittest.makeSuite(IndexedTableUsage3))
1262        testSuite.addTest(unittest.makeSuite(IndexedTableUsage4))
1263        testSuite.addTest(unittest.makeSuite(IndexedTableUsage5))
1264        testSuite.addTest(unittest.makeSuite(IndexedTableUsage6))
1265        testSuite.addTest(unittest.makeSuite(IndexedTableUsage7))
1266        testSuite.addTest(unittest.makeSuite(IndexedTableUsage8))
1267        testSuite.addTest(unittest.makeSuite(IndexedTableUsage9))
1268        testSuite.addTest(unittest.makeSuite(IndexedTableUsage10))
1269        testSuite.addTest(unittest.makeSuite(IndexedTableUsage11))
1270        testSuite.addTest(unittest.makeSuite(IndexedTableUsage12))
1271        testSuite.addTest(unittest.makeSuite(IndexedTableUsage13))
1272        testSuite.addTest(unittest.makeSuite(IndexedTableUsage14))
1273        testSuite.addTest(unittest.makeSuite(IndexedTableUsage15))
1274        testSuite.addTest(unittest.makeSuite(IndexedTableUsage16))
1275        testSuite.addTest(unittest.makeSuite(IndexedTableUsage17))
1276        testSuite.addTest(unittest.makeSuite(IndexedTableUsage18))
1277        testSuite.addTest(unittest.makeSuite(IndexedTableUsage19))
1278        testSuite.addTest(unittest.makeSuite(IndexedTableUsage20))
1279        testSuite.addTest(unittest.makeSuite(IndexedTableUsage21))
1280        testSuite.addTest(unittest.makeSuite(IndexedTableUsage22))
1281        testSuite.addTest(unittest.makeSuite(IndexedTableUsage23))
1282        testSuite.addTest(unittest.makeSuite(IndexedTableUsage24))
1283        testSuite.addTest(unittest.makeSuite(IndexedTableUsage25))
1284        testSuite.addTest(unittest.makeSuite(IndexedTableUsage26))
1285        testSuite.addTest(unittest.makeSuite(IndexedTableUsage27))
1286        testSuite.addTest(unittest.makeSuite(IndexedTableUsage28))
1287        testSuite.addTest(unittest.makeSuite(IndexedTableUsage29))
1288        testSuite.addTest(unittest.makeSuite(IndexedTableUsage30))
1289        testSuite.addTest(unittest.makeSuite(IndexedTableUsage31))
1290        testSuite.addTest(unittest.makeSuite(IndexedTableUsage32))
1291
1292    return testSuite
1293
1294
1295if __name__ == '__main__':
1296    common.parse_argv(sys.argv)
1297    common.print_versions()
1298    unittest.main(defaultTest='suite')
1299