Esempio n. 1
0
    def test_logging(self):
        with tempfile.TemporaryDirectory() as tdir:
            fname = os.path.join(tdir, "dummy")
            dummy = generate_artificial_data(inmemory=True)
            ldum = len(dummy._log)
            save(dummy, filename=fname)

            # ensure saving is logged correctly
            assert len(dummy._log) > ldum
            assert dummy.filename in dummy._log
            assert dummy.filename + FILE_EXT["info"] in dummy._log
            assert dummy.cfg["method"] == "save"
            assert dummy.filename in dummy.cfg["files"]
            assert dummy.filename + FILE_EXT["info"] in dummy.cfg["files"]

            # ensure loading is logged correctly
            dummy2 = load(filename=fname + ".analog")
            assert len(dummy2._log) > len(dummy._log)
            assert dummy2.filename in dummy2._log
            assert dummy2.filename + FILE_EXT["info"] in dummy._log
            assert dummy2.cfg.cfg["method"] == "load"
            assert dummy2.filename in dummy2.cfg.cfg["files"]
            assert dummy2.filename + FILE_EXT["info"] in dummy2.cfg.cfg[
                "files"]

            # Delete all open references to file objects b4 closing tmp dir
            del dummy, dummy2
    def test_sequential_nonequidistant(self):
        for overlapping in [False, True]:
            nonequidata = generate_artificial_data(nTrials=self.nTrials,
                                                   nChannels=self.nChannels,
                                                   equidistant=False,
                                                   overlapping=overlapping,
                                                   inmemory=False)

            # unsorted, w/repetitions
            toi = self.seed.choice(nonequidata.time[0],
                                   int(nonequidata.time[0].size))
            self.artdataSelections[1]["toi"] = toi

            for select in self.artdataSelections:
                sel = Selector(nonequidata, select)
                out = filter_manager(nonequidata,
                                     self.b,
                                     self.a,
                                     select=select)

                # compare expected w/actual shape of computed data
                reference = 0
                for tk, trlno in enumerate(sel.trials):
                    reference += nonequidata.trials[trlno][
                        sel.time[tk]].shape[0]
                    # check for correct time selection
                    # FIXME: remove `if` below as soon as `time` prop for lists is fixed
                    if not isinstance(sel.time[0], list):
                        assert np.array_equal(
                            out.time[tk],
                            nonequidata.time[trlno][sel.time[tk]])

                assert out.data.shape[0] == reference
                assert np.array_equal(out.channel,
                                      nonequidata.channel[sel.channel])

                # ensure pre-selection is equivalent to in-place selection
                if select is None:
                    selected = nonequidata.selectdata()
                else:
                    selected = nonequidata.selectdata(**select)
                out_sel = filter_manager(selected, self.b, self.a)
                assert np.array_equal(out.data, out_sel.data)
                assert np.array_equal(out.channel, out_sel.channel)
                for tk in range(len(out.trials)):
                    assert np.array_equal(out.time[tk], out_sel.time[tk])
Esempio n. 3
0
class TestAnalogDataPlotting():

    nChannels = 16
    nTrials = 8
    seed = 130810

    # To use `selectdata` w/``trials = None``-raw plotting, the trials must not
    # overlap - construct separate set of `raw*` AnalogData-objects for testing!
    dataReg = generate_artificial_data(nTrials=nTrials,
                                       nChannels=nChannels,
                                       seed=seed,
                                       equidistant=True,
                                       overlapping=True)
    dataInv = generate_artificial_data(nTrials=nTrials,
                                       nChannels=nChannels,
                                       seed=seed,
                                       equidistant=True,
                                       overlapping=True,
                                       dimord=dataReg.dimord[::-1])
    rawReg = generate_artificial_data(nTrials=nTrials,
                                      nChannels=nChannels,
                                      seed=seed,
                                      equidistant=True,
                                      overlapping=False)
    rawInv = generate_artificial_data(nTrials=nTrials,
                                      nChannels=nChannels,
                                      seed=seed,
                                      equidistant=True,
                                      overlapping=False,
                                      dimord=rawReg.dimord[::-1])

    trials = ["all", [4, 3, 2, 2, 7]]
    channels = ["all", [14, 13, 12, 12, 15]]
    toilim = [None, [1.9, 2.5], [2.1, np.inf]]

    # trlToilim = lambda self, trl, tlim: None if trl is None else tlim

    def test_singlepanelplot(self):

        # Lowest possible dpi setting permitting valid png comparisons in `figs_equal`
        mpl.rcParams["figure.dpi"] = 150

        # Test everything except "raw" plotting
        for trials in self.trials:
            for channels in self.channels:
                for toilim in self.toilim:
                    for avg_channels in [True, False]:

                        # Render figure using singlepanelplot mechanics, recreate w/`selectdata`,
                        # results must be identical
                        fig1 = self.dataReg.singlepanelplot(
                            trials=trials,
                            channels=channels,
                            toilim=toilim,
                            avg_channels=avg_channels)
                        selected = self.dataReg.selectdata(trials=trials,
                                                           channels=channels,
                                                           toilim=toilim)
                        fig2 = selected.singlepanelplot(
                            trials="all", avg_channels=avg_channels)

                        # Recreate `fig1` and `fig2` in a single sweep by using
                        # `spy.singlepanelplot` w/multiple input objects
                        fig1a, fig2a = singlepanelplot(
                            self.dataReg,
                            self.dataInv,
                            trials=trials,
                            channels=channels,
                            toilim=toilim,
                            avg_channels=avg_channels,
                            overlay=False)

                        # `fig2a` is based on `dataInv` - be more lenient there
                        tol = None
                        if avg_channels:
                            tol = 1e-2
                        assert figs_equal(fig1, fig2)
                        assert figs_equal(fig1, fig1a)
                        assert figs_equal(fig2, fig2a, tol=tol)
                        assert figs_equal(fig1, fig2a, tol=tol)

                        # Create overlay figures: `fig3` combines `dataReg` and
                        # `dataInv` - must be identical to overlaying `fig1` w/`dataInv`
                        fig3 = singlepanelplot(self.dataReg,
                                               self.dataInv,
                                               trials=trials,
                                               channels=channels,
                                               toilim=toilim,
                                               avg_channels=avg_channels,
                                               overlay=True)
                        fig1 = singlepanelplot(self.dataInv,
                                               trials=trials,
                                               channels=channels,
                                               toilim=toilim,
                                               avg_channels=avg_channels,
                                               fig=fig1)
                        assert figs_equal(fig1, fig3)

                        # Close figures to avoid memory overflow
                        plt.close("all")

        # The `selectdata(trials="all")` part requires consecutive trials!
        for channels in self.channels:
            for avg_channels in [True, False]:
                fig1 = self.rawReg.singlepanelplot(trials=None,
                                                   channels=channels,
                                                   avg_channels=avg_channels)
                selected = self.rawReg.selectdata(trials="all",
                                                  channels=channels)
                fig2 = selected.singlepanelplot(trials=None,
                                                avg_channels=avg_channels)
                assert figs_equal(fig1, fig2)
                plt.close("all")

        # Do not allow selecting time-intervals w/o trial-specification
        with pytest.raises(SPYValueError):
            self.dataReg.singlepanelplot(trials=None, toilim=self.toilim[1])

        # Do not overlay multi-channel plot w/chan-average and unequal channel-count
        multiChannelFig = self.dataReg.singlepanelplot(
            avg_channels=False, channels=self.channels[1])
        with pytest.raises(SPYValueError):
            self.dataReg.singlepanelplot(avg_channels=True,
                                         fig=multiChannelFig)
        with pytest.raises(SPYValueError):
            self.dataReg.singlepanelplot(channels="all", fig=multiChannelFig)

        # Do not overlay multi-panel plot w/figure produced by `singlepanelplot`
        multiChannelFig.nTrialsPanels = 99
        with pytest.raises(SPYValueError):
            self.dataReg.singlepanelplot(fig=multiChannelFig)
        multiChannelFig.nChanPanels = 99
        with pytest.raises(SPYValueError):
            self.dataReg.singlepanelplot(fig=multiChannelFig)

        # Ensure grid and title specifications are rendered correctly
        theTitle = "A title"
        gridFig = self.dataReg.singlepanelplot(grid=True)
        assert gridFig.axes[0].get_xgridlines()[0].get_visible() == True
        titleFig = self.dataReg.singlepanelplot(title=theTitle)
        assert titleFig.axes[0].get_title() == theTitle

        plt.close("all")

    def test_multipanelplot(self):

        # Lowest possible dpi setting permitting valid png comparisons in `figs_equal`
        mpl.rcParams["figure.dpi"] = 75

        # Test everything except "raw" plotting
        for trials in self.trials:
            for channels in self.channels:
                for toilim in self.toilim:
                    for avg_trials in [True, False]:
                        for avg_channels in [True, False]:

                            # ``avg_trials == avg_channels == True`` yields `SPYWarning` to
                            # use `singlepanelplot` -> no figs to compare here
                            if avg_trials is avg_channels is True:
                                continue

                            # Render figure using multipanelplot mechanics, recreate w/`selectdata`,
                            # results must be identical
                            fig1 = self.dataReg.multipanelplot(
                                trials=trials,
                                channels=channels,
                                toilim=toilim,
                                avg_trials=avg_trials,
                                avg_channels=avg_channels)
                            selected = self.dataReg.selectdata(
                                trials=trials,
                                channels=channels,
                                toilim=toilim)
                            fig2 = selected.multipanelplot(
                                trials="all",
                                avg_trials=avg_trials,
                                avg_channels=avg_channels)

                            # Recreate `fig1` and `fig2` in a single sweep by using
                            # `spy.multipanelplot` w/multiple input objects
                            fig1a, fig2a = multipanelplot(
                                self.dataReg,
                                self.dataInv,
                                trials=trials,
                                channels=channels,
                                toilim=toilim,
                                avg_trials=avg_trials,
                                avg_channels=avg_channels,
                                overlay=False)

                            # `selectdata` preserves trial order but not numbering: ensure
                            # plot titles are correct, but then remove them to allow
                            # comparison of `selected` and `dataReg` figures
                            figTitleLists = []
                            if avg_trials is False:
                                if trials != "all":
                                    for fig in [fig1, fig1a]:
                                        titleList = []
                                        for ax in fig.axes:
                                            titleList.append(copy(ax.title))
                                            ax.set_title("")
                                        titles = [
                                            title.get_text()
                                            for title in titleList
                                        ]
                                        assert titles == [
                                            "Trial #{}".format(trlno)
                                            for trlno in trials
                                        ]
                                        figTitleLists.append(titleList)
                                    for fig in [fig2, fig2a]:
                                        for ax in fig.axes:
                                            ax.set_title("")

                            # After (potential) axes title removal, compare figures;
                            # `fig2a` is based on `dataInv` - be more lenient there
                            tol = None
                            if avg_channels:
                                tol = 1e-2
                            assert figs_equal(fig1, fig2)
                            assert figs_equal(fig1, fig1a)
                            assert figs_equal(fig2, fig2a, tol=tol)
                            assert figs_equal(fig1, fig2a, tol=tol)

                            # If necessary, restore axes title from `figTitleLists`
                            if figTitleLists:
                                for k, ax in enumerate(fig1.axes):
                                    ax.title = figTitleLists[0][k]

                            # Create overlay figures: `fig3` combines `dataReg` and
                            # `dataInv` - must be identical to overlaying `fig1` w/`dataInv`
                            fig3 = multipanelplot(self.dataReg,
                                                  self.dataInv,
                                                  trials=trials,
                                                  channels=channels,
                                                  toilim=toilim,
                                                  avg_trials=avg_trials,
                                                  avg_channels=avg_channels)
                            fig4 = multipanelplot(self.dataInv,
                                                  trials=trials,
                                                  channels=channels,
                                                  toilim=toilim,
                                                  avg_trials=avg_trials,
                                                  avg_channels=avg_channels,
                                                  fig=fig1)
                            assert figs_equal(fig3, fig4)

                            plt.close("all")

        # The `selectdata(trials="all")` part requires consecutive trials! Add'ly,
        # `avg_channels` must be `False`, otherwise single-panel plot warning is triggered
        for channels in self.channels:
            fig1 = self.rawReg.multipanelplot(trials=None,
                                              channels=channels,
                                              avg_channels=False,
                                              avg_trials=False)
            selected = self.rawReg.selectdata(trials="all", channels=channels)
            fig2 = selected.multipanelplot(trials=None, avg_channels=False)
            assert figs_equal(fig1, fig2)
            plt.close("all")

        # Do not allow selecting time-intervals w/o trial-specification
        with pytest.raises(SPYValueError):
            self.dataReg.multipanelplot(trials=None, toilim=self.toilim[1])

        # Panels = trials, each panel shows single (averaged) channel
        multiTrialSingleChanFig = self.dataReg.multipanelplot(
            trials=self.trials[1], avg_trials=False, avg_channels=True)
        with pytest.raises(SPYValueError):  # trial-count does not match up
            self.dataReg.multipanelplot(trials="all",
                                        avg_trials=False,
                                        avg_channels=True,
                                        fig=multiTrialSingleChanFig)
        with pytest.raises(SPYValueError):  # multi-channel overlay
            self.dataReg.multipanelplot(trials=self.trials[1],
                                        avg_trials=False,
                                        avg_channels=False,
                                        fig=multiTrialSingleChanFig)

        # Panels = trials, each panel shows multiple channels
        multiTrialMultiChanFig = self.dataReg.multipanelplot(
            trials=self.trials[1], avg_trials=False, avg_channels=False)
        with pytest.raises(SPYValueError):  # no trial specification provided
            self.dataReg.multipanelplot(trials=None,
                                        avg_trials=False,
                                        avg_channels=False,
                                        fig=multiTrialMultiChanFig)
        with pytest.raises(SPYValueError):  # channel-count does not match up
            self.dataReg.multipanelplot(trials=self.trials[1],
                                        channels=self.channels[1],
                                        avg_trials=False,
                                        avg_channels=False,
                                        fig=multiTrialMultiChanFig)
        with pytest.raises(SPYValueError):  # single-channel overlay
            self.dataReg.multipanelplot(trials=self.trials[1],
                                        avg_trials=False,
                                        avg_channels=True,
                                        fig=multiTrialMultiChanFig)

        # Panels = channels
        multiChannelFig = self.dataReg.multipanelplot(trials=self.trials[1],
                                                      avg_trials=True,
                                                      avg_channels=False)
        with pytest.raises(SPYValueError):  # multi-trial overlay
            self.dataReg.multipanelplot(trials=self.trials[1],
                                        avg_trials=False,
                                        avg_channels=False,
                                        fig=multiChannelFig)
        with pytest.raises(SPYValueError):  # channel-count does not match up
            self.dataReg.multipanelplot(trials=self.trials[1],
                                        channels=self.channels[1],
                                        avg_trials=True,
                                        avg_channels=False,
                                        fig=multiChannelFig)

        # Do not overlay single-panel plot w/figure produced by `multipanelplot`
        singlePanelFig = self.dataReg.singlepanelplot()
        with pytest.raises(SPYValueError):
            self.dataReg.multipanelplot(fig=singlePanelFig)

        # Ensure grid and title specifications are rendered correctly
        theTitle = "A title"
        gridFig = self.dataReg.multipanelplot(avg_trials=False,
                                              avg_channels=True,
                                              grid=True)
        assert all([
            gridFig.axes[k].get_xgridlines()[0].get_visible()
            for k in range(self.nTrials)
        ])
        titleFig = self.dataReg.multipanelplot(avg_trials=False,
                                               avg_channels=True,
                                               title=theTitle)
        assert titleFig._suptitle.get_text() == theTitle
Esempio n. 4
0
class TestSpyCalls():
    
    nChan = 13
    nObjs = nChan

    # Generate `nChan` objects whose channel-labeling scheme obeys: 
    # ob1.channel =  ["A", "B", "C", ..., "M"]
    # ob2.channel =  [     "B", "C", ..., "M", "N"] 
    # ob3.channel =  [          "C", ..., "M", "N", "O"]
    # ...
    # ob13.channel = [                    "M", "N", "O", ..., "Z"]
    # Thus, channel no. 13 ("M") is common across all objects
    dataObjs = []
    for n in range(nObjs):
        obj = generate_artificial_data(nChannels=nChan, inmemory=False)
        obj.channel = list(string.ascii_uppercase[n : nChan + n])
        dataObjs.append(obj)
    data = dataObjs[0]
        
    def test_validcallstyles(self):
        
        # data positional
        fname, = group_objects(self.data)
        assert fname == self.data.filename
        
        # data as keyword
        fname, = group_objects(data=self.data)
        assert fname == self.data.filename
        
        # data in cfg
        cfg = StructDict()
        cfg.data = self.data
        fname, = group_objects(cfg)
        assert fname == self.data.filename
        
        # 1. data positional, 2. cfg positional
        cfg = StructDict()
        cfg.groupbychan = None
        fname, = group_objects(self.data, cfg)
        assert fname == self.data.filename

        # 1. cfg positional, 2. data positional
        fname, = group_objects(cfg, self.data)
        assert fname == self.data.filename
        
        # data positional, cfg as keyword
        fname, = group_objects(self.data, cfg=cfg)
        assert fname == self.data.filename
        
        # cfg positional, data as keyword
        fname, = group_objects(cfg, data=self.data)
        assert fname == self.data.filename
    
        # both keywords
        fname, = group_objects(cfg=cfg, data=self.data)
        assert fname == self.data.filename
                
    def test_invalidcallstyles(self):
        
        # expected error messages
        errmsg1 = "expected Syncopy data object(s) provided either via " +\
                 "`cfg`/keyword or positional arguments, not both"
        errmsg2 = "expected Syncopy data object(s) provided either via `cfg` " +\
            "or as keyword argument, not both"
        errmsg3 = "expected either 'data' or 'dataset' in `cfg`/keywords, not both"
        
        # ensure things break reliably for 'data' as well as 'dataset'
        for key in ["data", "dataset"]:
        
            # data + cfg w/data
            cfg = StructDict()
            cfg[key] = self.data
            with pytest.raises(SPYValueError) as exc:
                group_objects(self.data, cfg)
            assert errmsg1 in str(exc.value)
            
            # data as positional + kwarg
            with pytest.raises(SPYValueError) as exc:
                group_objects(self.data, data=self.data)
            assert errmsg1 in str(exc.value)
            with pytest.raises(SPYValueError) as exc:
                group_objects(self.data, dataset=self.data)
            assert errmsg1 in str(exc.value)

            # cfg w/data + kwarg + positional
            with pytest.raises(SPYValueError) as exc:
                group_objects(self.data, cfg, data=self.data)
            assert errmsg1 in str(exc.value)
            with pytest.raises(SPYValueError) as exc:
                group_objects(self.data, cfg, dataset=self.data)
            assert errmsg1 in str(exc.value)
                
            # cfg w/data + kwarg
            with pytest.raises(SPYValueError) as exc:
                group_objects(cfg, data=self.data)
            assert errmsg2 in str(exc.value)
            with pytest.raises(SPYValueError) as exc:
                group_objects(cfg, dataset=self.data)
            assert errmsg2 in str(exc.value)

        # cfg (no data) but double-whammied
        cfg = StructDict()
        cfg.groupbychan = None
        with pytest.raises(SPYValueError)as exc:
            group_objects(self.data, cfg, cfg=cfg)
        assert "expected `cfg` either as positional or keyword argument, not both" in str(exc.value)
        
        # keyword set via cfg and kwarg
        with pytest.raises(SPYValueError) as exc:
            group_objects(self.data, cfg, groupbychan="invalid")
        assert "'non-default value for groupbychan'; expected no keyword arguments" in str(exc.value)
        
        # both data and dataset in cfg/keywords
        cfg = StructDict()
        cfg.data = self.data
        cfg.dataset = self.data
        with pytest.raises(SPYValueError)as exc:
            group_objects(cfg)
        assert errmsg3 in str(exc.value)
        with pytest.raises(SPYValueError)as exc:
            group_objects(data=self.data, dataset=self.data)
        assert errmsg3 in str(exc.value)

        # data/dataset do not contain Syncopy object
        with pytest.raises(SPYError)as exc:
            group_objects(data="invalid")
        assert "`data` must be Syncopy data object(s)!" in str(exc.value)
        
        # cfg is not dict/StructDict
        with pytest.raises(SPYTypeError)as exc:
            group_objects(cfg="invalid")
        assert "Wrong type of cfg: expected dictionary-like" in str(exc.value)

        # no data input whatsoever        
        with pytest.raises(SPYError)as exc:
            group_objects("invalid")
        assert "missing mandatory argument: `data`" in str(exc.value)
        
    def test_varargin(self):
        
        # data positional
        allFnames = group_objects(*self.dataObjs)
        assert allFnames == [obj.filename for obj in self.dataObjs]

        # data in cfg
        cfg = StructDict()
        cfg.data = self.dataObjs
        fnameList = group_objects(cfg)
        assert allFnames == fnameList
        
        # group objects by single-letter "channels" in various ways
        for letter in ["L", "E", "I", "A"]:
            letterIdx = string.ascii_uppercase.index(letter)
            nOccurences = letterIdx + 1

            # data positional + keyword to get "reference"
            groupList = group_objects(*self.dataObjs, groupbychan=letter)
            assert len(groupList) == nOccurences
            
            # 1. data positional, 2. cfg positional
            cfg = StructDict()
            cfg.groupbychan = letter
            fnameList = group_objects(*self.dataObjs, cfg)
            assert groupList == fnameList

            # 1. cfg positional, 2. data positional
            fnameList = group_objects(cfg, *self.dataObjs)
            assert groupList == fnameList
            
            # data positional, cfg as keyword
            fnameList = group_objects(*self.dataObjs, cfg=cfg)
            assert groupList == fnameList
            
            # cfg w/data + keyword
            cfg = StructDict()
            cfg.dataset = self.dataObjs
            cfg.groupbychan = letter
            fnameList = group_objects(cfg)
            assert groupList == fnameList

            # data positional + select keyword
            fnameList = group_objects(*self.dataObjs[:letterIdx + 1], 
                                       select={"channels": [letter]})
            assert groupList == fnameList

            # data positional + cfg w/select
            cfg = StructDict()
            cfg.select = {"channels": [letter]}
            fnameList = group_objects(*self.dataObjs[:letterIdx + 1], cfg)
            assert groupList == fnameList

            # cfg w/data + select
            cfg = StructDict()
            cfg.data = self.dataObjs[:letterIdx + 1]
            cfg.select = {"channels": [letter]}
            fnameList = group_objects(cfg)
            assert groupList == fnameList
            
        # invalid selection
        with pytest.raises(SPYValueError) as exc:
            group_objects(*self.dataObjs, select={"channels": ["Z"]})
        assert "expected list/array of channel existing names or indices" in str(exc.value)

        # data does not only contain Syncopy objects        
        cfg = StructDict()
        cfg.data = self.dataObjs + ["invalid"]
        with pytest.raises(SPYError)as exc:
            group_objects(cfg)
        assert "`data` must be Syncopy data object(s)!" in str(exc.value)
Esempio n. 5
0
    def test_object_padding(self):

        # construct AnalogData object w/trials of unequal lengths
        adata = generate_artificial_data(nTrials=7,
                                         nChannels=16,
                                         equidistant=False,
                                         inmemory=False)
        timeAxis = adata.dimord.index("time")

        # test dictionary generation for `create_new = False`: ensure all trials
        # have padded length of `total_time` seconds (1 sample tolerance)
        total_time = 30
        pad_list = padding(adata,
                           "zero",
                           pad="absolute",
                           padlength=total_time,
                           unit="time",
                           create_new=False)
        for tk, trl in enumerate(adata.trials):
            assert "pad_width" in pad_list[tk].keys()
            assert "constant_values" in pad_list[tk].keys()
            trl_time = (pad_list[tk]["pad_width"][timeAxis, :].sum() +
                        trl.shape[timeAxis]) / adata.samplerate
            assert trl_time - total_time < 1 / adata.samplerate

        # jumble axes of `AnalogData` object and compute max. trial length
        adata2 = generate_artificial_data(nTrials=7,
                                          nChannels=16,
                                          equidistant=False,
                                          inmemory=False,
                                          dimord=adata.dimord[::-1])
        timeAxis2 = adata2.dimord.index("time")
        maxtrllen = 0
        for trl in adata2.trials:
            maxtrllen = max(maxtrllen, trl.shape[timeAxis2])

        # symmetric `maxlen` padding: 1 sample tolerance
        pad_list2 = padding(adata2, "zero", pad="maxlen", create_new=False)
        for tk, trl in enumerate(adata2.trials):
            trl_len = pad_list2[tk]["pad_width"][
                timeAxis2, :].sum() + trl.shape[timeAxis2]
            assert (trl_len - maxtrllen) <= 1
        pad_list2 = padding(adata2,
                            "zero",
                            pad="maxlen",
                            prepadlength=True,
                            postpadlength=True,
                            create_new=False)
        for tk, trl in enumerate(adata2.trials):
            trl_len = pad_list2[tk]["pad_width"][
                timeAxis2, :].sum() + trl.shape[timeAxis2]
            assert (trl_len - maxtrllen) <= 1

        # pre- and post- `maxlen` padding: no tolerance
        pad_list2 = padding(adata2,
                            "zero",
                            pad="maxlen",
                            prepadlength=True,
                            create_new=False)
        for tk, trl in enumerate(adata2.trials):
            trl_len = pad_list2[tk]["pad_width"][
                timeAxis2, :].sum() + trl.shape[timeAxis2]
            assert trl_len == maxtrllen
        pad_list2 = padding(adata2,
                            "zero",
                            pad="maxlen",
                            postpadlength=True,
                            create_new=False)
        for tk, trl in enumerate(adata2.trials):
            trl_len = pad_list2[tk]["pad_width"][
                timeAxis2, :].sum() + trl.shape[timeAxis2]
            assert trl_len == maxtrllen

        # `maxlen'-specific errors: `padlength` wrong type, wrong combo with `prepadlength`
        with pytest.raises(SPYTypeError):
            padding(adata,
                    "zero",
                    pad="maxlen",
                    padlength=self.ns,
                    create_new=False)
        with pytest.raises(SPYTypeError):
            padding(adata,
                    "zero",
                    pad="maxlen",
                    prepadlength=self.ns,
                    create_new=False)
        with pytest.raises(SPYTypeError):
            padding(adata,
                    "zero",
                    pad="maxlen",
                    padlength=self.ns,
                    prepadlength=True,
                    create_new=False)
    def test_parallel_nonequidistant(self, testcluster):
        client = dd.Client(testcluster)
        for overlapping in [False, True]:
            nonequidata = generate_artificial_data(nTrials=self.nTrials,
                                                   nChannels=self.nChannels,
                                                   equidistant=False,
                                                   overlapping=overlapping,
                                                   inmemory=False)

            # unsorted, w/repetitions
            toi = self.seed.choice(nonequidata.time[0],
                                   int(nonequidata.time[0].size))
            self.artdataSelections[1]["toi"] = toi

            for parallel_store in [True, False]:
                for chan_per_worker in [None, self.chanPerWrkr]:
                    for select in self.artdataSelections:
                        # FIXME: remove as soon as channel-parallelization works w/channel selectors
                        if chan_per_worker is not None:
                            select = None
                        sel = Selector(nonequidata, select)
                        out = filter_manager(nonequidata,
                                             self.b,
                                             self.a,
                                             select=select,
                                             chan_per_worker=chan_per_worker,
                                             parallel=True,
                                             parallel_store=parallel_store)

                        # compare expected w/actual shape of computed data
                        reference = 0
                        for tk, trlno in enumerate(sel.trials):
                            reference += nonequidata.trials[trlno][
                                sel.time[tk]].shape[0]
                            # check for correct time selection
                            # FIXME: remove `if` below as soon as `time` prop for lists is fixed
                            if not isinstance(sel.time[0], list):
                                assert np.array_equal(
                                    out.time[tk],
                                    nonequidata.time[trlno][sel.time[tk]])
                        assert out.data.shape[0] == reference
                        assert np.array_equal(out.channel,
                                              nonequidata.channel[sel.channel])
                        assert out.data.is_virtual == parallel_store

                        if parallel_store:
                            nfiles = len(
                                glob(
                                    os.path.join(
                                        os.path.splitext(out.filename)[0],
                                        "*.h5")))
                            if chan_per_worker is None:
                                assert nfiles == len(sel.trials)
                            else:
                                assert nfiles == len(sel.trials) * (
                                    int(out.channel.size / chan_per_worker) +
                                    int(out.channel.size % chan_per_worker > 0)
                                )

                        # ensure pre-selection is equivalent to in-place selection
                        if select is None:
                            selected = nonequidata.selectdata()
                        else:
                            selected = nonequidata.selectdata(**select)
                        out_sel = filter_manager(
                            selected,
                            self.b,
                            self.a,
                            chan_per_worker=chan_per_worker,
                            parallel=True,
                            parallel_store=parallel_store)
                        assert np.allclose(out.data, out_sel.data)
                        assert np.array_equal(out.channel, out_sel.channel)
                        for tk in range(len(out.trials)):
                            assert np.array_equal(out.time[tk],
                                                  out_sel.time[tk])

        client.close()