<commit_before><?php

declare (strict_types = 1); // @codeCoverageIgnore

namespace Recoil;

context('when called with 1 parameter', function () {
    rit('terminates one strand when the other exits', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () use ($strandA) {
            yield Recoil::link($strandA);
        });

        yield;

        expect($strandA->hasExited())->to->be->true;
        expect($strandB->hasExited())->to->be->true;
    });

    rit('terminates one strand when the other exits with an exception', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () use ($strandA) {
            yield Recoil::link($strandA);
            throw new \Exception('<fail>');
        });

        try {
            yield Recoil::adopt($strandB);
            expect(false)->to->be->ok('expected exception was not thrown');
        } catch (\Exception $e) {
            yield;  // Allow linked strands to be terminated.
            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->true;
        }
    });

    context('when a strand is terminated', function () {
        rit('terminates the linked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield Recoil::link($strandA);
                yield 10;
            });

            yield;
            $strandA->terminate();

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->true;
        });

        rit('does not terminate unlinked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield Recoil::link($strandA);
                yield Recoil::unlink($strandA);
                yield 10;
            });

            yield;
            $strandA->terminate();

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->false;

            $strandB->terminate();
        });
    });

    rit('creates bidirectional links', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () use ($strandA) {
            yield Recoil::link($strandA);
            yield 10;
        });

        yield;
        $strandB->terminate();

        expect($strandA->hasExited())->to->be->true;
        expect($strandB->hasExited())->to->be->true;
    });
});

context('when called with 2 parameters', function () {
    rit('terminates one strand when the other exits', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () {
            yield Recoil::suspend();
        });

        yield;
        yield Recoil::link($strandA, $strandB);
        yield $strandB->send();

        expect($strandA->hasExited())->to->be->true;
        expect($strandB->hasExited())->to->be->true;
    });

     rit('terminates one strand when the other exits with an exception', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () use ($strandA) {
            yield;
            throw new \Exception('<fail>');
        });

        yield;
        yield Recoil::link($strandA, $strandB);

        try {
            yield Recoil::adopt($strandB);
            expect(false)->to->be->ok('expected exception was not thrown');
        } catch (\Exception $e) {
            yield;  // Allow linked strands to be terminated.
            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->true;
        }
     });

    context('when a strand is terminated', function () {
        rit('terminates the linked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () {
                yield 10;
            });

            yield;
            yield Recoil::link($strandA, $strandB);
            $strandA->terminate();

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->true;
        });

        rit('does not terminate unlinked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () {
                yield 10;
            });

            yield;
            yield Recoil::link($strandA, $strandB);
            yield Recoil::unlink($strandA, $strandB);
            $strandA->terminate();

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->false;

            $strandB->terminate();
        });
    });

    rit('creates bidirectional links', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () {
            yield 10;
        });

        yield;
        yield Recoil::link($strandA, $strandB);
        $strandB->terminate();

        expect($strandA->hasExited())->to->be->true;
        expect($strandB->hasExited())->to->be->true;
    });
});<commit_msg>Add tests for unlinked strand exiting.<commit_after><?php

declare (strict_types = 1); // @codeCoverageIgnore

namespace Recoil;

context('when called with 1 parameter', function () {
    context('when a strand exits', function () {
        rit('terminates the linked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield Recoil::link($strandA);
            });

            yield;

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->true;
        });

        rit('does not terminate unlinked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield Recoil::link($strandA);
                yield Recoil::unlink($strandA);
            });

            yield;

            expect($strandA->hasExited())->to->be->false;
            expect($strandB->hasExited())->to->be->true;

            $strandA->terminate();
        });
    });

    context('when a strand exits with an exception', function () {
        rit('terminates the linked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield Recoil::link($strandA);
                throw new \Exception('<fail>');
            });

            try {
                yield Recoil::adopt($strandB);
                expect(false)->to->be->ok('expected exception was not thrown');
            } catch (\Exception $e) {
                yield;  // Allow linked strands to be terminated.
                expect($strandA->hasExited())->to->be->true;
                expect($strandB->hasExited())->to->be->true;
            }
        });

        rit('does not terminate unlinked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield Recoil::link($strandA);
                yield Recoil::unlink($strandA);
                throw new \Exception('<fail>');
            });

            try {
                yield Recoil::adopt($strandB);
                expect(false)->to->be->ok('expected exception was not thrown');
            } catch (\Exception $e) {
                yield;  // Allow linked strands to be terminated.

                expect($strandA->hasExited())->to->be->false;
                expect($strandB->hasExited())->to->be->true;
            }

            $strandA->terminate();
        });
    });

    context('when a strand is terminated', function () {
        rit('terminates the linked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield Recoil::link($strandA);
                yield 10;
            });

            yield;
            $strandA->terminate();

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->true;
        });

        rit('does not terminate unlinked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield Recoil::link($strandA);
                yield Recoil::unlink($strandA);
                yield 10;
            });

            yield;
            $strandA->terminate();

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->false;

            $strandB->terminate();
        });
    });

    rit('creates bidirectional links', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () use ($strandA) {
            yield Recoil::link($strandA);
            yield 10;
        });

        yield;
        $strandB->terminate();

        expect($strandA->hasExited())->to->be->true;
        expect($strandB->hasExited())->to->be->true;
    });

    rit('breaks bidirectional links', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () use ($strandA) {
            yield Recoil::link($strandA);
            yield Recoil::unlink($strandA);
            yield 10;
        });

        yield;
        $strandB->terminate();

        expect($strandA->hasExited())->to->be->false;
        expect($strandB->hasExited())->to->be->true;

        $strandA->terminate();
    });
});

context('when called with 2 parameters', function () {
    context('when a strand exits', function () {
        rit('terminates the linked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () {
                yield Recoil::suspend();
            });

            yield;
            yield Recoil::link($strandA, $strandB);
            yield $strandB->send();

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->true;
        });

        rit('does not terminate unlinked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () {
                yield Recoil::suspend();
            });

            yield;
            yield Recoil::link($strandA, $strandB);
            yield Recoil::unlink($strandA, $strandB);
            yield $strandB->send();

            expect($strandA->hasExited())->to->be->false;
            expect($strandB->hasExited())->to->be->true;

            $strandA->terminate();
        });
    });

    context('when a strand exits with an exception', function () {
         rit('terminates the linked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield;
                throw new \Exception('<fail>');
            });

            yield;
            yield Recoil::link($strandA, $strandB);

            try {
                yield Recoil::adopt($strandB);
                expect(false)->to->be->ok('expected exception was not thrown');
            } catch (\Exception $e) {
                yield;  // Allow linked strands to be terminated.
                expect($strandA->hasExited())->to->be->true;
                expect($strandB->hasExited())->to->be->true;
            }
         });

         rit('does not terminate unlinked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () use ($strandA) {
                yield;
                throw new \Exception('<fail>');
            });

            yield;
            yield Recoil::link($strandA, $strandB);
            yield Recoil::unlink($strandA, $strandB);

            try {
                yield Recoil::adopt($strandB);
                expect(false)->to->be->ok('expected exception was not thrown');
            } catch (\Exception $e) {
                yield;  // Allow linked strands to be terminated.
                expect($strandA->hasExited())->to->be->false;
                expect($strandB->hasExited())->to->be->true;
            }

            $strandA->terminate();
         });
    });

    context('when a strand is terminated', function () {
        rit('terminates the linked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () {
                yield 10;
            });

            yield;
            yield Recoil::link($strandA, $strandB);
            $strandA->terminate();

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->true;
        });

        rit('does not terminate unlinked strand', function () {
            $strandA = yield Recoil::execute(function () {
                yield 10;
            });

            $strandB = yield Recoil::execute(function () {
                yield 10;
            });

            yield;
            yield Recoil::link($strandA, $strandB);
            yield Recoil::unlink($strandA, $strandB);
            $strandA->terminate();

            expect($strandA->hasExited())->to->be->true;
            expect($strandB->hasExited())->to->be->false;

            $strandB->terminate();
        });
    });

    rit('creates bidirectional links', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () {
            yield 10;
        });

        yield;
        yield Recoil::link($strandA, $strandB);
        $strandB->terminate();

        expect($strandA->hasExited())->to->be->true;
        expect($strandB->hasExited())->to->be->true;
    });

    rit('breaks bidirectional links', function () {
        $strandA = yield Recoil::execute(function () {
            yield 10;
        });

        $strandB = yield Recoil::execute(function () {
            yield 10;
        });

        yield;
        yield Recoil::link($strandA, $strandB);
        yield Recoil::unlink($strandA, $strandB);
        $strandB->terminate();

        expect($strandA->hasExited())->to->be->false;
        expect($strandB->hasExited())->to->be->true;

        $strandA->terminate();
    });
});
{'fraction_non_alphanumeric': 0.15603015075376883, 'fraction_numerical': 0.005402010050251256, 'mean_word_length': 1.70581237253569, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 262, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4435269', 'n_tokens_mistral': 4758, 'n_tokens_neox': 4126, 'n_words': 841}