<!DOCTYPE html>
<script src='../resources/testharness.js'></script>
<script src='../resources/testharnessreport.js'></script>
<script src='reference-implementation/resources/streams-utils.js'></script>
<script>
// This is updated till https://github.com/whatwg/streams/commit/4ba861e6f60c248060811830e11271c84b439cc3

// This test is alone here for timing reasons though it should be at streams/reference-implementation/pipe-to.html.
var test24 = async_test('Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the source');
test24.step(function() {
    const timeoutMultiplier = 15;
    var desiredSizes = [];
    var rs = new ReadableStream({
        start: function(c) {
            setTimeout(test24.step_func(function() { enqueue('a'); }), 100 * timeoutMultiplier);
            setTimeout(test24.step_func(function() { enqueue('b'); }), 200 * timeoutMultiplier);
            setTimeout(test24.step_func(function() { enqueue('c'); }), 300 * timeoutMultiplier);
            setTimeout(test24.step_func(function() { enqueue('d'); }), 400 * timeoutMultiplier);
            setTimeout(test24.step_func(function() { c.close(); }), 500 * timeoutMultiplier);

            function enqueue(chunk) {
                c.enqueue(chunk);
                desiredSizes.push(c.desiredSize);
            }
        }
    });

    var chunksGivenToWrite = [];
    var chunksFinishedWriting = [];
    var startPromise = Promise.resolve();
    var ws = new WritableStream({
        start: function() {
            return startPromise;
        },
        write: function(chunk) {
            chunksGivenToWrite.push(chunk);
            return new Promise(test24.step_func(function(resolve) {
                setTimeout(test24.step_func(function() {
                    chunksFinishedWriting.push(chunk);
                    resolve();
                }), 350 * timeoutMultiplier);
            }));
        }
    });

    startPromise.then(test24.step_func(function() {
        rs.pipeTo(ws).then(test24.step_func(function() {
            assert_array_equals(desiredSizes, [1, 1, 0, -1], 'backpressure was correctly exerted at the source');
            assert_array_equals(chunksFinishedWriting, ['a', 'b', 'c', 'd'], 'all chunks were written');
            test24.done();
        }));

        assert_equals(ws.state, 'writable', 'at t = 0 ms, ws should be writable');

        setTimeout(test24.step_func(function() {
            assert_equals(ws.state, 'waiting', 'at t = 125 ms, ws should be waiting');
            assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 125 ms, ws.write should have been called with one chunk');
            assert_array_equals(chunksFinishedWriting, [], 'at t = 125 ms, no chunks should have finished writing');

            // When 'a' (the very first chunk) was enqueued, it was immediately used to fulfill the outstanding read request
            // promise, leaving room in the queue
            assert_array_equals(desiredSizes, [1], 'at t = 125 ms, the one enqueued chunk in rs did not cause backpressure');
        }), 125 * timeoutMultiplier);

        setTimeout(test24.step_func(function() {
            assert_equals(ws.state, 'waiting', 'at t = 225 ms, ws should be waiting');
            assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 225 ms, ws.write should have been called with one chunk');
            assert_array_equals(chunksFinishedWriting, [], 'at t = 225 ms, no chunks should have finished writing');

            // When 'b' was enqueued at 200 ms, the queue was also empty, since immediately after enqueuing 'a' at
            // t = 100 ms, it was dequeued in order to fulfill the read() call that was made at time t = 0.
            assert_array_equals(desiredSizes, [1, 1], 'at t = 225 ms, the two enqueued chunks in rs did not cause backpressure');
        }), 225 * timeoutMultiplier);

        setTimeout(test24.step_func(function() {
            assert_equals(ws.state, 'waiting', 'at t = 325 ms, ws should be waiting');
            assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 325 ms, ws.write should have been called with one chunk');
            assert_array_equals(chunksFinishedWriting, [], 'at t = 325 ms, no chunks should have finished writing');

            // When 'c' was enqueued at 300 ms, the queue was again empty, since at time t = 200 ms when 'b' was enqueued,
            // it was immediately dequeued in order to fulfill the second read() call that was made at time t = 0.
            // However, this time there was no pending read request to whisk it away, so after the enqueue desired size is 0.
            assert_array_equals(desiredSizes, [1, 1, 0], 'at t = 325 ms, the three enqueued chunks in rs did not cause backpressure');
        }), 325 * timeoutMultiplier);

        setTimeout(test24.step_func(function() {
            assert_equals(ws.state, 'waiting', 'at t = 425 ms, ws should be waiting');
            assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 425 ms, ws.write should have been called with one chunk');
            assert_array_equals(chunksFinishedWriting, [], 'at t = 425 ms, no chunks should have finished writing');

            // When 'd' was enqueued at 400 ms, the queue was *not* empty. 'c' was still in it, since the write() of 'b' will
            // not finish until t = 100 ms + 350 ms = 450 ms. Thus backpressure should have been exerted.
            assert_array_equals(desiredSizes, [1, 1, 0, -1], 'at t = 425 ms, the fourth enqueued chunks in rs did cause backpressure');
        }), 425 * timeoutMultiplier);

        setTimeout(test24.step_func(function() {
            assert_equals(ws.state, 'waiting', 'at t = 475 ms, ws should be waiting');
            assert_array_equals(chunksGivenToWrite, ['a', 'b'], 'at t = 475 ms, ws.write should have been called with two chunks');
            assert_array_equals(chunksFinishedWriting, ['a'], 'at t = 475 ms, one chunk should have finished writing');
        }), 475 * timeoutMultiplier);
    }));
});
</script>
