Initial release.
This commit is contained in:
commit
8c599e8e5c
13 changed files with 3651 additions and 0 deletions
1
test/go/README.md
Normal file
1
test/go/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
The tests in this directory are translated from [Go's tests](https://github.com/golang/go/tree/bad5abf64d76f9c302c084c5f62e6f70920d3c81/test/chan).
|
77
test/go/doubleselect.js
Normal file
77
test/go/doubleselect.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test the situation in which two cases of a select can
|
||||
// both end up running. See http://codereview.appspot.com/180068.
|
||||
|
||||
'use strict'
|
||||
|
||||
const Channel = require('../../lib')
|
||||
|
||||
it(`doubleselect`, async function () {
|
||||
this.timeout(10 * 1000)
|
||||
const iterations = 100000 // number of iterations
|
||||
|
||||
// sender sends a counter to one of four different channels. If two cases both
|
||||
// end up running in the same iteration, the same value will be sent to two
|
||||
// different channels.
|
||||
const sender = async (n, c1, c2, c3, c4) => {
|
||||
for (let i = 0; i < n; i++) {
|
||||
await Channel.select(c1.push(i), c2.push(i), c3.push(i), c4.push(i))
|
||||
}
|
||||
|
||||
c1.close()
|
||||
c2.close()
|
||||
c3.close()
|
||||
c4.close()
|
||||
}
|
||||
|
||||
// mux receives the values from sender and forwards them onto another channel.
|
||||
// It would be simpler to just have sender's four cases all be the same
|
||||
// channel, but this doesn't actually trigger the bug.
|
||||
const mux = async (output, input, done) => {
|
||||
await input.forEach(async (value) => {
|
||||
await output.push(value)
|
||||
})
|
||||
|
||||
await done.push(true)
|
||||
}
|
||||
|
||||
// recver gets a steam of values from the four mux's and checks for
|
||||
// duplicates.
|
||||
const recver = (input) => {
|
||||
const seen = new Map()
|
||||
|
||||
input.forEach((v) => {
|
||||
if (seen.has(v)) {
|
||||
throw new Error(`got duplicate value: ${v}`)
|
||||
}
|
||||
|
||||
seen.set(v, true)
|
||||
})
|
||||
}
|
||||
|
||||
const c1 = Channel()
|
||||
const c2 = Channel()
|
||||
const c3 = Channel()
|
||||
const c4 = Channel()
|
||||
const done = Channel()
|
||||
const cmux = Channel()
|
||||
sender(iterations, c1, c2, c3, c4)
|
||||
mux(cmux, c1, done)
|
||||
mux(cmux, c2, done)
|
||||
mux(cmux, c3, done)
|
||||
mux(cmux, c4, done)
|
||||
|
||||
// We keep the recver because it might catch more bugs in the future.
|
||||
// However, the result of the bug linked to at the top is that we'll
|
||||
// end up panicking with: "throw: bad g->status in ready".
|
||||
recver(cmux)
|
||||
|
||||
await done.shift()
|
||||
await done.shift()
|
||||
await done.shift()
|
||||
await done.shift()
|
||||
cmux.close()
|
||||
})
|
61
test/go/fifo.js
Normal file
61
test/go/fifo.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test that unbuffered channels act as pure fifos.
|
||||
|
||||
'use strict'
|
||||
|
||||
const Channel = require('../../lib')
|
||||
|
||||
it(`fifo`, function () {
|
||||
const N = 10
|
||||
|
||||
const AsynchFifo = async () => {
|
||||
const ch = Channel(10)
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
await ch.push(i)
|
||||
}
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (await ch.shift() !== i) {
|
||||
throw new Error(`bad receive`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Chain = async (ch, val, input, output) => {
|
||||
await input.shift()
|
||||
|
||||
if (await ch.shift() !== val) {
|
||||
throw new Error(val)
|
||||
}
|
||||
|
||||
await output.push(1)
|
||||
}
|
||||
|
||||
// thread together a daisy chain to read the elements in sequence
|
||||
const SynchFifo = async () => {
|
||||
const ch = Channel()
|
||||
let input = Channel()
|
||||
let start = input
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
const output = Channel()
|
||||
Chain(ch, i, input, output)
|
||||
input = output
|
||||
}
|
||||
|
||||
await start.push(0)
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
await ch.push(i)
|
||||
}
|
||||
|
||||
await input.shift()
|
||||
}
|
||||
|
||||
AsynchFifo()
|
||||
SynchFifo()
|
||||
})
|
33
test/go/goroutines.js
Normal file
33
test/go/goroutines.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Torture test for goroutines.
|
||||
// Make a lot of goroutines, threaded together, and tear them down cleanly.
|
||||
|
||||
'use strict'
|
||||
|
||||
const Channel = require('../../lib')
|
||||
|
||||
it(`goroutines`, async function () {
|
||||
const f = async (left, right) => {
|
||||
await left.push(await right.shift())
|
||||
}
|
||||
|
||||
const n = 10000
|
||||
const leftmost = Channel()
|
||||
let right = leftmost
|
||||
let left = leftmost
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
right = Channel()
|
||||
f(left, right)
|
||||
left = right
|
||||
}
|
||||
|
||||
;(async (c) => {
|
||||
await c.push(1)
|
||||
})(right)
|
||||
|
||||
await leftmost.shift()
|
||||
})
|
73
test/go/select.js
Normal file
73
test/go/select.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test simple select.
|
||||
|
||||
'use strict'
|
||||
|
||||
const Channel = require('../../lib')
|
||||
|
||||
it(`select`, async function () {
|
||||
const closed = Channel()
|
||||
closed.close()
|
||||
let counter = 0
|
||||
let shift = 0
|
||||
|
||||
const GetValue = () => {
|
||||
counter++
|
||||
return 1 << shift
|
||||
}
|
||||
|
||||
const Send = async (a, b) => {
|
||||
let done = false
|
||||
let i = 0
|
||||
|
||||
do {
|
||||
switch (await Channel.select(a.push(GetValue()), b.push(GetValue()),
|
||||
closed.shift())) {
|
||||
case a:
|
||||
i++
|
||||
a = Channel()
|
||||
break
|
||||
|
||||
case b:
|
||||
i++
|
||||
b = Channel()
|
||||
break
|
||||
|
||||
default:
|
||||
done = true
|
||||
}
|
||||
|
||||
shift++
|
||||
} while (!done)
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
let a = Channel(1)
|
||||
let b = Channel(1)
|
||||
let v = await Send(a, b)
|
||||
|
||||
if (v !== 2) {
|
||||
throw new Error(`Send returned ${v} !== 2`)
|
||||
}
|
||||
|
||||
const av = await a.shift()
|
||||
const bv = await b.shift()
|
||||
|
||||
if ((av | bv) !== 3) {
|
||||
throw new Error(`bad values ${av} ${bv}`)
|
||||
}
|
||||
|
||||
v = await Send(a, Channel())
|
||||
|
||||
if (v !== 1) {
|
||||
throw new Error(`Send returned ${v} !== 1`)
|
||||
}
|
||||
|
||||
if (counter !== 10) {
|
||||
throw new Error(`counter is ${counter} !== 10`)
|
||||
}
|
||||
})
|
339
test/index.js
Normal file
339
test/index.js
Normal file
|
@ -0,0 +1,339 @@
|
|||
'use strict'
|
||||
|
||||
const assert = require('@nodeguy/assert')
|
||||
const Channel = require('../lib')
|
||||
const stream = require('stream')
|
||||
|
||||
const toArray = async (channel) => {
|
||||
const array = []
|
||||
|
||||
await channel.forEach((item) => {
|
||||
array.push(item)
|
||||
})
|
||||
|
||||
return array
|
||||
}
|
||||
|
||||
describe(`Channel`, function () {
|
||||
it(`allows the use of new`, function () {
|
||||
return new Channel()
|
||||
})
|
||||
|
||||
it(`is frozen`, function () {
|
||||
assert.throws(() => {
|
||||
Channel.frozen = false
|
||||
})
|
||||
})
|
||||
|
||||
it(`creates a frozen object`, function () {
|
||||
assert.throws(() => {
|
||||
Channel().frozen = false
|
||||
})
|
||||
})
|
||||
|
||||
it(`creates a buffered channel`, async function () {
|
||||
const channel = Channel(2)
|
||||
|
||||
;(async () => {
|
||||
assert.equal(await channel.shift(), 0)
|
||||
})()
|
||||
|
||||
await channel.push(0, 1, 2)
|
||||
})
|
||||
|
||||
describe(`from`, function () {
|
||||
it(`iterable`, async function () {
|
||||
assert.deepEqual(await toArray(Channel.from([0, 1, 2])), [0, 1, 2])
|
||||
})
|
||||
|
||||
it(`Node.js's stream.readOnly`, async function () {
|
||||
const readOnly = stream.PassThrough({objectMode: true})
|
||||
readOnly.write(0)
|
||||
readOnly.write(1)
|
||||
readOnly.end(2)
|
||||
assert.deepEqual(await toArray(Channel.from(readOnly)), [0, 1, 2])
|
||||
})
|
||||
})
|
||||
|
||||
it(`of`, async function () {
|
||||
assert.deepEqual(await toArray(Channel.of(0, 1, 2)), [0, 1, 2])
|
||||
})
|
||||
|
||||
describe(`select`, function () {
|
||||
it(`miscellaneous`, async function () {
|
||||
const a = Channel()
|
||||
const b = Channel()
|
||||
|
||||
;(async () => {
|
||||
await b.push(0)
|
||||
await a.push(1)
|
||||
await a.shift()
|
||||
})()
|
||||
|
||||
assert.equal(await Channel.select(a.shift(), b.shift()), b)
|
||||
assert.equal(b.value, 0)
|
||||
assert.equal(await a.shift(), 1)
|
||||
assert.equal(await Channel.select(a.push(0), b.shift()), a)
|
||||
})
|
||||
})
|
||||
|
||||
it(`allows for non-blocking selects`, async function () {
|
||||
const a = Channel()
|
||||
const b = Channel()
|
||||
const nonBlocking = Channel()
|
||||
nonBlocking.close()
|
||||
|
||||
switch (await Channel.select(a.shift(), b.push(0), nonBlocking.shift())) {
|
||||
case a:
|
||||
assert(false)
|
||||
break
|
||||
|
||||
case b:
|
||||
assert(false)
|
||||
break
|
||||
|
||||
default:
|
||||
assert(true)
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`Channel object`, function () {
|
||||
describe(`close`, function () {
|
||||
it(`can't close an already closed channel`, function (done) {
|
||||
const channel = Channel()
|
||||
channel.close()
|
||||
|
||||
channel.close()
|
||||
.then(() => {
|
||||
done(new Error())
|
||||
})
|
||||
.catch((reason) => {
|
||||
assert.deepEqual(
|
||||
reason,
|
||||
new Error(`Can't close an already-closed channel.`)
|
||||
)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`can't push to a closed channel`, async function () {
|
||||
const channel = Channel()
|
||||
channel.close()
|
||||
|
||||
return (async () => {
|
||||
await channel.push(0)
|
||||
})().then(() => {
|
||||
assert(false)
|
||||
}).catch((reason) => {
|
||||
assert.deepEqual(reason, new Error(`Can't push to closed channel.`))
|
||||
})
|
||||
})
|
||||
|
||||
it(`returns 'undefined' immediately from shift`, async function () {
|
||||
const channel = Channel()
|
||||
channel.close()
|
||||
assert.strictEqual(await channel.shift(), undefined)
|
||||
})
|
||||
})
|
||||
|
||||
it(`filter`, async function () {
|
||||
assert.deepEqual(
|
||||
await toArray(Channel.of(0, 1, 2, 3, 4, 5).filter(x => x % 2 !== 0)),
|
||||
[1, 3, 5]
|
||||
)
|
||||
})
|
||||
|
||||
it(`forEach`, async function () {
|
||||
const output = Channel()
|
||||
|
||||
;(async () => {
|
||||
await Channel.of(0, 1, 2).forEach(output.push)
|
||||
output.close()
|
||||
})()
|
||||
|
||||
assert.deepEqual(await toArray(output), [0, 1, 2])
|
||||
})
|
||||
|
||||
it(`join`, async function () {
|
||||
assert.equal(await Channel.of(`a`, `b`, `c`).join(), `a,b,c`)
|
||||
})
|
||||
|
||||
it(`map`, async function () {
|
||||
assert.deepEqual(
|
||||
await toArray(Channel.of(`a`, `b`, `c`).map(x => x.toUpperCase())),
|
||||
[`A`, `B`, `C`]
|
||||
)
|
||||
})
|
||||
|
||||
describe(`push`, function () {
|
||||
it(`with shift`, async function () {
|
||||
const channel = Channel()
|
||||
|
||||
;(async () => {
|
||||
await channel.push(0)
|
||||
})()
|
||||
|
||||
assert.equal(await channel.shift(), 0)
|
||||
})
|
||||
|
||||
describe(`undefined`, function () {
|
||||
it(`outside select`, function () {
|
||||
const channel = Channel()
|
||||
|
||||
return (async () => {
|
||||
await channel.push(undefined)
|
||||
})().then(() => {
|
||||
assert(false)
|
||||
}).catch((reason) => {
|
||||
assert.deepEqual(
|
||||
reason,
|
||||
new TypeError(`Can't push 'undefined' to channel, use close instead.`)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it(`inside select`, function () {
|
||||
const channel = Channel()
|
||||
|
||||
return (async () => {
|
||||
await Channel.select(channel.push(undefined))
|
||||
})().then(() => {
|
||||
assert(false)
|
||||
}).catch((reason) => {
|
||||
assert.deepEqual(
|
||||
reason,
|
||||
new TypeError(`Can't push 'undefined' to channel, use close instead.`)
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it(`returns a frozen promise`, function () {
|
||||
assert.throws(() => {
|
||||
Channel().push(0).frozen = false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it(`readOnly`, async function () {
|
||||
const channel = Channel()
|
||||
const readOnly = channel.readOnly()
|
||||
|
||||
assert.throws(() => {
|
||||
readOnly.close()
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
readOnly.push(0)
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
readOnly.writeOnly()
|
||||
})
|
||||
|
||||
;(async () => {
|
||||
await channel.push(1)
|
||||
})()
|
||||
|
||||
assert.equal(readOnly.readOnly(), readOnly)
|
||||
assert.equal(await readOnly.shift(), 1)
|
||||
assert.equal(readOnly.value, 1)
|
||||
|
||||
assert.throws(() => {
|
||||
readOnly.frozen = false
|
||||
})
|
||||
})
|
||||
|
||||
it(`reduce`, async function () {
|
||||
assert.equal(await Channel.of(0, 1, 2)
|
||||
.reduce((previous, current) => previous + current),
|
||||
3
|
||||
)
|
||||
|
||||
assert.equal(await Channel.of(0, 1, 2)
|
||||
.reduce((previous, current) => previous + current, 10),
|
||||
13
|
||||
)
|
||||
})
|
||||
|
||||
describe(`shift`, function () {
|
||||
it(`with push`, async function () {
|
||||
const channel = Channel()
|
||||
|
||||
;(async () => {
|
||||
await channel.push(0)
|
||||
})()
|
||||
|
||||
assert.equal(await channel.shift(), 0)
|
||||
})
|
||||
|
||||
it(`returns a frozen promise`, function () {
|
||||
assert.throws(() => {
|
||||
Channel().shift().frozen = false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe(`slice`, function () {
|
||||
it(`start`, async function () {
|
||||
assert.deepEqual(await toArray(Channel.of(0, 1, 2).slice(1)), [1, 2])
|
||||
})
|
||||
|
||||
it(`end`, async function () {
|
||||
assert.deepEqual(
|
||||
await toArray(Channel.of(0, 1, 2, 3, 4).slice(1, 4)),
|
||||
[1, 2, 3]
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it(`value`, async function () {
|
||||
const channel = Channel()
|
||||
|
||||
;(async () => {
|
||||
await channel.push(0)
|
||||
})()
|
||||
|
||||
await channel.shift()
|
||||
assert.equal(channel.value, 0)
|
||||
|
||||
assert.throws(() => {
|
||||
channel.value = 1
|
||||
})
|
||||
|
||||
channel.close()
|
||||
await channel.shift()
|
||||
assert.equal(channel.value, undefined)
|
||||
})
|
||||
|
||||
describe(`writeOnly`, function () {
|
||||
it(`provides only write methods`, async function () {
|
||||
const channel = Channel()
|
||||
const writeOnly = channel.writeOnly()
|
||||
|
||||
assert.throws(() => {
|
||||
writeOnly.readOnly()
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
writeOnly.shift()
|
||||
})
|
||||
|
||||
assert.equal(writeOnly.value, undefined)
|
||||
|
||||
;(async () => {
|
||||
await channel.shift()
|
||||
})()
|
||||
|
||||
await writeOnly.push(0)
|
||||
writeOnly.close()
|
||||
|
||||
assert.throws(() => {
|
||||
writeOnly.frozen = false
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue