[med-svn] [Git][med-team/q2-cutadapt][master] 13 commits: routine-update: New upstream version

Steffen Möller gitlab at salsa.debian.org
Tue Dec 1 23:38:25 GMT 2020



Steffen Möller pushed to branch master at Debian Med / q2-cutadapt


Commits:
34d351a3 by Steffen Moeller at 2020-06-10T16:20:31+02:00
routine-update: New upstream version

- - - - -
e90e9700 by Steffen Moeller at 2020-06-10T16:20:31+02:00
New upstream version 2020.2.0
- - - - -
86832932 by Steffen Moeller at 2020-06-10T16:20:35+02:00
Update upstream source from tag 'upstream/2020.2.0'

Update to upstream version '2020.2.0'
with Debian dir 84db90a41cf320170608eec375deb6e6d09fc50e
- - - - -
b908d131 by Steffen Moeller at 2020-06-10T16:20:35+02:00
routine-update: Standards-Version: 4.5.0

- - - - -
80542e5e by Steffen Moeller at 2020-06-10T16:20:35+02:00
routine-update: debhelper-compat 13

- - - - -
f3c4fde4 by Steffen Moeller at 2020-06-10T16:20:37+02:00
routine-update: Add salsa-ci file

- - - - -
66076685 by Steffen Moeller at 2020-06-10T16:20:38+02:00
routine-update: Rules-Requires-Root: no

- - - - -
53e357e5 by Steffen Moeller at 2020-12-02T00:09:41+01:00
Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, Repository-Browse.

Fixes: lintian: upstream-metadata-missing-bug-tracking
See-also: https://lintian.debian.org/tags/upstream-metadata-missing-bug-tracking.html

Fixes: lintian: upstream-metadata-missing-repository
See-also: https://lintian.debian.org/tags/upstream-metadata-missing-repository.html

- - - - -
4eb7c8ad by Steffen Moeller at 2020-12-02T00:09:57+01:00
routine-update: New upstream version

- - - - -
34f17f14 by Steffen Moeller at 2020-12-02T00:09:58+01:00
New upstream version 2020.11.0
- - - - -
5d662236 by Steffen Moeller at 2020-12-02T00:10:01+01:00
Update upstream source from tag 'upstream/2020.11.0'

Update to upstream version '2020.11.0'
with Debian dir 47bf0049603f1c279f96263b70486ae1ac9d2078
- - - - -
b722faa0 by Steffen Moeller at 2020-12-02T00:10:01+01:00
routine-update: Standards-Version: 4.5.1

- - - - -
16dc7b27 by Steffen Moeller at 2020-12-02T00:36:59+01:00
New upstream version.

- - - - -


19 changed files:

- LICENSE
- ci/recipe/meta.yaml
- debian/changelog
- debian/control
- debian/copyright
- debian/rules
- + debian/salsa-ci.yml
- debian/upstream/metadata
- q2_cutadapt/__init__.py
- q2_cutadapt/_demux.py
- q2_cutadapt/_trim.py
- q2_cutadapt/_version.py
- q2_cutadapt/plugin_setup.py
- q2_cutadapt/tests/__init__.py
- + q2_cutadapt/tests/data/mixed-orientation/forward.fastq.gz
- + q2_cutadapt/tests/data/mixed-orientation/reverse.fastq.gz
- q2_cutadapt/tests/test_demux.py
- q2_cutadapt/tests/test_trim.py
- setup.py


Changes:

=====================================
LICENSE
=====================================
@@ -1,6 +1,6 @@
 BSD 3-Clause License
 
-Copyright (c) 2017-2019, QIIME 2 development team.
+Copyright (c) 2017-2020, QIIME 2 development team.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without


=====================================
ci/recipe/meta.yaml
=====================================
@@ -19,7 +19,7 @@ requirements:
 
   run:
     - python {{ python }}
-    - cutadapt
+    - cutadapt >=3
     - pigz
     - pandas
     - numpy


=====================================
debian/changelog
=====================================
@@ -1,3 +1,18 @@
+q2-cutadapt (2020.11.0-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream version
+  * Standards-Version: 4.5.1 (routine-update)
+  * debhelper-compat 13 (routine-update)
+  * Add salsa-ci file (routine-update)
+  * Rules-Requires-Root: no (routine-update)
+  * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository,
+    Repository-Browse.
+  * No build-time tests to circumvent module registration
+  * Updated dependy on qiime (>= 2020.11.0)
+
+ -- Steffen Moeller <moeller at debian.org>  Wed, 02 Dec 2020 00:10:06 +0100
+
 q2-cutadapt (2019.10.0-1) unstable; urgency=medium
 
   * New upstream version


=====================================
debian/control
=====================================
@@ -3,19 +3,20 @@ Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.
 Uploaders: Liubov Chuprikova <chuprikovalv at gmail.com>
 Section: science
 Priority: optional
-Build-Depends: debhelper-compat (= 12),
+Build-Depends: debhelper-compat (= 13),
                dh-python,
                python3,
                python3-setuptools,
                python3-pytest-cov,
                python3-pandas,
                cutadapt (>= 2.3),
-               qiime (>= 2019.10.0),
-               q2-types (>= 2019.10.0)
-Standards-Version: 4.4.1
+               qiime (>= 2020.11.0),
+               q2-types (>= 2020.11.0)
+Standards-Version: 4.5.1
 Vcs-Browser: https://salsa.debian.org/med-team/q2-cutadapt
 Vcs-Git: https://salsa.debian.org/med-team/q2-cutadapt.git
 Homepage: https://qiime2.org/
+Rules-Requires-Root: no
 
 Package: q2-cutadapt
 Architecture: any


=====================================
debian/copyright
=====================================
@@ -3,7 +3,7 @@ Upstream-Name: QIIME 2
 Source: https://github.com/qiime2/q2-cutadapt/releases
 
 Files: *
-Copyright: 2016-2019 QIIME 2 development team
+Copyright: 2016-2020 QIIME 2 development team
 License: BSD-3-clause
 
 Files: debian/*


=====================================
debian/rules
=====================================
@@ -13,7 +13,16 @@ export PYBUILD_AFTER_TEST=rm -v {build_dir}/site.py {build_dir}/.coverage*; \
 %:
 	dh $@ --with python3 --buildsystem=pybuild
 
+# FIXME: similarly to the qiime and q2-types packages, the build time testing fails, as 
+#        the Qiime plugin system is not fully available at this point of the package
+#        construction.  The test suite is only run at autopkgtest time, but this
+#        is not ideal, since the only tested python version is the default one.
 override_dh_auto_test:
 ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
-	dh_auto_test -- -s custom --test-args="cd {build_dir}; py.test-3 --cov=q2_cutadapt"
+#	dh_auto_test -- -s custom --test-args="cd {build_dir}; py.test-3 --cov=q2_cutadapt"
 endif
+
+override_dh_auto_clean:
+	dh_auto_clean
+	rm -f q2_cutadapt.egg-info/PKG-INFO q2_cutadapt.egg-info/SOURCES.txt q2_cutadapt.egg-info/dependency_links.txt q2_cutadapt.egg-info/entry_points.txt q2_cutadapt.egg-info/not-zip-safe q2_cutadapt.egg-info/top_level.txt
+


=====================================
debian/salsa-ci.yml
=====================================
@@ -0,0 +1,4 @@
+---
+include:
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml


=====================================
debian/upstream/metadata
=====================================
@@ -1,3 +1,5 @@
+Bug-Database: https://github.com/qiime2/q2-cutadapt/issues
+Bug-Submit: https://github.com/qiime2/q2-cutadapt/issues/new
 Reference:
  - Author: >
     Evan Bolyen and Jai Ram Rideout and Matthew R Dillon and Nicholas A
@@ -42,3 +44,5 @@ Reference:
    DOI: 10.1038/s41587-019-0209-9
    URL: https://www.nature.com/articles/s41587-019-0209-9
    eprint: https://www.nature.com/articles/s41587-019-0209-9.pdf
+Repository: https://github.com/qiime2/q2-cutadapt.git
+Repository-Browse: https://github.com/qiime2/q2-cutadapt


=====================================
q2_cutadapt/__init__.py
=====================================
@@ -1,5 +1,5 @@
 # ----------------------------------------------------------------------------
-# Copyright (c) 2017-2019, QIIME 2 development team.
+# Copyright (c) 2017-2020, QIIME 2 development team.
 #
 # Distributed under the terms of the Modified BSD License.
 #


=====================================
q2_cutadapt/_demux.py
=====================================
@@ -1,5 +1,5 @@
 # ----------------------------------------------------------------------------
-# Copyright (c) 2017-2019, QIIME 2 development team.
+# Copyright (c) 2017-2020, QIIME 2 development team.
 #
 # Distributed under the terms of the Modified BSD License.
 #
@@ -8,6 +8,7 @@
 
 import gzip
 import os
+import shutil
 import subprocess
 import tempfile
 
@@ -83,8 +84,20 @@ def _rename_files(seqs_dir_fmt, per_sample_dir_fmt, barcode_series):
             src = os.path.join(str(per_sample_dir_fmt),
                                '%s.%d.fastq.gz' % (sample_id,
                                                    read_direction))
+
+            # TODO: remove this outer guard when we upgrade to cutadapt 3
             if os.path.isfile(src):
-                os.rename(src, str(out_fp))
+                if out_fp.exists():
+                    _merge_files(src, str(out_fp))
+                    os.remove(src)
+                else:
+                    os.rename(src, str(out_fp))
+
+
+def _merge_files(src, dst):
+    with gzip.open(src, mode='rt', encoding='ascii') as src_fh, \
+            gzip.open(dst, mode='at', encoding='ascii') as dst_fh:
+        shutil.copyfileobj(src_fh, dst_fh)
 
 
 def _write_barcode_fasta(barcode_series, barcode_fasta):
@@ -106,8 +119,8 @@ def _write_empty_fastq_to_mux_barcode_in_seq_fmt(seqs_dir_fmt):
         seqs_dir_fmt.file.write_data(fastq, FastqGzFormat)
 
 
-def _demux(seqs, forward_barcodes, reverse_barcodes, error_tolerance,
-           mux_fmt, batch_size, minimum_length):
+def _demux(seqs, per_sample_sequences, forward_barcodes, reverse_barcodes,
+           error_tolerance, mux_fmt, batch_size, minimum_length):
     fwd_barcode_name = forward_barcodes.name
     forward_barcodes = forward_barcodes.drop_missing_values()
     barcodes = forward_barcodes.to_series().to_frame()
@@ -130,14 +143,16 @@ def _demux(seqs, forward_barcodes, reverse_barcodes, error_tolerance,
 
         if samples_w_missing_barcodes:
             raise ValueError('The following samples do not have both '
-                             'forward and reverse barcodes: %s'
+                             'forward and reverse barcodes (note: if your '
+                             'reads are in single index mixed orientation, '
+                             'try again with all of your barcodes in a single '
+                             'metadata column): %s'
                              % ', '.join(sorted(samples_w_missing_barcodes)))
         if samples_w_dup_barcode_pairs:
             raise ValueError('The following samples have duplicate barcode'
                              ' pairs: %s' %
                              ', '.join(sorted(samples_w_dup_barcode_pairs)))
 
-    per_sample_sequences = CasavaOneEightSingleLanePerSampleDirFmt()
     n_samples = len(barcodes)
     if batch_size > n_samples:
         raise ValueError('The batch_size (%d) cannot be greater than the '
@@ -164,13 +179,13 @@ def _demux(seqs, forward_barcodes, reverse_barcodes, error_tolerance,
         if reverse_barcodes is not None:
             open_fhs['rev'].close()
         previous_untrimmed = current_untrimmed
-        # Only use the forward barcode in the renamed files
-    _rename_files(seqs, per_sample_sequences, barcodes[fwd_barcode_name])
 
+    # Only use the forward barcode in the renamed files
+    _rename_files(seqs, per_sample_sequences, barcodes[fwd_barcode_name])
     muxed = len(list(per_sample_sequences.sequences.iter_views(FastqGzFormat)))
     if muxed == 0:
         raise ValueError('No samples were demultiplexed.')
-    return per_sample_sequences, previous_untrimmed
+    return previous_untrimmed
 
 
 def demux_single(seqs: MultiplexedSingleEndBarcodeInSequenceDirFmt,
@@ -180,9 +195,14 @@ def demux_single(seqs: MultiplexedSingleEndBarcodeInSequenceDirFmt,
                  minimum_length: int = 1) -> \
                     (CasavaOneEightSingleLanePerSampleDirFmt,
                      MultiplexedSingleEndBarcodeInSequenceDirFmt):
+    per_sample_sequences = CasavaOneEightSingleLanePerSampleDirFmt()
     mux_fmt = MultiplexedSingleEndBarcodeInSequenceDirFmt
-    return _demux(seqs, barcodes, None, error_rate, mux_fmt, batch_size,
-                  minimum_length)
+
+    untrimmed = _demux(
+        seqs, per_sample_sequences, barcodes, None, error_rate, mux_fmt,
+        batch_size, minimum_length)
+
+    return per_sample_sequences, untrimmed
 
 
 def demux_paired(seqs: MultiplexedPairedEndBarcodeInSequenceDirFmt,
@@ -190,9 +210,33 @@ def demux_paired(seqs: MultiplexedPairedEndBarcodeInSequenceDirFmt,
                  reverse_barcodes: qiime2.CategoricalMetadataColumn = None,
                  error_rate: float = 0.1,
                  batch_size: int = 0,
-                 minimum_length: int = 1) -> \
+                 minimum_length: int = 1,
+                 mixed_orientation: bool = False) -> \
                     (CasavaOneEightSingleLanePerSampleDirFmt,
                      MultiplexedPairedEndBarcodeInSequenceDirFmt):
+    if mixed_orientation and reverse_barcodes is not None:
+        raise ValueError('Dual-indexed barcodes for mixed orientation '
+                         'reads are not supported.')
+
+    per_sample_sequences = CasavaOneEightSingleLanePerSampleDirFmt()
     mux_fmt = MultiplexedPairedEndBarcodeInSequenceDirFmt
-    return _demux(seqs, forward_barcodes, reverse_barcodes, error_rate,
-                  mux_fmt, batch_size, minimum_length)
+
+    untrimmed = _demux(
+        seqs, per_sample_sequences, forward_barcodes, reverse_barcodes,
+        error_rate, mux_fmt, batch_size, minimum_length)
+
+    if mixed_orientation:
+        fwd = untrimmed.forward_sequences.view(FastqGzFormat)
+        rev = untrimmed.reverse_sequences.view(FastqGzFormat)
+
+        remaining_seqs = MultiplexedPairedEndBarcodeInSequenceDirFmt()
+        # fwd -> rev && rev -> fwd
+        remaining_seqs.forward_sequences.write_data(rev, FastqGzFormat)
+        remaining_seqs.reverse_sequences.write_data(fwd, FastqGzFormat)
+
+        untrimmed = _demux(
+            remaining_seqs, per_sample_sequences, forward_barcodes,
+            reverse_barcodes, error_rate, mux_fmt, batch_size,
+            minimum_length)
+
+    return per_sample_sequences, untrimmed


=====================================
q2_cutadapt/_trim.py
=====================================
@@ -1,5 +1,5 @@
 # ----------------------------------------------------------------------------
-# Copyright (c) 2017-2019, QIIME 2 development team.
+# Copyright (c) 2017-2020, QIIME 2 development team.
 #
 # Distributed under the terms of the Modified BSD License.
 #


=====================================
q2_cutadapt/_version.py
=====================================
@@ -23,9 +23,9 @@ def get_keywords():
     # setup.py/versioneer.py will grep for the variable names, so they must
     # each be defined on a line of their own. _version.py will just call
     # get_keywords().
-    git_refnames = " (tag: 2019.10.0)"
-    git_full = "b7f4a9c3769dbea95c437997289ffdf6858e40f9"
-    git_date = "2019-11-01 01:04:25 +0000"
+    git_refnames = " (HEAD -> master, tag: 2020.11.0)"
+    git_full = "8a18174db6b284af087f8bc82b0b717a446aab4e"
+    git_date = "2020-11-25 17:13:09 +0000"
     keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
     return keywords
 


=====================================
q2_cutadapt/plugin_setup.py
=====================================
@@ -1,5 +1,5 @@
 # ----------------------------------------------------------------------------
-# Copyright (c) 2017-2019, QIIME 2 development team.
+# Copyright (c) 2017-2020, QIIME 2 development team.
 #
 # Distributed under the terms of the Modified BSD License.
 #
@@ -282,6 +282,7 @@ plugin.methods.register_function(
                                     inclusive_end=True),
         'batch_size': Int % Range(0, None),
         'minimum_length': Int % Range(1, None),
+        'mixed_orientation': Bool,
     },
     outputs=[
         ('per_sample_sequences', SampleData[PairedEndSequencesWithQuality]),
@@ -306,6 +307,9 @@ plugin.methods.register_function(
                           'the cutadapt default of 0 has been overridden, '
                           'because that value produces empty sequence '
                           'records.',
+        'mixed_orientation': 'Handle demultiplexing of mixed orientation '
+                             'reads (i.e. when forward and reverse reads '
+                             'coexist in the same file).'
     },
     output_descriptions={
         'per_sample_sequences': 'The resulting demultiplexed sequences.',


=====================================
q2_cutadapt/tests/__init__.py
=====================================
@@ -1,5 +1,5 @@
 # ----------------------------------------------------------------------------
-# Copyright (c) 2017-2019, QIIME 2 development team.
+# Copyright (c) 2017-2020, QIIME 2 development team.
 #
 # Distributed under the terms of the Modified BSD License.
 #


=====================================
q2_cutadapt/tests/data/mixed-orientation/forward.fastq.gz
=====================================
Binary files /dev/null and b/q2_cutadapt/tests/data/mixed-orientation/forward.fastq.gz differ


=====================================
q2_cutadapt/tests/data/mixed-orientation/reverse.fastq.gz
=====================================
Binary files /dev/null and b/q2_cutadapt/tests/data/mixed-orientation/reverse.fastq.gz differ


=====================================
q2_cutadapt/tests/test_demux.py
=====================================
@@ -1,5 +1,5 @@
 # ----------------------------------------------------------------------------
-# Copyright (c) 2017-2019, QIIME 2 development team.
+# Copyright (c) 2017-2020, QIIME 2 development team.
 #
 # Distributed under the terms of the Modified BSD License.
 #
@@ -7,6 +7,7 @@
 # ----------------------------------------------------------------------------
 
 import gzip
+import itertools
 import os
 import pathlib
 import shutil
@@ -33,22 +34,28 @@ from qiime2.plugin.testing import TestPluginBase
 class TestDemuxSingle(TestPluginBase):
     package = 'q2_cutadapt.tests'
 
-    def assert_demux_results(self, exp_samples_and_barcodes, obs_demuxed_art):
+    def assert_demux_results(self, exp_samples_and_barcodes, exp_results,
+                             obs_demuxed_art):
         obs_demuxed = obs_demuxed_art.view(
             SingleLanePerSampleSingleEndFastqDirFmt)
         obs_demuxed_seqs = obs_demuxed.sequences.iter_views(FastqGzFormat)
-        zipped = zip(exp_samples_and_barcodes.iteritems(), obs_demuxed_seqs)
-        for (sample_id, barcode), (filename, _) in zipped:
+        zipped = itertools.zip_longest(exp_samples_and_barcodes.iteritems(),
+                                       exp_results, obs_demuxed_seqs)
+        for (sample_id, barcode), exp, (filename, fmt) in zipped:
             filename = str(filename)
             self.assertTrue(sample_id in filename)
             self.assertTrue(barcode in filename)
+            with gzip.open(str(fmt), 'rt') as fh:
+                obs = ''.join(fh.readlines())
+            self.assertEqual(exp, obs)
 
     def assert_untrimmed_results(self, exp, obs_untrimmed_art):
         obs_untrimmed = obs_untrimmed_art.view(
             MultiplexedSingleEndBarcodeInSequenceDirFmt)
         obs_untrimmed = obs_untrimmed.file.view(FastqGzFormat)
-        obs_untrimmed = gzip.decompress(obs_untrimmed.path.read_bytes())
-        self.assertEqual(exp, obs_untrimmed)
+        with gzip.open(str(obs_untrimmed), 'rt') as fh:
+            obs = ''.join(fh.readlines())
+        self.assertEqual(exp, obs)
 
     def setUp(self):
         super().setUp()
@@ -62,13 +69,21 @@ class TestDemuxSingle(TestPluginBase):
         metadata = CategoricalMetadataColumn(
             pd.Series(['AAAA', 'CCCC'], name='Barcode',
                       index=pd.Index(['sample_a', 'sample_b'], name='id')))
+        exp = [
+            # sample a
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample b
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n', ]
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_single_fn(self.muxed_sequences, metadata)
 
-        self.assert_demux_results(metadata.to_series(), obs_demuxed_art)
-        self.assert_untrimmed_results(b'@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
+        self.assert_demux_results(metadata.to_series(), exp, obs_demuxed_art)
+        self.assert_untrimmed_results('@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
                                       obs_untrimmed_art)
 
     def test_all_matched(self):
@@ -76,45 +91,83 @@ class TestDemuxSingle(TestPluginBase):
             pd.Series(['AAAA', 'CCCC', 'GGGG'], name='Barcode',
                       index=pd.Index(['sample_a', 'sample_b', 'sample_c'],
                                      name='id')))
+        exp = [
+            # sample a
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample b
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample c
+            '@id6\nACGTACGT\n+\nzzzzzzzz\n', ]
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_single_fn(self.muxed_sequences, metadata)
 
-        self.assert_demux_results(metadata.to_series(), obs_demuxed_art)
-        # obs_untrimmed should be empty, since everything matched
-        self.assert_untrimmed_results(b'', obs_untrimmed_art)
+        self.assert_demux_results(metadata.to_series(), exp, obs_demuxed_art)
+        self.assert_untrimmed_results('', obs_untrimmed_art)
 
+    # NOTE: this test used to check for an exception because it was
+    # possible to generate a completely empty output dir with no fastq.gz
+    # files in it. As of cutadapt 3 this is no longer possible, because output
+    # files are generated for every sample (and we must specify at least one
+    # sample in order for the barcodes to be valid QIIME 2 Metadata). Rather
+    # than remove the test, we will retool it here.
     def test_none_matched(self):
         metadata = CategoricalMetadataColumn(
             pd.Series(['TTTT'], name='Barcode',
                       index=pd.Index(['sample_d'], name='id')))
 
         with redirected_stdio(stderr=os.devnull):
-            with self.assertRaisesRegex(ValueError, 'demultiplexed'):
+            obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_single_fn(self.muxed_sequences, metadata)
 
+        self.assert_demux_results(metadata.to_series(), [''], obs_demuxed_art)
+        self.assert_untrimmed_results('@id1\nAAAAACGTACGT\n+\nzzzzzzzzzzzz\n'
+                                      '@id2\nCCCCACGTACGT\n+\nzzzzzzzzzzzz\n'
+                                      '@id3\nAAAAACGTACGT\n+\nzzzzzzzzzzzz\n'
+                                      '@id4\nCCCCACGTACGT\n+\nzzzzzzzzzzzz\n'
+                                      '@id5\nCCCCACGTACGT\n+\nzzzzzzzzzzzz\n'
+                                      '@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
+                                      obs_untrimmed_art)
+
     def test_error_tolerance_filtering(self):
         metadata = CategoricalMetadataColumn(
             pd.Series(['AAAG', 'CCCC'], name='Barcode',
                       index=pd.Index(['sample_a', 'sample_b'], name='id')))
+        exp = [
+            # sample a has no reads (bc we misspelled the barcode)
+            '',
+            # sample b
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n', ]
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_single_fn(self.muxed_sequences, metadata)
 
-        # sample_a is dropped because of a substitution error (AAAA vs AAAG)
-        exp_samples_and_barcodes = pd.Series(['CCCC'], index=['sample_b'])
-        self.assert_demux_results(exp_samples_and_barcodes, obs_demuxed_art)
-        self.assert_untrimmed_results(b'@id1\nAAAAACGTACGT\n+\nzzzzzzzzzzzz\n'
-                                      b'@id3\nAAAAACGTACGT\n+\nzzzzzzzzzzzz\n'
-                                      b'@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
+        self.assert_demux_results(metadata.to_series(), exp,
+                                  obs_demuxed_art)
+        self.assert_untrimmed_results('@id1\nAAAAACGTACGT\n+\nzzzzzzzzzzzz\n'
+                                      '@id3\nAAAAACGTACGT\n+\nzzzzzzzzzzzz\n'
+                                      '@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
                                       obs_untrimmed_art)
 
     def test_error_tolerance_high_enough_to_prevent_filtering(self):
         metadata = CategoricalMetadataColumn(
             pd.Series(['AAAG', 'CCCC'], name='Barcode',
                       index=pd.Index(['sample_a', 'sample_b'], name='id')))
+        exp = [
+            # sample a
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample b
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n', ]
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
@@ -122,8 +175,8 @@ class TestDemuxSingle(TestPluginBase):
                                      error_rate=0.25)
 
         # This test should yield the same results as test_typical, above
-        self.assert_demux_results(metadata.to_series(), obs_demuxed_art)
-        self.assert_untrimmed_results(b'@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
+        self.assert_demux_results(metadata.to_series(), exp, obs_demuxed_art)
+        self.assert_untrimmed_results('@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
                                       obs_untrimmed_art)
 
     def test_extra_barcode_in_metadata(self):
@@ -131,19 +184,29 @@ class TestDemuxSingle(TestPluginBase):
             pd.Series(['AAAA', 'CCCC', 'GGGG', 'TTTT'], name='Barcode',
                       index=pd.Index(['sample_a', 'sample_b', 'sample_c',
                                       'sample_d'], name='id')))
+        exp = [
+            # sample a
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample b
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample c
+            '@id6\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample d is empty bc no reads matched the barcode TTTT
+            '', ]
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_single_fn(self.muxed_sequences, metadata)
 
-        # TTTT/sample_d shouldn't be in the demuxed results, because there
-        # were no reads with that barcode present
-        exp_samples_and_barcodes = pd.Series(['AAAA', 'CCCC', 'GGGG'],
+        exp_samples_and_barcodes = pd.Series(['AAAA', 'CCCC', 'GGGG', 'TTTT'],
                                              index=['sample_a', 'sample_b',
-                                                    'sample_c'])
-        self.assert_demux_results(exp_samples_and_barcodes, obs_demuxed_art)
-        # obs_untrimmed should be empty, since everything matched
-        self.assert_untrimmed_results(b'', obs_untrimmed_art)
+                                                    'sample_c', 'sample_d'])
+        self.assert_demux_results(exp_samples_and_barcodes, exp,
+                                  obs_demuxed_art)
+        self.assert_untrimmed_results('', obs_untrimmed_art)
 
     def test_variable_length_barcodes(self):
         metadata = CategoricalMetadataColumn(
@@ -153,28 +216,46 @@ class TestDemuxSingle(TestPluginBase):
         muxed_sequences_fp = self.get_data_path('variable_length.fastq.gz')
         muxed_sequences = Artifact.import_data(
             'MultiplexedSingleEndBarcodeInSequence', muxed_sequences_fp)
+        exp = [
+            # sample a
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample b
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample c
+            '@id6\nACGTACGT\n+\nzzzzzzzz\n', ]
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_single_fn(muxed_sequences, metadata)
 
-        # This test should yield the same results as test_typical, above, just
-        # with variable length barcodes
-        self.assert_demux_results(metadata.to_series(), obs_demuxed_art)
-        self.assert_untrimmed_results(b'', obs_untrimmed_art)
+        self.assert_demux_results(metadata.to_series(), exp, obs_demuxed_art)
+        self.assert_untrimmed_results('', obs_untrimmed_art)
 
     def test_batch_size(self):
         metadata = CategoricalMetadataColumn(
             pd.Series(['AAAA', 'CCCC'], name='Barcode',
                       index=pd.Index(['sample_a', 'sample_b'], name='id')))
+        exp = [
+            # sample a
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample b
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n', ]
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_single_fn(self.muxed_sequences, metadata,
                                      batch_size=1)
 
-        self.assert_demux_results(metadata.to_series(), obs_demuxed_art)
-        self.assert_untrimmed_results(b'@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
+        # This test should yield the same results as test_typical, above,
+        # the fact that we are batching shouldn't impact the final results
+        self.assert_demux_results(metadata.to_series(), exp, obs_demuxed_art)
+        self.assert_untrimmed_results('@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
                                       obs_untrimmed_art)
 
     def test_invalid_batch_size(self):
@@ -190,15 +271,24 @@ class TestDemuxSingle(TestPluginBase):
             pd.Series(['AAAA', 'CCCC', 'GGGG'], name='Barcode',
                       index=pd.Index(['sample_a', 'sample_b', 'sample_c'],
                       name='id')))
+        exp = [
+            # sample a
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample b
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample c
+            '@id6\nACGTACGT\n+\nzzzzzzzz\n', ]
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_single_fn(self.muxed_sequences, metadata,
                                      batch_size=2)
 
-        self.assert_demux_results(metadata.to_series(), obs_demuxed_art)
-        # obs_untrimmed should be empty, since everything matched
-        self.assert_untrimmed_results(b'', obs_untrimmed_art)
+        self.assert_demux_results(metadata.to_series(), exp, obs_demuxed_art)
+        self.assert_untrimmed_results('', obs_untrimmed_art)
 
     def test_min_length(self):
         metadata = CategoricalMetadataColumn(
@@ -207,42 +297,59 @@ class TestDemuxSingle(TestPluginBase):
             pd.Series(['AAAA', 'CCCC', 'GGGGACGTACGT'], name='Barcode',
                       index=pd.Index(['sample_a', 'sample_b', 'sample_c'],
                       name='id')))
+        exp = [
+            # sample a
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample b
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample c is empty because the barcode matched the entire
+            # read, which removed everything.
+            '', ]
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_single_fn(self.muxed_sequences, metadata)
 
-        obs = obs_demuxed_art.view(SingleLanePerSampleSingleEndFastqDirFmt)
-
-        (obs_f1, _), (obs_f2, _) = obs.sequences.iter_views(FastqGzFormat)
-
-        self.assertEqual('sample_a_AAAA_L001_R1_001.fastq.gz', str(obs_f1))
-        self.assertEqual('sample_b_CCCC_L001_R1_001.fastq.gz', str(obs_f2))
+        self.assert_demux_results(metadata.to_series(), exp, obs_demuxed_art)
+        self.assert_untrimmed_results('', obs_untrimmed_art)
 
 
 class TestDemuxPaired(TestPluginBase):
     package = 'q2_cutadapt.tests'
 
-    def assert_demux_results(self, exp_samples_and_barcodes, obs_demuxed_art):
+    def assert_demux_results(self, exp_samples_and_barcodes, exp_results,
+                             obs_demuxed_art):
         obs_demuxed = obs_demuxed_art.view(
             SingleLanePerSamplePairedEndFastqDirFmt)
         obs_demuxed_seqs = obs_demuxed.sequences.iter_views(FastqGzFormat)
         # Since we are working with fwd/rev reads, duplicate each list elem
         exp = [x for x in exp_samples_and_barcodes.iteritems() for _ in (0, 1)]
-        zipped = zip(exp, obs_demuxed_seqs)
-        for (sample_id, barcode), (filename, _) in zipped:
+        zipped = itertools.zip_longest(exp, exp_results, obs_demuxed_seqs)
+        for (sample_id, barcode), exp, (filename, fmt) in zipped:
             filename = str(filename)
             self.assertTrue(sample_id in filename)
             self.assertTrue(barcode in filename)
+            with gzip.open(str(fmt), 'rt') as fh:
+                obs = ''.join(fh.readlines())
+            self.assertEqual(exp, obs)
 
     def assert_untrimmed_results(self, exp, obs_untrimmed_art):
         obs_untrimmed = obs_untrimmed_art.view(
             MultiplexedPairedEndBarcodeInSequenceDirFmt)
+
+        # first check the fwd reads
         obs_untrimmed_f = obs_untrimmed.forward_sequences.view(FastqGzFormat)
-        obs_untrimmed_f = gzip.decompress(obs_untrimmed_f.path.read_bytes())
+        with gzip.open(str(obs_untrimmed_f), 'rt') as fh:
+            obs_untrimmed_f = ''.join(fh.readlines())
         self.assertEqual(exp[0], obs_untrimmed_f)
+
+        # next check the rev reads
         obs_untrimmed_r = obs_untrimmed.reverse_sequences.view(FastqGzFormat)
-        obs_untrimmed_r = gzip.decompress(obs_untrimmed_r.path.read_bytes())
+        with gzip.open(str(obs_untrimmed_r), 'rt') as fh:
+            obs_untrimmed_r = ''.join(fh.readlines())
         self.assertEqual(exp[1], obs_untrimmed_r)
 
     def setUp(self):
@@ -264,14 +371,29 @@ class TestDemuxPaired(TestPluginBase):
         metadata = CategoricalMetadataColumn(
             pd.Series(['AAAA', 'CCCC'], name='Barcode',
                       index=pd.Index(['sample_a', 'sample_b'], name='id')))
+        exp = [
+            # sample a, fwd
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample a, rev
+            '@id1\nGGGGTGCATGCA\n+\nzzzzzzzzzzzz\n'
+            '@id3\nGGGGTGCATGCA\n+\nzzzzzzzzzzzz\n',
+            # sample b, fwd
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample b, fwd
+            '@id2\nTTTTTGCATGCA\n+\nzzzzzzzzzzzz\n'
+            '@id4\nTTTTTGCATGCA\n+\nzzzzzzzzzzzz\n'
+            '@id5\nTTTTTGCATGCA\n+\nzzzzzzzzzzzz\n', ]
+        exp_untrimmed = ['@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
+                         '@id6\nTTTTTGCATGCA\n+\nzzzzzzzzzzzz\n']
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
                 self.demux_paired_fn(self.muxed_sequences, metadata)
 
-        self.assert_demux_results(metadata.to_series(), obs_demuxed_art)
-        exp_untrimmed = [b'@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
-                         b'@id6\nTTTTTGCATGCA\n+\nzzzzzzzzzzzz\n']
+        self.assert_demux_results(metadata.to_series(), exp, obs_demuxed_art)
         self.assert_untrimmed_results(exp_untrimmed, obs_untrimmed_art)
 
     def test_di_typical(self):
@@ -281,6 +403,23 @@ class TestDemuxPaired(TestPluginBase):
         reverse_barcodes = CategoricalMetadataColumn(
             pd.Series(['GGGG', 'TTTT'], name='ReverseBarcode',
                       index=pd.Index(['sample_a', 'sample_b'], name='id')))
+        exp = [
+            # sample a, fwd
+            '@id1\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id3\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample a, rev
+            '@id1\nTGCATGCA\n+\nzzzzzzzz\n'
+            '@id3\nTGCATGCA\n+\nzzzzzzzz\n',
+            # sample b, fwd
+            '@id2\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id4\nACGTACGT\n+\nzzzzzzzz\n'
+            '@id5\nACGTACGT\n+\nzzzzzzzz\n',
+            # sample a, fwd
+            '@id2\nTGCATGCA\n+\nzzzzzzzz\n'
+            '@id4\nTGCATGCA\n+\nzzzzzzzz\n'
+            '@id5\nTGCATGCA\n+\nzzzzzzzz\n', ]
+        exp_untrimmed = ['@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
+                         '@id6\nTTTTTGCATGCA\n+\nzzzzzzzzzzzz\n']
 
         with redirected_stdio(stderr=os.devnull):
             obs_demuxed_art, obs_untrimmed_art = \
@@ -288,12 +427,66 @@ class TestDemuxPaired(TestPluginBase):
                                      forward_barcodes=forward_barcodes,
                                      reverse_barcodes=reverse_barcodes)
 
-        self.assert_demux_results(forward_barcodes.to_series(),
+        self.assert_demux_results(forward_barcodes.to_series(), exp,
                                   obs_demuxed_art)
-        exp_untrimmed = [b'@id6\nGGGGACGTACGT\n+\nzzzzzzzzzzzz\n',
-                         b'@id6\nTTTTTGCATGCA\n+\nzzzzzzzzzzzz\n']
         self.assert_untrimmed_results(exp_untrimmed, obs_untrimmed_art)
 
+    def test_mixed_orientation_success(self):
+        # sample_a and sample_b have reads in both fwd and rev directions.
+        # sample_c only has reads in the fwd direction.
+        # sample_d only has reads in the rev direction.
+        forward_barcodes = CategoricalMetadataColumn(
+            pd.Series(['AAAA', 'CCCC', 'GGGG', 'TTTT'], name='ForwardBarcode',
+                      index=pd.Index(['sample_a', 'sample_b', 'sample_c',
+                                      'sample_d'], name='id')))
+        mixed_orientation_sequences_f_fp = self.get_data_path(
+            'mixed-orientation/forward.fastq.gz')
+        mixed_orientation_sequences_r_fp = self.get_data_path(
+            'mixed-orientation/reverse.fastq.gz')
+        with tempfile.TemporaryDirectory() as temp:
+            shutil.copy(mixed_orientation_sequences_f_fp, temp)
+            shutil.copy(mixed_orientation_sequences_r_fp, temp)
+            mixed_orientation_sequences = Artifact.import_data(
+                'MultiplexedPairedEndBarcodeInSequence', temp)
+
+        with redirected_stdio(stderr=os.devnull):
+            obs_demuxed_art, obs_untrimmed_art = \
+                self.demux_paired_fn(mixed_orientation_sequences,
+                                     forward_barcodes=forward_barcodes,
+                                     mixed_orientation=True)
+        exp = [
+            # sample_a fwd
+            '@id1\nACGTACGT\n+\nyyyyyyyy\n' \
+            '@id3\nACGTACGT\n+\nyyyyyyyy\n',
+            # sample_a rev
+            '@id1\nTGCATGCATGCA\n+\nzzzzzzzzzzzz\n' \
+            '@id3\nTGCATGCATGCA\n+\nzzzzzzzzzzzz\n',
+            # sample_b fwd
+            '@id4\nACGTACGT\n+\nyyyyyyyy\n' \
+            '@id2\nACGTACGT\n+\nyyyyyyyy\n',
+            # sample_b rev
+            '@id4\nTGCATGCATGCA\n+\nzzzzzzzzzzzz\n' \
+            '@id2\nTGCATGCATGCA\n+\nzzzzzzzzzzzz\n',
+            # sample_c fwd
+            '@id5\nACGTACGT\n+\nyyyyyyyy\n',
+            # sample_c rev
+            '@id5\nTGCATGCATGCA\n+\nzzzzzzzzzzzz\n',
+            # sample_d fwd
+            '@id6\nACGTACGT\n+\nyyyyyyyy\n',
+            # sample_d rev
+            '@id6\nTGCATGCATGCA\n+\nzzzzzzzzzzzz\n', ]
+
+        # We want to be sure that the validation is 100%, not just `min`,
+        obs_demuxed_art.validate(level='max')
+        # checkpoint assertion for the above `validate` - nothing should fail
+        self.assertTrue(True)
+
+        self.assert_demux_results(forward_barcodes.to_series(), exp,
+                                  obs_demuxed_art)
+
+        # Everything should match, so untrimmed should be empty
+        self.assert_untrimmed_results(['', ''], obs_untrimmed_art)
+
     def test_di_mismatched_barcodes(self):
         forward_barcodes = CategoricalMetadataColumn(
             pd.Series(['AAAA', 'CCCC', 'ACGT'], name='ForwardBarcode',
@@ -326,6 +519,36 @@ class TestDemuxPaired(TestPluginBase):
                                  forward_barcodes=forward_barcodes,
                                  reverse_barcodes=reverse_barcodes)
 
+    def test_multiple_orientations_dual_indices(self):
+        forward_barcodes = CategoricalMetadataColumn(
+            pd.Series(['AAAA', 'CCCC'], name='ForwardBarcode',
+                      index=pd.Index(['sample_a', 'sample_b'], name='id')))
+        reverse_barcodes = CategoricalMetadataColumn(
+            pd.Series(['GGGG', 'TTTT'], name='ReverseBarcode',
+                      index=pd.Index(['sample_a', 'sample_b'], name='id')))
+
+        mixed_orientation_sequences_f_fp = self.get_data_path(
+            'mixed-orientation/forward.fastq.gz')
+        mixed_orientation_sequences_r_fp = self.get_data_path(
+            'mixed-orientation/reverse.fastq.gz')
+
+        # These files have forward and reverse reads mixed together in the same
+        # file
+        with tempfile.TemporaryDirectory() as temp:
+            shutil.copy(mixed_orientation_sequences_f_fp, temp)
+            shutil.copy(mixed_orientation_sequences_r_fp, temp)
+            mixed_orientation_sequences = Artifact.import_data(
+                'MultiplexedPairedEndBarcodeInSequence', temp)
+
+        with self.assertRaisesRegex(ValueError,
+                                    'Dual-indexed barcodes for mixed '
+                                    'orientation reads are not supported.'):
+            obs_demuxed_art, obs_untrimmed_art = \
+                self.demux_paired_fn(mixed_orientation_sequences,
+                                     forward_barcodes=forward_barcodes,
+                                     reverse_barcodes=reverse_barcodes,
+                                     mixed_orientation=True)
+
 
 class TestDemuxUtilsSingleEnd(TestPluginBase):
     package = 'q2_cutadapt.tests'
@@ -360,7 +583,7 @@ class TestDemuxUtilsSingleEnd(TestPluginBase):
                          obs[11])
 
     def test_rename_files_single(self):
-        for fn in ['sample_a.fastq.gz', 'sample_b.fastq.gz']:
+        for fn in ['sample_a.1.fastq.gz', 'sample_b.1.fastq.gz']:
             shutil.copy(self.fastq_fp,
                         str(self.per_sample_dir_fmt.path / pathlib.Path(fn)))
 
@@ -368,16 +591,19 @@ class TestDemuxUtilsSingleEnd(TestPluginBase):
                       self.barcode_series)
 
         seqs = self.per_sample_dir_fmt.sequences.iter_views(FastqGzFormat)
+        counter = 0
         for fn, (sample_id, barcode) in zip(seqs,
                                             self.barcode_series.iteritems()):
             self.assertTrue(sample_id in str(fn))
             self.assertTrue(barcode in str(fn))
+            counter += 1
+        self.assertEqual(counter, 2)
 
     def test_rename_files_extra_samples_in_barcode_map(self):
         barcode_series = pd.Series(['A', 'G', 'C'],
                                    index=['sample_a', 'sample_b', 'sample_c'])
 
-        for fn in ['sample_a.fastq.gz', 'sample_b.fastq.gz']:
+        for fn in ['sample_a.1.fastq.gz', 'sample_b.1.fastq.gz']:
             shutil.copy(self.fastq_fp,
                         str(self.per_sample_dir_fmt.path / pathlib.Path(fn)))
 
@@ -385,9 +611,12 @@ class TestDemuxUtilsSingleEnd(TestPluginBase):
                       barcode_series)
 
         seqs = self.per_sample_dir_fmt.sequences.iter_views(FastqGzFormat)
+        counter = 0
         for fn, (sample_id, barcode) in zip(seqs, barcode_series.iteritems()):
             self.assertTrue(sample_id in str(fn))
             self.assertTrue(barcode in str(fn))
+            counter += 1
+        self.assertEqual(counter, 2)
 
     def test_write_empty_fastq_to_mux_barcode_in_seq_fmt(self):
         _write_empty_fastq_to_mux_barcode_in_seq_fmt(self.untrimmed_dir_fmt)
@@ -471,9 +700,12 @@ class TestDemuxUtilsPairedEnd(TestPluginBase):
         seqs = self.per_sample_dir_fmt.sequences.iter_views(FastqGzFormat)
         exp = [('sample_a', 'A'), ('sample_a', 'A'),
                ('sample_b', 'G'), ('sample_b', 'G')]
+        counter = 0
         for fn, (sample_id, barcode) in zip(seqs, exp):
             self.assertTrue(sample_id in str(fn))
             self.assertTrue(barcode in str(fn))
+            counter += 1
+        self.assertEqual(counter, 4)
 
     def test_rename_files_extra_samples_in_barcode_map(self):
         barcode_series = pd.Series(['A', 'G', 'C'],
@@ -490,9 +722,12 @@ class TestDemuxUtilsPairedEnd(TestPluginBase):
         seqs = self.per_sample_dir_fmt.sequences.iter_views(FastqGzFormat)
         exp = [('sample_a', 'A'), ('sample_a', 'A'),
                ('sample_b', 'G'), ('sample_b', 'G')]
+        counter = 0
         for fn, (sample_id, barcode) in zip(seqs, exp):
             self.assertTrue(sample_id in str(fn))
             self.assertTrue(barcode in str(fn))
+            counter += 1
+        self.assertEqual(counter, 4)
 
     def test_write_empty_fastq_to_mux_barcode_in_seq_fmt(self):
         _write_empty_fastq_to_mux_barcode_in_seq_fmt(self.untrimmed_dir_fmt)


=====================================
q2_cutadapt/tests/test_trim.py
=====================================
@@ -1,5 +1,5 @@
 # ----------------------------------------------------------------------------
-# Copyright (c) 2017-2019, QIIME 2 development team.
+# Copyright (c) 2017-2020, QIIME 2 development team.
 #
 # Distributed under the terms of the Modified BSD License.
 #


=====================================
setup.py
=====================================
@@ -1,5 +1,5 @@
 # ----------------------------------------------------------------------------
-# Copyright (c) 2017-2019, QIIME 2 development team.
+# Copyright (c) 2017-2020, QIIME 2 development team.
 #
 # Distributed under the terms of the Modified BSD License.
 #
@@ -31,6 +31,7 @@ setup(
             'data/single-end/*',
             'data/paired-end/*',
             'data/paired-end-unordered/*',
+            'data/mixed-orientation/*',
         ],
     },
     zip_safe=False,



View it on GitLab: https://salsa.debian.org/med-team/q2-cutadapt/-/compare/3a1a4f2c22ed15f629d0e7024e145f6c9f9b33ab...16dc7b27bfed6fea2e2d421edf0bb0b3ef33a1b1

-- 
View it on GitLab: https://salsa.debian.org/med-team/q2-cutadapt/-/compare/3a1a4f2c22ed15f629d0e7024e145f6c9f9b33ab...16dc7b27bfed6fea2e2d421edf0bb0b3ef33a1b1
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20201201/b0e35c13/attachment-0001.html>


More information about the debian-med-commit mailing list