def test_intersection_error(self):
     with pytest.raises(MismatchedParentException):
         EmptyLocation().intersection(SingleInterval(0,
                                                     1,
                                                     Strand.PLUS,
                                                     parent="seq"),
                                      strict_parent_compare=True)
示例#2
0
    def get_3p_interval(self) -> Location:
        """Returns the 3' UTR as a location, if it exists.

        WARNING: If this is a chunk-relative transcript, the result of this function will also be chunk-relative.
        """
        if not self.is_coding:
            raise NoncodingTranscriptError("No 3' UTR on a non-coding transcript")
        # handle the edge case where the CDS is full length
        if self.cds.chunk_relative_location == self.chunk_relative_location:
            return EmptyLocation()
        cds_inclusive_end_on_transcript = self.cds_pos_to_transcript(len(self.cds.chunk_relative_location) - 1)
        return self.chunk_relative_location.relative_interval_to_parent_location(
            cds_inclusive_end_on_transcript + 1, len(self._location), Strand.PLUS
        )
 def test_reverse_strand(self):
     assert EmptyLocation().reverse_strand() == EmptyLocation()
示例#4
0
class TestFeatureInterval:
    """Test constructing features of various types"""

    @pytest.mark.parametrize(
        "schema,expected",
        [
            (se_unspliced, se_unspliced_repr),
            (e3_spliced, e3_spliced_repr),
            (e3_spliced, e3_spliced_repr),
            (se_unspliced_minus, se_unspliced_repr_minus),
            (e3_spliced_minus, e3_spliced_repr_minus),
        ],
    )
    def test_feature_constructor(self, schema, expected):
        assert str(schema.to_feature_interval()) == expected
        assert str(schema.to_feature_interval(parent_or_seq_chunk_parent=parent)) == expected

    @pytest.mark.parametrize(
        "schema,expected_exception",
        [
            (
                FeatureIntervalModel.Schema().load(
                    dict(interval_starts=[0], interval_ends=[], strand=Strand.PLUS.name)
                ),
                ValidationException,
            ),
            (
                FeatureIntervalModel.Schema().load(
                    dict(interval_starts=[0], interval_ends=[2, 5], strand=Strand.PLUS.name)
                ),
                ValidationException,
            ),
            (
                FeatureIntervalModel.Schema().load(
                    dict(interval_starts=[], interval_ends=[2, 5], strand=Strand.PLUS.name)
                ),
                ValidationException,
            ),
        ],
    )
    def test_feature_excecptions(self, schema, expected_exception):
        with pytest.raises(expected_exception):
            _ = schema.to_feature_interval()

    @pytest.mark.parametrize(
        "schema,value,expected",
        [
            (e3_spliced, 2, 0),
            (e3_spliced, 7, 4),
            (e3_spliced, 14, 9),
            (e3_spliced_minus, 2, 9),
            (e3_spliced_minus, 7, 5),
            (e3_spliced_minus, 14, 0),
        ],
    )
    def test_sequence_pos_to_feature(self, schema, value, expected):
        feat = schema.to_feature_interval()
        assert feat.sequence_pos_to_feature(value) == expected
        assert feat.chunk_relative_pos_to_feature(value) == expected

    @pytest.mark.parametrize(
        "schema,value,expected",
        [
            (e3_spliced, (7, 13, Strand.PLUS), SingleInterval(4, 8, Strand.PLUS)),
            (e3_spliced_minus, (7, 13, Strand.PLUS), (SingleInterval(2, 6, Strand.MINUS))),
        ],
    )
    def test_sequence_interval_to_feature(self, schema, value, expected):
        feat = schema.to_feature_interval()
        assert feat.sequence_interval_to_feature(*value) == expected
        assert feat.chunk_relative_interval_to_feature(*value) == expected

    @pytest.mark.parametrize(
        "schema,value,expected",
        [
            (e3_spliced, 0, 2),
            (e3_spliced, 9, 14),
            (e3_spliced, 4, 7),
            (e3_spliced_minus, 0, 14),
            (e3_spliced_minus, 9, 2),
            (e3_spliced_minus, 5, 7),
        ],
    )
    def test_feature_pos_to_sequence(self, schema, value, expected):
        feat = schema.to_feature_interval()
        assert feat.feature_pos_to_sequence(value) == expected
        assert feat.feature_pos_to_chunk_relative(value) == expected
        feat = schema.to_feature_interval(parent_or_seq_chunk_parent=parent)
        assert feat.feature_pos_to_sequence(value) == expected
        assert feat.feature_pos_to_chunk_relative(value) == expected

    @pytest.mark.parametrize(
        "schema,value,expected",
        [
            (e3_spliced, (0, 5, Strand.PLUS), CompoundInterval([2, 7], [6, 8], Strand.PLUS)),
            (e3_spliced_minus, (0, 5, Strand.PLUS), CompoundInterval([8, 12], [10, 15], Strand.MINUS)),
        ],
    )
    def test_feature_interval_to_sequence(self, schema, value, expected):
        feat = schema.to_feature_interval()
        assert feat.feature_interval_to_sequence(*value).reset_parent(None) == expected
        assert feat.feature_interval_to_chunk_relative(*value).reset_parent(None) == expected

    @pytest.mark.parametrize(
        "schema,value,expected",
        [
            (e3_spliced, (0, 5, Strand.PLUS), CompoundInterval([2, 7], [6, 8], Strand.PLUS, parent=parent)),
            (e3_spliced_minus, (0, 5, Strand.PLUS), CompoundInterval([8, 12], [10, 15], Strand.MINUS, parent=parent)),
        ],
    )
    def test_feature_interval_to_sequence_parent(self, schema, value, expected):
        feat = schema.to_feature_interval(parent_or_seq_chunk_parent=parent)
        assert feat.feature_interval_to_sequence(*value) == expected
        assert feat.feature_interval_to_chunk_relative(*value) == expected

    @pytest.mark.parametrize(
        "schema,value,expected",
        [
            (
                e3_spliced,
                SingleInterval(0, 10, Strand.PLUS, parent=parent),
                dict(interval_starts=(2, 7), interval_ends=(6, 10), strand=Strand.PLUS.name),
            ),
            (
                e3_spliced_minus,
                SingleInterval(0, 10, Strand.PLUS, parent=parent),
                dict(interval_starts=(2, 7), interval_ends=(6, 10), strand=Strand.MINUS.name),
            ),
        ],
    )
    def test_intersection(self, schema, value, expected):
        feat = schema.to_feature_interval(parent_or_seq_chunk_parent=parent)
        val = feat.intersect(value)
        expected = FeatureIntervalModel.Schema().load(expected).to_feature_interval(parent_or_seq_chunk_parent=parent)
        assert str(expected) == str(val)

    @pytest.mark.parametrize(
        "schema,value,expected",
        [
            (
                e3_spliced,
                SingleInterval(0, 10, Strand.PLUS),
                dict(interval_starts=(2, 7), interval_ends=(6, 10), strand=Strand.PLUS.name),
            ),
            (
                e3_spliced_minus,
                SingleInterval(0, 10, Strand.PLUS),
                dict(interval_starts=(2, 7), interval_ends=(6, 10), strand=Strand.MINUS.name),
            ),
        ],
    )
    def test_intersection_no_parent(self, schema, value, expected):
        feat = schema.to_feature_interval()
        val = feat.intersect(value)
        expected = FeatureIntervalModel.Schema().load(expected).to_feature_interval()
        assert str(expected) == str(val)

    @pytest.mark.parametrize(
        "schema,parent,expected_spliced",
        [
            (e3_spliced, parent, "ATTCTGGCTA"),
            (e3_spliced, parent_genome2, "GTATCTTACC"),
            (
                # explicit strand on Parent has no effect
                e3_spliced,
                parent_genome2_minus,
                "GTATCTTACC",
            ),
            (e3_spliced_minus, parent, "TAGCCAGAAT"),
            (e3_spliced_minus, parent_genome2, "GGTAAGATAC"),
            (
                # explicit strand on Parent has no effect
                e3_spliced_minus,
                parent_genome2_minus,
                "GGTAAGATAC",
            ),
        ],
    )
    def test_spliced_sequence(self, schema, parent, expected_spliced):
        feat = schema.to_feature_interval(parent_or_seq_chunk_parent=parent)
        assert str(feat.get_spliced_sequence()) == expected_spliced

    @pytest.mark.parametrize(
        "schema,parent,expected_genomic,expected_stranded_genomic",
        [
            # positive strand, so genomic == genomic_stranded
            (e3_spliced, parent, "ATTCTTGGACCTA", "ATTCTTGGACCTA"),
            (e3_spliced, parent_genome2, "GTATTCTTGGACC", "GTATTCTTGGACC"),
            (
                # explicit strand on Parent has no effect
                e3_spliced,
                parent_genome2_minus,
                "GTATTCTTGGACC",
                "GTATTCTTGGACC",
            ),
            (e3_spliced_minus, parent, "ATTCTTGGACCTA", "TAGGTCCAAGAAT"),
            (e3_spliced_minus, parent_genome2, "GTATTCTTGGACC", "GGTCCAAGAATAC"),
            (
                # explicit strand on Parent flips it
                e3_spliced_minus,
                parent_genome2_minus,
                "GTATTCTTGGACC",
                "GGTCCAAGAATAC",
            ),
        ],
    )
    def test_genomic_sequence(self, schema, parent, expected_genomic, expected_stranded_genomic):
        feat = schema.to_feature_interval(parent_or_seq_chunk_parent=parent)
        assert str(feat.get_reference_sequence()) == expected_genomic
        assert str(feat.get_genomic_sequence()) == expected_stranded_genomic

    @pytest.mark.parametrize(
        "schema,expected",
        [(e3_spliced, "GTATCTTACC"), (e3_spliced_minus, "GGTAAGATAC")],
    )
    def test_reset_parent(self, schema, expected):
        feat = schema.to_feature_interval(parent_or_seq_chunk_parent=parent)
        feat._reset_parent(parent_genome2)
        assert str(feat.get_spliced_sequence()) == str(expected)

    def test_object_conversion(self):
        for model in [se_unspliced, e3_spliced_minus, e3_spliced]:
            obj = model.to_feature_interval()
            new_model = FeatureIntervalModel.from_feature_interval(obj)
            new_model.feature_interval_guid = None
            assert model == new_model
            obj = model.to_feature_interval(parent_or_seq_chunk_parent=parent)
            new_model = FeatureIntervalModel.from_feature_interval(obj)
            new_model.feature_interval_guid = None
            assert model == new_model

    def test_identifiers(self):
        feat = se_unspliced.to_feature_interval()
        feat.feature_name = "test"
        feat.feature_id = "testid"
        assert feat.identifiers == {"test", "testid"}
        assert feat.identifiers_dict == {"feature_name": "test", "feature_id": "testid"}

    def test_intersection_exception(self):
        schema = FeatureIntervalModel.Schema().load(
            dict(interval_starts=[10], interval_ends=[15], strand=Strand.MINUS.name)
        )
        feat = schema.to_feature_interval()
        s = SingleInterval(0, 5, Strand.PLUS)
        with pytest.raises(EmptyLocationException):
            _ = feat.intersect(s)

    def test_gff_export_exceptions(self):
        feat = se_unspliced.to_feature_interval(parent_or_seq_chunk_parent=parent)
        with pytest.raises(GFF3MissingSequenceNameError):
            _ = "\n".join(str(x) for x in feat.to_gff())
        feat.sequence_name = "myseq"
        with pytest.raises(NoSuchAncestorException):
            _ = "\n".join(str(x) for x in feat.to_gff(chromosome_relative_coordinates=False))

    def test_gff_export(self):
        feat = se_unspliced.to_feature_interval(parent_or_seq_chunk_parent=parent)
        feat.sequence_name = "myseq"
        assert (
            "\n".join(str(x) for x in feat.to_gff())
            == "myseq\tBioCantor\tfeature_interval\t1\t18\t.\t+\t.\tID=6940c467-070a-3524-2dcb-a478a6fa0388\n"
            "myseq\tBioCantor\tsubregion\t1\t18\t.\t+\t.\tID=feature-6940c467-070a-3524-2dcb-a478a6fa0388-1;"
            "Parent=6940c467-070a-3524-2dcb-a478a6fa0388"
        )

    def test_gff_export_subset(self):
        feat = e3_spliced.to_feature_interval(parent_or_seq_chunk_parent=parent_genome2_1_15)
        feat.sequence_name = "myseq"
        assert (
            "\n".join(str(x) for x in feat.to_gff())
            == "myseq\tBioCantor\tfeature_interval\t3\t15\t.\t+\t.\tID=c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80\n"
            "myseq\tBioCantor\tsubregion\t3\t6\t.\t+\t.\tID=feature-c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80-1;"
            "Parent=c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80\n"
            "myseq\tBioCantor\tsubregion\t8\t10\t.\t+\t.\tID=feature-c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80-2;"
            "Parent=c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80\n"
            "myseq\tBioCantor\tsubregion\t13\t15\t.\t+\t.\tID=feature-c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80-3;"
            "Parent=c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80"
        )
        assert (
            "\n".join(str(x) for x in feat.to_gff(chromosome_relative_coordinates=False))
            == "myseq\tBioCantor\tfeature_interval\t2\t14\t.\t+\t.\tID=c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80\n"
            "myseq\tBioCantor\tsubregion\t2\t5\t.\t+\t.\tID=feature-c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80-1;"
            "Parent=c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80\n"
            "myseq\tBioCantor\tsubregion\t7\t9\t.\t+\t.\tID=feature-c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80-2;"
            "Parent=c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80\n"
            "myseq\tBioCantor\tsubregion\t12\t14\t.\t+\t.\tID=feature-c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80-3;"
            "Parent=c45e8d7b-cbd6-43b2-bb08-429d9cb7fe80"
        )

    @pytest.mark.parametrize(
        "feature,parent,expected_span",
        [
            (e3_spliced, None, SingleInterval(2, 15, Strand.PLUS)),
            (e3_spliced_minus, None, SingleInterval(2, 15, Strand.MINUS)),
            (se_unspliced, None, SingleInterval(0, 18, Strand.PLUS)),
            (se_unspliced, None, SingleInterval(0, 18, Strand.PLUS)),
            (e3_spliced, parent_genome2, SingleInterval(2, 15, Strand.PLUS, parent=parent_genome2)),
            (e3_spliced_minus, parent_genome2, SingleInterval(2, 15, Strand.MINUS, parent=parent_genome2)),
            (se_unspliced, parent_genome2, SingleInterval(0, 18, Strand.PLUS, parent=parent_genome2)),
            (se_unspliced, parent_genome2, SingleInterval(0, 18, Strand.PLUS, parent=parent_genome2)),
            (
                e3_spliced,
                parent_genome2_1_15,
                SingleInterval(
                    2, 15, Strand.PLUS, parent=parent_genome2_1_15.first_ancestor_of_type(SequenceType.CHROMOSOME)
                ),
            ),
            (
                e3_spliced_minus,
                parent_genome2_1_15,
                SingleInterval(
                    2, 15, Strand.MINUS, parent=parent_genome2_1_15.first_ancestor_of_type(SequenceType.CHROMOSOME)
                ),
            ),
            (
                e3_spliced,
                parent_genome2_2_8,
                SingleInterval(
                    2, 15, Strand.PLUS, parent=parent_genome2_2_8.first_ancestor_of_type(SequenceType.CHROMOSOME)
                ),
            ),
            (
                e3_spliced_minus,
                parent_genome2_2_8,
                SingleInterval(
                    2, 15, Strand.MINUS, parent=parent_genome2_2_8.first_ancestor_of_type(SequenceType.CHROMOSOME)
                ),
            ),
        ],
    )
    def test_chromosome_span(self, feature, parent, expected_span):
        feat = feature.to_feature_interval(parent)
        assert feat.chromosome_span == expected_span

    @pytest.mark.parametrize(
        "feature,parent,expected_gaps",
        [
            (e3_spliced, None, CompoundInterval([6, 10], [7, 12], Strand.PLUS)),
            (e3_spliced_minus, None, CompoundInterval([6, 10], [7, 12], Strand.MINUS)),
            (se_unspliced, None, EmptyLocation()),
            (se_unspliced, None, EmptyLocation()),
            (e3_spliced, parent_genome2, CompoundInterval([6, 10], [7, 12], Strand.PLUS, parent=parent_genome2)),
            (e3_spliced_minus, parent_genome2, CompoundInterval([6, 10], [7, 12], Strand.MINUS, parent=parent_genome2)),
            (se_unspliced, parent_genome2, EmptyLocation()),
            (se_unspliced, parent_genome2, EmptyLocation()),
            (
                e3_spliced,
                parent_genome2_1_15,
                CompoundInterval(
                    [6, 10],
                    [7, 12],
                    Strand.PLUS,
                    parent=parent_genome2_1_15.first_ancestor_of_type(SequenceType.CHROMOSOME),
                ),
            ),
            (
                e3_spliced_minus,
                parent_genome2_1_15,
                CompoundInterval(
                    [6, 10],
                    [7, 12],
                    Strand.MINUS,
                    parent=parent_genome2_1_15.first_ancestor_of_type(SequenceType.CHROMOSOME),
                ),
            ),
            (se_unspliced, parent_genome2_1_15, EmptyLocation()),
            (se_unspliced, parent_genome2_1_15, EmptyLocation()),
            (
                e3_spliced,
                parent_genome2_2_8,
                CompoundInterval(
                    [6, 10],
                    [7, 12],
                    Strand.PLUS,
                    parent=parent_genome2_1_15.first_ancestor_of_type(SequenceType.CHROMOSOME),
                ),
            ),
            (
                e3_spliced_minus,
                parent_genome2_2_8,
                CompoundInterval(
                    [6, 10],
                    [7, 12],
                    Strand.MINUS,
                    parent=parent_genome2_1_15.first_ancestor_of_type(SequenceType.CHROMOSOME),
                ),
            ),
        ],
    )
    def test_chromosome_gaps_location(self, feature, parent, expected_gaps):
        feat = feature.to_feature_interval(parent)
        assert feat.chromosome_gaps_location == expected_gaps
 def test_parent_to_relative_location(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().parent_to_relative_location(
             SingleInterval(0, 0, Strand.PLUS))
 def test_extract_sequence(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().extract_sequence()
 def test_gap_list(self):
     assert EmptyLocation().gap_list() == []
 def test_num_blocks(self):
     assert EmptyLocation().num_blocks == 0
 def test_merge_overlapping(self):
     assert EmptyLocation().merge_overlapping() == EmptyLocation()
 def test_length(self):
     assert EmptyLocation().length == 0
 def test_distance_to(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().distance_to(EmptyLocation())
 def test_shift_position(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().shift_position(0)
 def test_extend_relative(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().extend_relative(0, 0)
 def test_reset_parent(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().reset_parent(None)
 def test_reset_strand(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().reset_strand(Strand.PLUS)
 def test_blocks(self):
     assert EmptyLocation().blocks == []
 def test_scan_blocks(self):
     assert not EmptyLocation().scan_blocks()
 def test_first_ancestor_of_type(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().first_ancestor_of_type("seqtype")
 def test_optimize_blocks(self):
     assert EmptyLocation().optimize_blocks() == EmptyLocation()
 def test_has_ancestor_of_type(self):
     assert EmptyLocation().has_ancestor_of_type("seqtype") is False
 def test_gaps_location(self):
     assert EmptyLocation().gaps_location() == EmptyLocation()
 def test_union_preserve_overlaps(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().union_preserve_overlaps(
             SingleInterval(0, 1, Strand.PLUS))
 def test_relative_to_parent_pos(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().relative_to_parent_pos(0)
 def test_parent(self):
     assert EmptyLocation().parent is None
 def test_intersection(self, other):
     assert EmptyLocation().intersection(other) == EmptyLocation()
 def test_start(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().start
示例#27
0
class TestParent:
    @pytest.mark.parametrize(
        "id,sequence_type,strand,location,sequence,expected",
        [
            (None, None, None, None, None, Parent()),
            (
                "id",
                "seqtype",
                Strand.MINUS,
                SingleInterval(
                    0,
                    1,
                    Strand.MINUS,
                    Parent(
                        id="id",
                        sequence_type="seqtype",
                        strand=Strand.MINUS,
                        sequence=Sequence(
                            "AAA",
                            Alphabet.NT_STRICT,
                            id="id",
                            type="seqtype",
                            parent=Parent(
                                id="id2",
                                sequence=Sequence("CCC", Alphabet.NT_STRICT, id="id2"),
                            ),
                        ),
                    ),
                ),
                Sequence(
                    "AAA",
                    Alphabet.NT_STRICT,
                    id="id",
                    type="seqtype",
                    parent=Parent(id="id2", sequence=Sequence("CCC", Alphabet.NT_STRICT, id="id2")),
                ),
                Parent(
                    id="id",
                    sequence_type="seqtype",
                    strand=Strand.MINUS,
                    location=SingleInterval(
                        0,
                        1,
                        Strand.MINUS,
                        Parent(
                            id="id",
                            sequence_type="seqtype",
                            strand=Strand.MINUS,
                            sequence=Sequence(
                                "AAA",
                                Alphabet.NT_STRICT,
                                id="id",
                                type="seqtype",
                                parent=Parent(
                                    id="id2",
                                    sequence=Sequence("CCC", Alphabet.NT_STRICT, id="id2"),
                                ),
                            ),
                        ),
                    ),
                    sequence=Sequence(
                        "AAA",
                        Alphabet.NT_STRICT,
                        id="id",
                        type="seqtype",
                        parent=Parent(
                            id="id2",
                            sequence=Sequence("CCC", Alphabet.NT_STRICT, id="id2"),
                        ),
                    ),
                ),
            ),
        ],
    )
    def test_init(self, id, sequence_type, strand, location, sequence, expected):
        assert expected == Parent(
            id=id,
            sequence_type=sequence_type,
            strand=strand,
            location=location,
            sequence=sequence,
        )

    @pytest.mark.parametrize(
        "id,sequence_type,strand,location,sequence,parent,expected_exception",
        [
            ("id1", None, None, SingleInterval(0, 5, Strand.PLUS, parent="id2"), None, None, ParentException),
            ("id1", None, None, None, Sequence("AAA", Alphabet.NT_STRICT, id="id2"), None, ParentException),
            (
                None,
                None,
                None,
                SingleInterval(0, 5, Strand.PLUS, parent="id1"),
                Sequence("AAC", Alphabet.NT_STRICT, id="id2"),
                None,
                ParentException,
            ),
            (
                None,
                "seqtype",
                None,
                SingleInterval(0, 5, Strand.PLUS, parent=Parent(sequence_type="unknown")),
                None,
                None,
                ParentException,
            ),
            (None, "seqtype", None, None, Sequence("AAT", Alphabet.NT_STRICT, type="unknown"), None, ParentException),
            (
                None,
                None,
                None,
                SingleInterval(0, 5, Strand.PLUS, parent=Parent(sequence_type="unknown")),
                Sequence("AAG", Alphabet.NT_STRICT, type="seqtype"),
                None,
                ParentException,
            ),
            (None, None, Strand.MINUS, SingleInterval(0, 5, Strand.PLUS), None, None, InvalidStrandException),
            (
                None,
                None,
                None,
                SingleInterval(0, 10, Strand.PLUS),
                Sequence("A", Alphabet.NT_STRICT),
                None,
                InvalidPositionException,
            ),
            (
                None,
                None,
                None,
                None,
                Sequence("AA", Alphabet.NT_STRICT),
                Parent(sequence=Sequence("A", Alphabet.NT_STRICT)),
                LocationException,
            ),
            (None, None, Strand.PLUS, SingleInterval(5, 10, Strand.MINUS), None, None, InvalidStrandException),
            (
                None,
                None,
                None,
                None,
                Sequence("AA", Alphabet.NT_STRICT, parent="id1"),
                Parent(id="id2"),
                MismatchedParentException,
            ),
        ],
    )
    def test_init_error(self, id, sequence_type, strand, location, sequence, parent, expected_exception):
        with pytest.raises(expected_exception):
            Parent(
                id=id,
                sequence_type=sequence_type,
                strand=strand,
                location=location,
                sequence=sequence,
                parent=parent,
            )

    @pytest.mark.parametrize(
        "obj,expected",
        [
            (
                Sequence("AAA", Alphabet.NT_STRICT),
                Parent(sequence=Sequence("AAA", Alphabet.NT_STRICT)),
            ),
            ("parent", Parent(id="parent")),
            (
                SingleInterval(5, 10, Strand.PLUS),
                Parent(location=SingleInterval(5, 10, Strand.PLUS)),
            ),
            (
                CompoundInterval([5], [10], Strand.PLUS),
                Parent(location=CompoundInterval([5], [10], Strand.PLUS)),
            ),
            (EmptyLocation(), Parent(location=EmptyLocation())),
            (Strand.MINUS, Parent(strand=Strand.MINUS)),
            (
                Parent(
                    id="parent",
                    sequence_type="chr",
                    strand=Strand.MINUS,
                    parent=Parent(id="grandparent"),
                ),
                Parent(
                    id="parent",
                    sequence_type="chr",
                    strand=Strand.MINUS,
                    parent=Parent(id="grandparent"),
                ),
            ),
        ],
    )
    def test_make_parent(self, obj, expected):
        assert make_parent(obj) == expected

    @pytest.mark.parametrize(
        "parent1,parent2,expected",
        [
            (Parent(), Parent(), True),
            (Parent(), Parent(id=None, sequence_type=None), True),
            (Parent(id="id1"), Parent(id="id2"), False),
            (
                Parent(sequence_type=None),
                Parent(sequence_type="unknown"),
                False,
            ),
            (Parent(strand=Strand.UNSTRANDED), Parent(strand=Strand.MINUS), False),
            (
                Parent(location=SingleInterval(0, 5, Strand.PLUS, parent="id1")),
                Parent(location=SingleInterval(0, 5, Strand.PLUS, parent="id2")),
                False,
            ),
            (
                Parent(sequence=Sequence("A", Alphabet.NT_STRICT)),
                Parent(sequence=Sequence("A", Alphabet.NT_STRICT, parent=Parent(id="parent"))),
                False,
            ),
            (
                Parent(parent="parent1"),
                Parent(parent="parent2"),
                False,
            ),
        ],
    )
    def test_eq(self, parent1, parent2, expected):
        assert (parent1 == parent2) is expected

    @pytest.mark.parametrize(
        "parent1,parent2,expected",
        [
            (Parent(), Parent(), True),
            (Parent(id="id1"), Parent(id="id2"), False),
            (
                Parent(sequence_type=None),
                Parent(sequence_type="unknown"),
                False,
            ),
            (Parent(strand=Strand.UNSTRANDED), Parent(strand=Strand.MINUS), True),
            (
                Parent(location=SingleInterval(0, 5, Strand.PLUS, parent="id1")),
                Parent(location=SingleInterval(0, 5, Strand.PLUS, parent="id2")),
                False,
            ),
            (
                Parent(sequence=Sequence("A", Alphabet.NT_STRICT)),
                Parent(sequence=Sequence("A", Alphabet.NT_STRICT, parent="parent")),
                False,
            ),
            (
                Parent(parent="parent1"),
                Parent(parent="parent2"),
                False,
            ),
        ],
    )
    def test_equals_except_location(self, parent1, parent2, expected):
        assert parent1.equals_except_location(parent2) is expected

    @pytest.mark.parametrize(
        "id,location,sequence,expected",
        [
            ("id", None, None, "id"),
            (
                None,
                SingleInterval(0, 1, Strand.PLUS, parent="id"),
                None,
                "id",
            ),
            (
                None,
                None,
                Sequence("A", Alphabet.NT_STRICT, id="id", parent="id2"),
                "id",
            ),
            (
                "id",
                SingleInterval(0, 1, Strand.PLUS, parent="id"),
                Sequence("A", Alphabet.NT_STRICT, id="id", parent="id2"),
                "id",
            ),
        ],
    )
    def test_id(self, id, location, sequence, expected):
        assert Parent(id=id, location=location, sequence=sequence).id == expected

    @pytest.mark.parametrize(
        "sequence_type,location,sequence,expected",
        [
            ("seqtype", None, None, "seqtype"),
            (
                None,
                SingleInterval(
                    0,
                    5,
                    Strand.PLUS,
                    parent=Parent(sequence_type="seqtype"),
                ),
                None,
                "seqtype",
            ),
            (
                None,
                None,
                Sequence("A", Alphabet.NT_STRICT, type="seqtype"),
                "seqtype",
            ),
            (
                None,
                None,
                Sequence(
                    "A",
                    Alphabet.NT_STRICT,
                    type="seqtype",
                    parent=Parent(sequence_type="seqtype_2"),
                ),
                "seqtype",
            ),
        ],
    )
    def test_sequence_type(self, sequence_type, location, sequence, expected):
        assert Parent(sequence_type=sequence_type, location=location, sequence=sequence).sequence_type == expected

    @pytest.mark.parametrize(
        "strand,location,sequence,expected",
        [
            (Strand.PLUS, None, None, Strand.PLUS),
            (None, SingleInterval(0, 5, Strand.MINUS), None, Strand.MINUS),
            (
                Strand.PLUS,
                None,
                Sequence("A", Alphabet.NT_STRICT, parent=Strand.MINUS),
                Strand.PLUS,
            ),
        ],
    )
    def test_strand(self, strand, location, sequence, expected):
        assert Parent(strand=strand, location=location, sequence=sequence).strand == expected

    def test_location(self):
        assert Parent(location=SingleInterval(0, 1, Strand.PLUS)).location == SingleInterval(0, 1, Strand.PLUS)

    def test_sequence(self):
        assert Parent(sequence=Sequence("A", Alphabet.NT_STRICT)).sequence == Sequence("A", Alphabet.NT_STRICT)

    @pytest.mark.parametrize(
        "parent,expected",
        [
            (Parent(parent="id"), Parent(id="id")),
            (
                Parent(
                    sequence=Sequence(
                        "AA",
                        Alphabet.NT_STRICT,
                        parent=Parent(sequence_type="chr"),
                    )
                ),
                Parent(sequence_type="chr"),
            ),
        ],
    )
    def test_parent(self, parent, expected):
        assert parent.parent == expected

    @pytest.mark.parametrize(
        "parent,expected",
        [
            (Parent(), Parent()),
            (Parent(strand=Strand.PLUS), Parent()),
            (
                Parent(strand=Strand.PLUS, location=SingleInterval(5, 10, Strand.PLUS)),
                Parent(),
            ),
            (
                Parent(
                    id="parent",
                    sequence_type="unknown",
                    strand=Strand.PLUS,
                    location=SingleInterval(0, 1, Strand.PLUS),
                    sequence=Sequence("AAA", Alphabet.NT_STRICT),
                    parent="grandparent",
                ),
                Parent(
                    id="parent",
                    sequence_type="unknown",
                    sequence=Sequence("AAA", Alphabet.NT_STRICT),
                    parent="grandparent",
                ),
            ),
        ],
    )
    def test_strip_location_info(self, parent, expected):
        assert parent.strip_location_info() == expected

    @pytest.mark.parametrize(
        "parent,sequence_type,include_self,expected",
        [
            (
                Parent(
                    id="self",
                    sequence_type="seqtype",
                    parent=Parent(id="parent", sequence_type="seqtype"),
                ),
                "seqtype",
                True,
                Parent(
                    id="self",
                    sequence_type="seqtype",
                    parent=Parent(id="parent", sequence_type="seqtype"),
                ),
            ),
            (
                Parent(
                    id="self",
                    sequence_type="seqtype",
                    parent=Parent(id="parent", sequence_type="seqtype"),
                ),
                "seqtype",
                False,
                Parent(id="parent", sequence_type="seqtype"),
            ),
            (
                Parent(
                    id="self",
                    sequence_type="seqtype",
                    parent=Parent(
                        id="parent",
                        sequence_type="seqtype_2",
                        parent=Parent(id="grandparent", sequence_type="seqtype_2"),
                    ),
                ),
                "seqtype_2",
                True,
                Parent(
                    id="parent",
                    sequence_type="seqtype_2",
                    parent=Parent(id="grandparent", sequence_type="seqtype_2"),
                ),
            ),
        ],
    )
    def test_first_ancestor_of_type(self, parent, sequence_type, include_self, expected):
        assert parent.first_ancestor_of_type(sequence_type, include_self=include_self) == expected

    @pytest.mark.parametrize(
        "parent,sequence_type,include_self",
        [
            (Parent(id="self"), "seqtype_2", True),
            (
                Parent(id="self", parent="parent"),
                "seqtype_2",
                True,
            ),
            (
                Parent(
                    id="self",
                    sequence_type="seqtype",
                    parent=Parent(
                        id="parent",
                        sequence_type="seqtype_2",
                        parent=Parent(id="grandparent", sequence_type="seqtype_2"),
                    ),
                ),
                "chr",
                True,
            ),
        ],
    )
    def test_first_ancestor_of_type_error(self, parent, sequence_type, include_self):
        with pytest.raises(NoSuchAncestorException):
            parent.first_ancestor_of_type(sequence_type, include_self=include_self)

    @pytest.mark.parametrize(
        "parent,sequence_type,include_self,expected",
        [
            (
                Parent(
                    id="self",
                    sequence_type="seqtype",
                    parent=Parent(id="parent", sequence_type="seqtype"),
                ),
                "seqtype",
                True,
                True,
            ),
            (
                Parent(
                    id="self",
                    sequence_type="seqtype",
                    parent=Parent(id="parent", sequence_type="seqtype"),
                ),
                "seqtype",
                False,
                True,
            ),
            (
                Parent(
                    id="self",
                    sequence_type="seqtype",
                    parent=Parent(
                        id="parent",
                        sequence_type="seqtype_2",
                        parent=Parent(id="grandparent", sequence_type="seqtype_2"),
                    ),
                ),
                "seqtype_2",
                True,
                True,
            ),
            (
                Parent(id="self"),
                "seqtype_2",
                True,
                False,
            ),
            (
                Parent(id="self", parent="parent"),
                "seqtype_2",
                True,
                False,
            ),
            (
                Parent(
                    id="self",
                    sequence_type="seqtype",
                    parent=Parent(
                        id="parent",
                        sequence_type="seqtype_2",
                        parent=Parent(id="grandparent", sequence_type="seqtype_2"),
                    ),
                ),
                "chr",
                True,
                False,
            ),
        ],
    )
    def test_has_ancestor_of_type(self, parent, sequence_type, include_self, expected):
        assert parent.has_ancestor_of_type(sequence_type, include_self=include_self) is expected

    @pytest.mark.parametrize(
        "parent,expected",
        [
            (
                Parent(
                    id="parent",
                    location=SingleInterval(3, 5, Strand.PLUS),
                    parent=Parent(id="grandparent", location=SingleInterval(10, 20, Strand.PLUS)),
                ),
                SingleInterval(13, 15, Strand.PLUS, parent="grandparent"),
            ),
            (
                Parent(
                    id="parent",
                    location=SingleInterval(0, 5, Strand.PLUS),
                    sequence_type="unknown",
                    strand=Strand.PLUS,
                    parent=Parent(
                        id="grandparent",
                        location=SingleInterval(100, 200, Strand.MINUS),
                    ),
                ),
                SingleInterval(195, 200, Strand.MINUS, parent="grandparent"),
            ),
            (
                Parent(
                    id="parent",
                    location=SingleInterval(6, 9, Strand.MINUS),
                    parent=Parent(
                        id="grandparent",
                        location=SingleInterval(0, 10, Strand.PLUS),
                        sequence_type="chr",
                        strand=Strand.PLUS,
                        parent="great grandparent",
                    ),
                ),
                SingleInterval(
                    6,
                    9,
                    Strand.MINUS,
                    parent=Parent(
                        id="grandparent",
                        sequence_type="chr",
                        parent="great grandparent",
                    ),
                ),
            ),
            (
                Parent(
                    id="parent",
                    sequence_type="chr",
                    strand=Strand.MINUS,
                    location=SingleInterval(6, 8, Strand.MINUS),
                    parent=Parent(
                        id="grandparent",
                        sequence_type="unknown",
                        strand=Strand.MINUS,
                        location=SingleInterval(5, 15, Strand.MINUS),
                        parent="great grandparent",
                    ),
                ),
                SingleInterval(
                    7,
                    9,
                    Strand.PLUS,
                    parent=Parent(
                        id="grandparent",
                        sequence_type="unknown",
                        parent="great grandparent",
                    ),
                ),
            ),
            (
                Parent(
                    id="parent",
                    location=SingleInterval(3, 5, Strand.UNSTRANDED),
                    parent=Parent(id="grandparent", location=SingleInterval(10, 20, Strand.PLUS)),
                ),
                SingleInterval(13, 15, Strand.UNSTRANDED, parent="grandparent"),
            ),
            (
                Parent(
                    id="parent",
                    location=SingleInterval(3, 5, Strand.UNSTRANDED),
                    parent=Parent(id="grandparent", location=SingleInterval(10, 20, Strand.MINUS)),
                ),
                SingleInterval(15, 17, Strand.UNSTRANDED, parent="grandparent"),
            ),
        ],
    )
    def test_lift_child_location_contiguous_to_parent_single_interval(self, parent, expected):
        assert parent.lift_child_location_to_parent() == expected

    @pytest.mark.parametrize(
        "parent,expected",
        [
            (
                Parent(
                    id="parent",
                    location=CompoundInterval([3, 7], [5, 10], Strand.PLUS),
                    parent=Parent(id="grandparent", location=SingleInterval(10, 20, Strand.PLUS)),
                ),
                CompoundInterval([13, 17], [15, 20], Strand.PLUS, parent="grandparent"),
            ),
            (
                Parent(
                    id="parent",
                    location=CompoundInterval([0, 10], [5, 15], Strand.PLUS),
                    sequence_type="unknown",
                    strand=Strand.PLUS,
                    parent=Parent(
                        id="grandparent",
                        location=SingleInterval(100, 200, Strand.MINUS),
                    ),
                ),
                CompoundInterval(
                    [185, 195],
                    [190, 200],
                    Strand.MINUS,
                    parent="grandparent",
                ),
            ),
            (
                Parent(
                    id="parent",
                    location=CompoundInterval([6], [9], Strand.MINUS),
                    parent=Parent(
                        id="grandparent",
                        location=SingleInterval(0, 10, Strand.PLUS),
                        sequence_type="chr",
                        strand=Strand.PLUS,
                        parent="great grandparent",
                    ),
                ),
                SingleInterval(
                    6,
                    9,
                    Strand.MINUS,
                    parent=Parent(
                        id="grandparent",
                        sequence_type="chr",
                        parent="great grandparent",
                    ),
                ),
            ),
            (
                Parent(
                    id="parent",
                    sequence_type="chr",
                    strand=Strand.MINUS,
                    location=CompoundInterval([6], [8], Strand.MINUS),
                    parent=Parent(
                        id="grandparent",
                        sequence_type="unknown",
                        strand=Strand.MINUS,
                        location=SingleInterval(5, 15, Strand.MINUS),
                        parent="great grandparent",
                    ),
                ),
                SingleInterval(
                    7,
                    9,
                    Strand.PLUS,
                    parent=Parent(
                        id="grandparent",
                        sequence_type="unknown",
                        parent="great grandparent",
                    ),
                ),
            ),
            (
                Parent(
                    id="parent",
                    location=CompoundInterval([3, 7], [5, 10], Strand.UNSTRANDED),
                    parent=Parent(id="grandparent", location=SingleInterval(10, 20, Strand.PLUS)),
                ),
                CompoundInterval(
                    [13, 17],
                    [15, 20],
                    Strand.UNSTRANDED,
                    parent="grandparent",
                ),
            ),
            (
                Parent(
                    id="parent",
                    location=CompoundInterval([3], [5], Strand.UNSTRANDED),
                    parent=Parent(id="grandparent", location=SingleInterval(10, 20, Strand.MINUS)),
                ),
                SingleInterval(15, 17, Strand.UNSTRANDED, parent="grandparent"),
            ),
        ],
    )
    def test_lift_child_location_discontiguous_to_parent_single_interval(self, parent, expected):
        assert parent.lift_child_location_to_parent() == expected

    @pytest.mark.parametrize(
        "parent,expected_error",
        [
            # No location
            (
                Parent(parent=SingleInterval(5, 10, Strand.PLUS)),
                NullParentException,
            ),
            # Parent has no location
            (
                Parent(
                    location=SingleInterval(5, 10, Strand.PLUS),
                    parent="grandparent",
                ),
                NullParentException,
            ),
            # Location on parent can't be unstranded
            (
                Parent(
                    location=SingleInterval(5, 10, Strand.PLUS),
                    parent=Parent(
                        id="grandparent",
                        location=SingleInterval(0, 100, Strand.UNSTRANDED),
                    ),
                ),
                InvalidStrandException,
            ),
            # Location must fit inside location on parent
            (
                Parent(
                    location=SingleInterval(5, 10, Strand.PLUS),
                    parent=Parent(id="grandparent", location=SingleInterval(30, 31, Strand.PLUS)),
                ),
                ValueError,
            ),
        ],
    )
    def test_lift_child_location_to_parent_single_interval_error(self, parent, expected_error):
        with pytest.raises(expected_error):
            parent.lift_child_location_to_parent()

    @pytest.mark.parametrize(
        "parent,expected",
        [
            # Location takes up entire parent location
            (
                Parent(
                    id="parent",
                    location=SingleInterval(0, 10, Strand.PLUS),
                    parent=Parent(
                        id="grandparent",
                        location=CompoundInterval([5, 20], [10, 25], Strand.PLUS),
                    ),
                ),
                CompoundInterval([5, 20], [10, 25], Strand.PLUS, parent="grandparent"),
            ),
            # Location (unstranded) takes up part of parent location (minus)
            (
                Parent(
                    id="parent",
                    location=SingleInterval(10, 20, Strand.UNSTRANDED),
                    parent=Parent(
                        id="grandparent",
                        location=CompoundInterval([10, 20, 30], [18, 28, 38], Strand.MINUS),
                    ),
                ),
                CompoundInterval(
                    [14, 20],
                    [18, 26],
                    Strand.UNSTRANDED,
                    parent="grandparent",
                ),
            ),
            # Location (minus) takes up one block of parent location (plus); location is at end of sequence
            (
                Parent(
                    id="parent",
                    location=SingleInterval(5, 10, Strand.MINUS),
                    parent=Parent(
                        id="grandparent",
                        location=CompoundInterval([30, 40], [35, 45], Strand.PLUS),
                    ),
                ),
                SingleInterval(40, 45, Strand.MINUS, parent="grandparent"),
            ),
            # Location (minus) takes up part of one block of parent location (minus)
            (
                Parent(
                    id="parent",
                    location=SingleInterval(0, 4, Strand.MINUS),
                    parent=Parent(
                        id="grandparent",
                        location=CompoundInterval([30, 40], [35, 45], Strand.MINUS),
                    ),
                ),
                SingleInterval(41, 45, Strand.PLUS, parent="grandparent"),
            ),
        ],
    )
    def test_lift_child_location_contiguous_to_parent_compound_interval(self, parent, expected):
        assert parent.lift_child_location_to_parent() == expected

    @pytest.mark.parametrize(
        "parent,expected",
        [
            # Location takes up entire parent location
            (
                Parent(
                    id="parent",
                    location=CompoundInterval([0, 5], [5, 10], Strand.PLUS),
                    parent=Parent(
                        id="grandparent",
                        location=CompoundInterval([5, 20], [10, 25], Strand.PLUS),
                    ),
                ),
                CompoundInterval([5, 20], [10, 25], Strand.PLUS, parent="grandparent"),
            ),
            # Location (unstranded) takes up part of parent location (minus)
            (
                Parent(
                    id="parent",
                    location=CompoundInterval([10, 22], [20, 23], Strand.UNSTRANDED),
                    parent=Parent(
                        id="grandparent",
                        location=CompoundInterval([10, 20, 30], [18, 28, 38], Strand.MINUS),
                    ),
                ),
                CompoundInterval(
                    [11, 14, 20],
                    [12, 18, 26],
                    Strand.UNSTRANDED,
                    parent="grandparent",
                ),
            ),
            # Location (minus) takes up one block of parent location (plus); location is at end of sequence
            (
                Parent(
                    id="parent",
                    location=CompoundInterval([5], [10], Strand.MINUS),
                    parent=Parent(
                        id="grandparent",
                        location=CompoundInterval([30, 40], [35, 45], Strand.PLUS),
                    ),
                ),
                SingleInterval(40, 45, Strand.MINUS, parent="grandparent"),
            ),
            # Location (minus) takes up part of one block of parent location (minus)
            (
                Parent(
                    id="parent",
                    location=CompoundInterval([0, 3], [1, 4], Strand.MINUS),
                    parent=Parent(
                        id="grandparent",
                        location=CompoundInterval([30, 40], [35, 45], Strand.MINUS),
                    ),
                ),
                CompoundInterval([41, 44], [42, 45], Strand.PLUS, parent="grandparent"),
            ),
        ],
    )
    def test_lift_child_location_discontiguous_to_parent_compound_interval(self, parent, expected):
        assert parent.lift_child_location_to_parent() == expected

    @pytest.mark.parametrize(
        "parent,expected_error",
        [
            # Location must fit inside location on parent
            (
                Parent(
                    location=SingleInterval(5, 50, Strand.PLUS),
                    parent=Parent(
                        id="grandparent",
                        location=CompoundInterval([10, 20], [15, 25], Strand.PLUS),
                    ),
                ),
                InvalidPositionException,
            ),
        ],
    )
    def test_lift_child_location_to_parent_compound_interval_error(self, parent, expected_error):
        with pytest.raises(expected_error):
            parent.lift_child_location_to_parent()

    @pytest.mark.parametrize(
        "parent,location,expected",
        [
            (
                Parent(),
                SingleInterval(5, 10, Strand.PLUS),
                Parent(location=SingleInterval(5, 10, Strand.PLUS), strand=Strand.PLUS),
            ),
            (
                Parent(
                    id="parent",
                    sequence_type="unknown",
                    strand=Strand.MINUS,
                    location=SingleInterval(0, 2, Strand.MINUS),
                    sequence=Sequence("AAA", Alphabet.NT_STRICT),
                ),
                SingleInterval(2, 3, Strand.PLUS),
                Parent(
                    id="parent",
                    sequence_type="unknown",
                    strand=Strand.PLUS,
                    location=SingleInterval(2, 3, Strand.PLUS),
                    sequence=Sequence("AAA", Alphabet.NT_STRICT),
                ),
            ),
            (
                Parent(
                    id="parent",
                    sequence_type="unknown",
                    strand=Strand.MINUS,
                    location=SingleInterval(0, 2, Strand.MINUS),
                    sequence=Sequence("AAA", Alphabet.NT_STRICT),
                ),
                None,
                Parent(
                    id="parent",
                    sequence_type="unknown",
                    sequence=Sequence("AAA", Alphabet.NT_STRICT),
                ),
            ),
        ],
    )
    def test_reset_location(self, parent, location, expected):
        assert parent.reset_location(location) == expected

    @pytest.mark.parametrize(
        "parent,location,expected_exception",
        [
            (
                Parent(sequence=Sequence("AAA", Alphabet.NT_STRICT)),
                SingleInterval(0, 5, Strand.PLUS),
                InvalidPositionException,
            ),
            (
                Parent(id="id1", sequence=Sequence("AAA", Alphabet.NT_STRICT)),
                SingleInterval(
                    0,
                    1,
                    Strand.PLUS,
                    parent=Parent(id="id2", sequence=Sequence("AAA", Alphabet.NT_STRICT)),
                ),
                ParentException,
            ),
        ],
    )
    def test_reset_location_error(self, parent, location, expected_exception):
        with pytest.raises(expected_exception):
            parent.reset_location(location)

    @pytest.mark.parametrize(
        "parent,sequence,include_self,expected",
        [
            (Parent(), Sequence("AA", Alphabet.NT_STRICT), True, False),
            (Parent(), Sequence("AA", Alphabet.NT_STRICT), False, False),
            (
                Parent(sequence=Sequence("AA", Alphabet.NT_STRICT)),
                Sequence("AA", Alphabet.NT_STRICT),
                True,
                True,
            ),
            (
                Parent(sequence=Sequence("AA", Alphabet.NT_STRICT)),
                Sequence("AA", Alphabet.NT_STRICT),
                False,
                False,
            ),
            (
                Parent(
                    sequence=Sequence("AA", Alphabet.NT_STRICT),
                    parent=Sequence("AA", Alphabet.NT_STRICT),
                ),
                Sequence("AA", Alphabet.NT_STRICT),
                False,
                True,
            ),
            (
                Parent(
                    sequence=Sequence("AA", Alphabet.NT_STRICT),
                    parent=Sequence("AAT", Alphabet.NT_STRICT),
                ),
                Sequence("AAT", Alphabet.NT_STRICT),
                False,
                True,
            ),
            (
                Parent(
                    sequence=Sequence("AA", Alphabet.NT_STRICT),
                    parent=Sequence("AAT", Alphabet.NT_STRICT),
                ),
                Sequence("AAT", Alphabet.NT_STRICT, id="id"),
                True,
                False,
            ),
            (
                Parent(
                    parent=Parent(parent=Parent(parent=Parent(sequence=Sequence("AAA", Alphabet.NT_STRICT, id="seq"))))
                ),
                Sequence("AAA", Alphabet.NT_STRICT, id="seq"),
                True,
                True,
            ),
        ],
    )
    def test_has_ancestor_sequence(self, parent, sequence, include_self, expected):
        assert parent.has_ancestor_sequence(sequence, include_self) == expected
 def test_end(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().end
 def test_is_contiguous(self):
     with pytest.raises(EmptyLocationException):
         EmptyLocation().is_contiguous
 def test_reverse(self):
     assert EmptyLocation().reverse() == EmptyLocation()