//  [python]
def opTest(in_data,
           tol=DEFAULT_TOL,
           atol=DEFAULT_ATOL,
           do_grads=False,
           skip_theano=True,
           skip_tensorflow=False,
           verbose=False,
           input_shapes=None):
    # If using with non-tensor parameters, all tensor params must appear before
    # all non-tensor params
    # input_shapes should be None or an iterable containing tuples
    # defining the shape for each input.
    # Use this to define symbolic shapes (None).
    def run_one_backend(self, data, test_func, b, shapes=None, *run_args):
        tf_session = tensorflow.Session()
        tf.set_session(tf_session)
        results = []
        with tf_session.as_default():
            if shapes:
                x = [b.placeholder(shape=t) for t in shapes]
            else:
                x = [b.placeholder(shape=t.shape) for t in data if hasattr(t, 'shape')]
            xv = [b.variable(t, dtype=floatx()) for t in data if hasattr(t, 'shape')]
            ps = [t for t in data if not hasattr(t, 'shape')]
            grad_funcs = test_func(self, b, *(x + ps + list(run_args)))
            funcs = test_func(self, b, *(xv + ps + list(run_args)))
            tf_session.run(t.global_variables_initializer())
            for gf, f in zip(grad_funcs, funcs):
                fr = f.eval()
                if do_grads:
                    df = b.gradients(b.mean(gf), x)
                    gfn = b.function(x, df, updates=[])
                    gr = gfn([t for t in data if hasattr(t, 'shape')])
                    try:
                        values = gr[0].values
                        dense_shape = gr[0].dense_shape
                        indices = gr[0].indices
                        result = np.zeros(dense_shape)
                        for i in indices:
                            result[i] += values[i]
                        gr[0] = result
                    except AttributeError:
                        # This wasn't an IndexedSlices object, do nothing
                        pass
                if args.verbose > 2 or verbose:
                    print('backend: ', b)
                    print('data: {}'.format(data))
                    print('fr: {}'.format(fr))
                    if do_grads:
                        print('gr: {}'.format(gr))
                if do_grads:
                    results.append((fr, gr))
                else:
                    results.append((fr,))
        return results

    def apply(test_func):

        def output(self, *args):
            for didx, data in enumerate(in_data):
                shapes = None
                if input_shapes:
                    shapes = input_shapes[didx]
                if not skip_theano:
                    theano_results = run_one_backend(self,
                                                     data,
                                                     test_func,
                                                     th,
                                                     *args,
                                                     shapes=shapes)
                if not skip_tensorflow:
                    tensorflow_results = run_one_backend(self,
                                                         data,
                                                         test_func,
                                                         tf,
                                                         *args,
                                                         shapes=shapes)
                plaidml_results = run_one_backend(self, data, test_func, pkb, *args, shapes=shapes)
                if not skip_theano:
                    for idx, (pmlr, thr) in enumerate(zip(plaidml_results, theano_results)):
                        idx = idx + 1
                        npt.assert_allclose(
                            pmlr[0],
                            thr[0],
                            rtol=tol,
                            atol=atol,
                            err_msg='ERR: datum={}, test={}, x=plaidml, y=theano'.format(
                                didx, idx))
                        if do_grads:
                            for x in range(0, len(pmlr[1])):
                                npt.assert_allclose(
                                    pmlr[1][x],
                                    thr[1][x],
                                    rtol=tol,
                                    atol=atol,
                                    err_msg='ERR: datum={}, test={}, grad, x=plaidml, y=theano'.
                                    format(didx, idx))
                if not skip_tensorflow:
                    for idx, (pmlr, tfr) in enumerate(zip(plaidml_results, tensorflow_results)):
                        idx = idx + 1
                        npt.assert_allclose(
                            pmlr[0],
                            tfr[0],
                            rtol=tol,
                            atol=atol,
                            err_msg='ERR: datum={}, test={}, x=plaidml, y=tensorflow'.format(
                                didx, idx))
                        if do_grads:
                            for x in range(0, len(pmlr[1])):
                                npt.assert_allclose(
                                    pmlr[1][x],
                                    tfr[1][x],
                                    rtol=tol,
                                    atol=atol,
                                    err_msg='ERR: datum={}, test={}, grad, x=plaidml, y=tensorflow'
                                    .format(didx, idx))

        return output

    return apply