const std = @import("std");

var defer_f1: bool = false;
var defer_f2: bool = false;
var defer_f3: bool = false;

test "cancel forwards" {
    var da = std.heap.DirectAllocator.init();
    defer da.deinit();

    const p = async<&da.allocator> f1() catch unreachable;
    cancel p;
    std.testing.expect(defer_f1);
    std.testing.expect(defer_f2);
    std.testing.expect(defer_f3);
}

async fn f1() void {
    defer {
        defer_f1 = true;
    }
    await (async f2() catch unreachable);
}

async fn f2() void {
    defer {
        defer_f2 = true;
    }
    await (async f3() catch unreachable);
}

async fn f3() void {
    defer {
        defer_f3 = true;
    }
    suspend;
}

var defer_b1: bool = false;
var defer_b2: bool = false;
var defer_b3: bool = false;
var defer_b4: bool = false;

test "cancel backwards" {
    var da = std.heap.DirectAllocator.init();
    defer da.deinit();

    const p = async<&da.allocator> b1() catch unreachable;
    cancel p;
    std.testing.expect(defer_b1);
    std.testing.expect(defer_b2);
    std.testing.expect(defer_b3);
    std.testing.expect(defer_b4);
}

async fn b1() void {
    defer {
        defer_b1 = true;
    }
    await (async b2() catch unreachable);
}

var b4_handle: promise = undefined;

async fn b2() void {
    const b3_handle = async b3() catch unreachable;
    resume b4_handle;
    cancel b4_handle;
    defer {
        defer_b2 = true;
    }
    const value = await b3_handle;
    @panic("unreachable");
}

async fn b3() i32 {
    defer {
        defer_b3 = true;
    }
    await (async b4() catch unreachable);
    return 1234;
}

async fn b4() void {
    defer {
        defer_b4 = true;
    }
    suspend {
        b4_handle = @handle();
    }
    suspend;
}
