mirror of
https://github.com/pion/webrtc.git
synced 2025-09-26 19:21:12 +08:00
Add tests for WASM bindings
The tests are run in a Node.js environment, and this does not include any browser tests. This requires the wrtc package from npm as well as a shim which adds portions of the WebRTC API to the global scope. Some tests introduced here can be combined when differences between the Go API and the WASM bindings are addressed and as missing features are added to the WASM bindings. We can and should add more tests in the future to improve test coverage. This should be considered the minimum number of tests reuqired to ensure basic functionality is working.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@
|
||||
###############
|
||||
bin/
|
||||
vendor/
|
||||
node_modules/
|
||||
|
||||
### Files ###
|
||||
#############
|
||||
|
24
.travis.yml
24
.travis.yml
@@ -1,8 +1,3 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.x" # use the latest Go release
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
@@ -11,8 +6,26 @@ notifications:
|
||||
slack:
|
||||
secure: IYjXoe03KykZ3v4GgUwGzfWRepO5DnJdxB87lSQ2IMsF6PBFSc3CaOX3GUclHIlzTdchR+PHj1jtEZZVSkgfp9amZBCcqbJTBOPG1YA6hxOvTpgeWIttMH0cmMxSCuCa4RfkuRH2+UXbjREMJ3ENau2CTMKReyW4Jddh9dREZohVmYuqN6uuBqCndYpt3Lm1Hv+T+vqxwTDdE/q0hwGMiwgvQm7N3K397e1q1mg+o4tMGwqyUIPnEPjaSKcEuOBa8Rqyl96nn+HGZK0zvNqUOxlzeRMM0VBcxe2s+zY/SuLj4OwNl1zEmIfY6Qj70t2cmT3xJvJprB4pCwR7q78b4lfpNu6rqCJPIZG/qDFT+XSuhDCmLlCO/+Uhtu11pgjV8UMNLTKJth+7hurH7oLNb7jYk9VYsiKhs41LICyDjJNzS5yPatF5xj0HOujb6Uh/pfI+9a+IpPSeXv1gBo8H3oWa6TfRhuTUS3Jc48p/jriZmgWgbKa1HKTaY9ENvAdZFfxJdrRg3Y4SKnjZcAPw7ijRIx1oaM3rHYbOTm/dj4ggho7EgTO3k8toQ5PKohrbBG5RERqHJvC47SXDt0fEjeGnAfN7Xtj0Pq8YyaFIj7CmCCGoI//2sWkK3AmjnwIuW0hUMsL3GsED+p0lsu6FX9wysJwy2Z2mTfIX/CXmB6w=
|
||||
|
||||
install:
|
||||
# Manually download and install Go 1.12 instead of using gimme.
|
||||
# It looks like gimme Go causes some errors on go-test for Wasm.
|
||||
- wget -O go.tar.gz https://dl.google.com/go/go1.12.linux-amd64.tar.gz
|
||||
- tar -C ~ -xzf go.tar.gz
|
||||
- rm go.tar.gz
|
||||
- export GOROOT=~/go
|
||||
- export PATH=$GOROOT/bin:$PATH
|
||||
- go version
|
||||
- go env
|
||||
# Install Node 11 (required for WASM tests)
|
||||
- source ~/.nvm/nvm.sh
|
||||
- nvm install 11
|
||||
- node --version
|
||||
- npm i -g yarn
|
||||
- yarn install
|
||||
|
||||
before_install:
|
||||
- sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
|
||||
- wget https://raw.githubusercontent.com/creationix/nvm/v0.31.0/nvm.sh -O ~/.nvm/nvm.sh
|
||||
|
||||
before_script:
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.14.0
|
||||
@@ -22,6 +35,7 @@ script:
|
||||
- golangci-lint run ./...
|
||||
- rm -rf examples # Remove examples, no test coverage for them
|
||||
- go test -coverpkg=$(go list ./... | tr '\n' ',') -coverprofile=cover.out -v -race -covermode=atomic ./...
|
||||
- GOOS=js GOARCH=wasm go test -exec="./test-wasm/go_js_wasm_exec" -v .
|
||||
- goveralls -coverprofile=cover.out -service=travis-ci
|
||||
- bash .github/assert-contributors.sh
|
||||
- bash .github/lint-disallowed-functions-in-library.sh
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
|
248
datachannel_go_test.go
Normal file
248
datachannel_go_test.go
Normal file
@@ -0,0 +1,248 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pions/transport/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGenerateDataChannelID(t *testing.T) {
|
||||
api := NewAPI()
|
||||
|
||||
testCases := []struct {
|
||||
client bool
|
||||
c *PeerConnection
|
||||
result uint16
|
||||
}{
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{}, api: api}, 0},
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{1: nil}, api: api}, 0},
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{0: nil}, api: api}, 2},
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{0: nil, 2: nil}, api: api}, 4},
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{0: nil, 4: nil}, api: api}, 2},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{}, api: api}, 1},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{0: nil}, api: api}, 1},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{1: nil}, api: api}, 3},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{1: nil, 3: nil}, api: api}, 5},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{1: nil, 5: nil}, api: api}, 3},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
id, err := testCase.c.generateDataChannelID(testCase.client)
|
||||
if err != nil {
|
||||
t.Errorf("failed to generate id: %v", err)
|
||||
return
|
||||
}
|
||||
if id != testCase.result {
|
||||
t.Errorf("Wrong id: %d expected %d", id, testCase.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataChannel_Send(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
api := NewAPI()
|
||||
offerPC, answerPC, err := api.newPair()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a PC pair for testing")
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
dc, err := offerPC.CreateDataChannel("data", nil)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a PC pair for testing")
|
||||
}
|
||||
|
||||
assert.True(t, dc.Ordered, "Ordered should be set to true")
|
||||
|
||||
dc.OnOpen(func() {
|
||||
e := dc.SendText("Ping")
|
||||
if e != nil {
|
||||
t.Fatalf("Failed to send string on data channel")
|
||||
}
|
||||
})
|
||||
dc.OnMessage(func(msg DataChannelMessage) {
|
||||
done <- true
|
||||
})
|
||||
|
||||
answerPC.OnDataChannel(func(d *DataChannel) {
|
||||
assert.True(t, d.Ordered, "Ordered should be set to true")
|
||||
|
||||
d.OnMessage(func(msg DataChannelMessage) {
|
||||
e := d.Send([]byte("Pong"))
|
||||
if e != nil {
|
||||
t.Fatalf("Failed to send string on data channel")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
err = signalPair(offerPC, answerPC)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to signal our PC pair for testing")
|
||||
}
|
||||
|
||||
closePair(t, offerPC, answerPC, done)
|
||||
}
|
||||
|
||||
func TestDataChannel_EventHandlers(t *testing.T) {
|
||||
to := test.TimeOut(time.Second * 20)
|
||||
defer to.Stop()
|
||||
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
api := NewAPI()
|
||||
dc := &DataChannel{api: api}
|
||||
|
||||
onOpenCalled := make(chan struct{})
|
||||
onMessageCalled := make(chan struct{})
|
||||
|
||||
// Verify that the noop case works
|
||||
assert.NotPanics(t, func() { dc.onOpen() })
|
||||
|
||||
dc.OnOpen(func() {
|
||||
close(onOpenCalled)
|
||||
})
|
||||
|
||||
dc.OnMessage(func(p DataChannelMessage) {
|
||||
close(onMessageCalled)
|
||||
})
|
||||
|
||||
// Verify that the set handlers are called
|
||||
assert.NotPanics(t, func() { dc.onOpen() })
|
||||
assert.NotPanics(t, func() { dc.onMessage(DataChannelMessage{Data: []byte("o hai")}) })
|
||||
|
||||
// Wait for all handlers to be called
|
||||
<-onOpenCalled
|
||||
<-onMessageCalled
|
||||
}
|
||||
|
||||
func TestDataChannel_MessagesAreOrdered(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
api := NewAPI()
|
||||
dc := &DataChannel{api: api}
|
||||
|
||||
max := 512
|
||||
out := make(chan int)
|
||||
inner := func(msg DataChannelMessage) {
|
||||
// randomly sleep
|
||||
// math/rand a weak RNG, but this does not need to be secure. Ignore with #nosec
|
||||
/* #nosec */
|
||||
randInt, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
||||
/* #nosec */
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get random sleep duration: %s", err)
|
||||
}
|
||||
time.Sleep(time.Duration(randInt.Int64()) * time.Microsecond)
|
||||
s, _ := binary.Varint(msg.Data)
|
||||
out <- int(s)
|
||||
}
|
||||
dc.OnMessage(func(p DataChannelMessage) {
|
||||
inner(p)
|
||||
})
|
||||
|
||||
go func() {
|
||||
for i := 1; i <= max; i++ {
|
||||
buf := make([]byte, 8)
|
||||
binary.PutVarint(buf, int64(i))
|
||||
dc.onMessage(DataChannelMessage{Data: buf})
|
||||
// Change the registered handler a couple of times to make sure
|
||||
// that everything continues to work, we don't lose messages, etc.
|
||||
if i%2 == 0 {
|
||||
hdlr := func(msg DataChannelMessage) {
|
||||
inner(msg)
|
||||
}
|
||||
dc.OnMessage(hdlr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
values := make([]int, 0, max)
|
||||
for v := range out {
|
||||
values = append(values, v)
|
||||
if len(values) == max {
|
||||
close(out)
|
||||
}
|
||||
}
|
||||
|
||||
expected := make([]int, max)
|
||||
for i := 1; i <= max; i++ {
|
||||
expected[i-1] = i
|
||||
}
|
||||
assert.EqualValues(t, expected, values)
|
||||
}
|
||||
|
||||
func TestDataChannelParamters(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
t.Run("MaxPacketLifeTime exchange", func(t *testing.T) {
|
||||
var ordered = true
|
||||
var maxPacketLifeTime uint16 = 3
|
||||
options := &DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
MaxPacketLifeTime: &maxPacketLifeTime,
|
||||
}
|
||||
|
||||
offerPC, answerPC, dc, done := setUpReliabilityParamTest(t, options)
|
||||
|
||||
// Check if parameters are correctly set
|
||||
assert.True(t, dc.Ordered, "Ordered should be set to true")
|
||||
if assert.NotNil(t, dc.MaxPacketLifeTime, "should not be nil") {
|
||||
assert.Equal(t, maxPacketLifeTime, *dc.MaxPacketLifeTime, "should match")
|
||||
}
|
||||
|
||||
answerPC.OnDataChannel(func(d *DataChannel) {
|
||||
// Check if parameters are correctly set
|
||||
assert.True(t, d.Ordered, "Ordered should be set to true")
|
||||
if assert.NotNil(t, d.MaxPacketLifeTime, "should not be nil") {
|
||||
assert.Equal(t, maxPacketLifeTime, *d.MaxPacketLifeTime, "should match")
|
||||
}
|
||||
done <- true
|
||||
})
|
||||
|
||||
closeReliabilityParamTest(t, offerPC, answerPC, done)
|
||||
})
|
||||
|
||||
t.Run("MaxRetransmits exchange", func(t *testing.T) {
|
||||
var ordered = false
|
||||
var maxRetransmits uint16 = 3000
|
||||
options := &DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
MaxRetransmits: &maxRetransmits,
|
||||
}
|
||||
|
||||
offerPC, answerPC, dc, done := setUpReliabilityParamTest(t, options)
|
||||
|
||||
// Check if parameters are correctly set
|
||||
assert.False(t, dc.Ordered, "Ordered should be set to false")
|
||||
if assert.NotNil(t, dc.MaxRetransmits, "should not be nil") {
|
||||
assert.Equal(t, maxRetransmits, *dc.MaxRetransmits, "should match")
|
||||
}
|
||||
|
||||
answerPC.OnDataChannel(func(d *DataChannel) {
|
||||
// Check if parameters are correctly set
|
||||
assert.False(t, d.Ordered, "Ordered should be set to false")
|
||||
if assert.NotNil(t, d.MaxRetransmits, "should not be nil") {
|
||||
assert.Equal(t, maxRetransmits, *d.MaxRetransmits, "should match")
|
||||
}
|
||||
done <- true
|
||||
})
|
||||
|
||||
closeReliabilityParamTest(t, offerPC, answerPC, done)
|
||||
})
|
||||
}
|
@@ -127,13 +127,23 @@ func (d *DataChannel) Label() string {
|
||||
// Ordered represents if the DataChannel is ordered, and false if
|
||||
// out-of-order delivery is allowed.
|
||||
func (d *DataChannel) Ordered() bool {
|
||||
return d.underlying.Get("ordered").Bool()
|
||||
ordered := d.underlying.Get("ordered")
|
||||
if ordered == js.Undefined() {
|
||||
return true // default is true
|
||||
}
|
||||
return ordered.Bool()
|
||||
}
|
||||
|
||||
// MaxPacketLifeTime represents the length of the time window (msec) during
|
||||
// which transmissions and retransmissions may occur in unreliable mode.
|
||||
func (d *DataChannel) MaxPacketLifeTime() *uint16 {
|
||||
return valueToUint16Pointer(d.underlying.Get("maxPacketLifeTime"))
|
||||
if d.underlying.Get("maxPacketLifeTime") != js.Undefined() {
|
||||
return valueToUint16Pointer(d.underlying.Get("maxPacketLifeTime"))
|
||||
} else {
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=696681
|
||||
// Chrome calls this "maxRetransmitTime"
|
||||
return valueToUint16Pointer(d.underlying.Get("maxRetransmitTime"))
|
||||
}
|
||||
}
|
||||
|
||||
// MaxRetransmits represents the maximum number of retransmissions that are
|
||||
|
140
datachannel_js_test.go
Normal file
140
datachannel_js_test.go
Normal file
@@ -0,0 +1,140 @@
|
||||
// +build js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pions/transport/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TODO(albrow): This test can be combined into a single test for both Go and
|
||||
// the WASM binding after the Go API has been updated to make Ordered a method
|
||||
// instead of a struct field.
|
||||
func TestDataChannel_Send(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
offerPC, answerPC, err := newPair()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a PC pair for testing")
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
answerPC.OnDataChannel(func(d *DataChannel) {
|
||||
// Make sure this is the data channel we were looking for. (Not the one
|
||||
// created in signalPair).
|
||||
if d.Label() != "data" {
|
||||
return
|
||||
}
|
||||
d.OnMessage(func(msg DataChannelMessage) {
|
||||
e := d.Send([]byte("Pong"))
|
||||
if e != nil {
|
||||
t.Fatalf("Failed to send string on data channel")
|
||||
}
|
||||
})
|
||||
assert.True(t, d.Ordered(), "Ordered should be set to true")
|
||||
})
|
||||
|
||||
dc, err := offerPC.CreateDataChannel("data", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a PC pair for testing")
|
||||
}
|
||||
|
||||
assert.True(t, dc.Ordered(), "Ordered should be set to true")
|
||||
|
||||
dc.OnOpen(func() {
|
||||
e := dc.SendText("Ping")
|
||||
if e != nil {
|
||||
t.Fatalf("Failed to send string on data channel")
|
||||
}
|
||||
})
|
||||
dc.OnMessage(func(msg DataChannelMessage) {
|
||||
done <- true
|
||||
})
|
||||
|
||||
err = signalPair(offerPC, answerPC)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to signal our PC pair for testing")
|
||||
}
|
||||
|
||||
closePair(t, offerPC, answerPC, done)
|
||||
}
|
||||
|
||||
// TODO(albrow): This test can be combined into a single test for both Go and
|
||||
// the WASM binding after the Go API has been updated to make Ordered and
|
||||
// MaxPacketLifeTime methods instead of struct fields.
|
||||
func TestDataChannelParamters(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
t.Run("MaxPacketLifeTime exchange", func(t *testing.T) {
|
||||
// Note(albrow): See https://github.com/node-webrtc/node-webrtc/issues/492.
|
||||
// There is a bug in the npm wrtc package which causes this test to fail.
|
||||
t.Skip("Skipping because of upstream issue")
|
||||
|
||||
// var ordered = true
|
||||
// var maxPacketLifeTime uint16 = 3
|
||||
// options := &DataChannelInit{
|
||||
// Ordered: &ordered,
|
||||
// MaxPacketLifeTime: &maxPacketLifeTime,
|
||||
// }
|
||||
|
||||
// offerPC, answerPC, dc, done := setUpReliabilityParamTest(t, options)
|
||||
|
||||
// // Check if parameters are correctly set
|
||||
// assert.True(t, dc.Ordered(), "Ordered should be set to true")
|
||||
// if assert.NotNil(t, dc.MaxPacketLifeTime(), "should not be nil") {
|
||||
// assert.Equal(t, maxPacketLifeTime, *dc.MaxPacketLifeTime(), "should match")
|
||||
// }
|
||||
|
||||
// answerPC.OnDataChannel(func(d *DataChannel) {
|
||||
// if d.Label() != "data" {
|
||||
// return
|
||||
// }
|
||||
// // Check if parameters are correctly set
|
||||
// assert.True(t, d.Ordered(), "Ordered should be set to true")
|
||||
// if assert.NotNil(t, d.MaxPacketLifeTime(), "should not be nil") {
|
||||
// assert.Equal(t, maxPacketLifeTime, *d.MaxPacketLifeTime(), "should match")
|
||||
// }
|
||||
// done <- true
|
||||
// })
|
||||
|
||||
// closeReliabilityParamTest(t, offerPC, answerPC, done)
|
||||
})
|
||||
|
||||
t.Run("MaxRetransmits exchange", func(t *testing.T) {
|
||||
var ordered = false
|
||||
var maxRetransmits uint16 = 3000
|
||||
options := &DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
MaxRetransmits: &maxRetransmits,
|
||||
}
|
||||
|
||||
offerPC, answerPC, dc, done := setUpReliabilityParamTest(t, options)
|
||||
|
||||
// Check if parameters are correctly set
|
||||
assert.False(t, dc.Ordered(), "Ordered should be set to false")
|
||||
if assert.NotNil(t, dc.MaxRetransmits(), "should not be nil") {
|
||||
assert.Equal(t, maxRetransmits, *dc.MaxRetransmits(), "should match")
|
||||
}
|
||||
|
||||
answerPC.OnDataChannel(func(d *DataChannel) {
|
||||
// Make sure this is the data channel we were looking for. (Not the one
|
||||
// created in signalPair).
|
||||
if d.Label() != "data" {
|
||||
return
|
||||
}
|
||||
// Check if parameters are correctly set
|
||||
assert.False(t, d.Ordered(), "Ordered should be set to false")
|
||||
if assert.NotNil(t, d.MaxRetransmits(), "should not be nil") {
|
||||
assert.Equal(t, maxRetransmits, *d.MaxRetransmits(), "should match")
|
||||
}
|
||||
done <- true
|
||||
})
|
||||
|
||||
closeReliabilityParamTest(t, offerPC, answerPC, done)
|
||||
})
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
|
@@ -1,22 +1,16 @@
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pions/transport/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func closePair(t *testing.T, pc1, pc2 io.Closer, done chan bool) {
|
||||
var err error
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatalf("Datachannel Send Test Timeout")
|
||||
t.Fatalf("closePair timed out waiting for done signal")
|
||||
case <-done:
|
||||
err = pc1.Close()
|
||||
if err != nil {
|
||||
@@ -29,182 +23,8 @@ func closePair(t *testing.T, pc1, pc2 io.Closer, done chan bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateDataChannelID(t *testing.T) {
|
||||
api := NewAPI()
|
||||
|
||||
testCases := []struct {
|
||||
client bool
|
||||
c *PeerConnection
|
||||
result uint16
|
||||
}{
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{}, api: api}, 0},
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{1: nil}, api: api}, 0},
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{0: nil}, api: api}, 2},
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{0: nil, 2: nil}, api: api}, 4},
|
||||
{true, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{0: nil, 4: nil}, api: api}, 2},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{}, api: api}, 1},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{0: nil}, api: api}, 1},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{1: nil}, api: api}, 3},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{1: nil, 3: nil}, api: api}, 5},
|
||||
{false, &PeerConnection{sctpTransport: api.NewSCTPTransport(nil), dataChannels: map[uint16]*DataChannel{1: nil, 5: nil}, api: api}, 3},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
id, err := testCase.c.generateDataChannelID(testCase.client)
|
||||
if err != nil {
|
||||
t.Errorf("failed to generate id: %v", err)
|
||||
return
|
||||
}
|
||||
if id != testCase.result {
|
||||
t.Errorf("Wrong id: %d expected %d", id, testCase.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataChannel_Send(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
api := NewAPI()
|
||||
offerPC, answerPC, err := api.newPair()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a PC pair for testing")
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
dc, err := offerPC.CreateDataChannel("data", nil)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a PC pair for testing")
|
||||
}
|
||||
|
||||
assert.True(t, dc.Ordered, "Ordered should be set to true")
|
||||
|
||||
dc.OnOpen(func() {
|
||||
e := dc.SendText("Ping")
|
||||
if e != nil {
|
||||
t.Fatalf("Failed to send string on data channel")
|
||||
}
|
||||
})
|
||||
dc.OnMessage(func(msg DataChannelMessage) {
|
||||
done <- true
|
||||
})
|
||||
|
||||
answerPC.OnDataChannel(func(d *DataChannel) {
|
||||
assert.True(t, d.Ordered, "Ordered should be set to true")
|
||||
|
||||
d.OnMessage(func(msg DataChannelMessage) {
|
||||
e := d.Send([]byte("Pong"))
|
||||
if e != nil {
|
||||
t.Fatalf("Failed to send string on data channel")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
err = signalPair(offerPC, answerPC)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to signal our PC pair for testing")
|
||||
}
|
||||
|
||||
closePair(t, offerPC, answerPC, done)
|
||||
}
|
||||
|
||||
func TestDataChannel_EventHandlers(t *testing.T) {
|
||||
to := test.TimeOut(time.Second * 20)
|
||||
defer to.Stop()
|
||||
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
api := NewAPI()
|
||||
dc := &DataChannel{api: api}
|
||||
|
||||
onOpenCalled := make(chan struct{})
|
||||
onMessageCalled := make(chan struct{})
|
||||
|
||||
// Verify that the noop case works
|
||||
assert.NotPanics(t, func() { dc.onOpen() })
|
||||
|
||||
dc.OnOpen(func() {
|
||||
close(onOpenCalled)
|
||||
})
|
||||
|
||||
dc.OnMessage(func(p DataChannelMessage) {
|
||||
close(onMessageCalled)
|
||||
})
|
||||
|
||||
// Verify that the set handlers are called
|
||||
assert.NotPanics(t, func() { dc.onOpen() })
|
||||
assert.NotPanics(t, func() { dc.onMessage(DataChannelMessage{Data: []byte("o hai")}) })
|
||||
|
||||
// Wait for all handlers to be called
|
||||
<-onOpenCalled
|
||||
<-onMessageCalled
|
||||
}
|
||||
|
||||
func TestDataChannel_MessagesAreOrdered(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
api := NewAPI()
|
||||
dc := &DataChannel{api: api}
|
||||
|
||||
max := 512
|
||||
out := make(chan int)
|
||||
inner := func(msg DataChannelMessage) {
|
||||
// randomly sleep
|
||||
// math/rand a weak RNG, but this does not need to be secure. Ignore with #nosec
|
||||
/* #nosec */
|
||||
randInt, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
||||
/* #nosec */
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get random sleep duration: %s", err)
|
||||
}
|
||||
time.Sleep(time.Duration(randInt.Int64()) * time.Microsecond)
|
||||
s, _ := binary.Varint(msg.Data)
|
||||
out <- int(s)
|
||||
}
|
||||
dc.OnMessage(func(p DataChannelMessage) {
|
||||
inner(p)
|
||||
})
|
||||
|
||||
go func() {
|
||||
for i := 1; i <= max; i++ {
|
||||
buf := make([]byte, 8)
|
||||
binary.PutVarint(buf, int64(i))
|
||||
dc.onMessage(DataChannelMessage{Data: buf})
|
||||
// Change the registered handler a couple of times to make sure
|
||||
// that everything continues to work, we don't lose messages, etc.
|
||||
if i%2 == 0 {
|
||||
hdlr := func(msg DataChannelMessage) {
|
||||
inner(msg)
|
||||
}
|
||||
dc.OnMessage(hdlr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
values := make([]int, 0, max)
|
||||
for v := range out {
|
||||
values = append(values, v)
|
||||
if len(values) == max {
|
||||
close(out)
|
||||
}
|
||||
}
|
||||
|
||||
expected := make([]int, max)
|
||||
for i := 1; i <= max; i++ {
|
||||
expected[i-1] = i
|
||||
}
|
||||
assert.EqualValues(t, expected, values)
|
||||
}
|
||||
|
||||
func setUpReliabilityParamTest(t *testing.T, options *DataChannelInit) (*PeerConnection, *PeerConnection, *DataChannel, chan bool) {
|
||||
api := NewAPI()
|
||||
offerPC, answerPC, err := api.newPair()
|
||||
offerPC, answerPC, err := newPair()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a PC pair for testing")
|
||||
}
|
||||
@@ -226,64 +46,3 @@ func closeReliabilityParamTest(t *testing.T, pc1, pc2 *PeerConnection, done chan
|
||||
|
||||
closePair(t, pc1, pc2, done)
|
||||
}
|
||||
|
||||
func TestDataChannelParamters(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
t.Run("MaxPacketLifeTime exchange", func(t *testing.T) {
|
||||
var ordered = true
|
||||
var maxPacketLifeTime uint16 = 3
|
||||
options := &DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
MaxPacketLifeTime: &maxPacketLifeTime,
|
||||
}
|
||||
|
||||
offerPC, answerPC, dc, done := setUpReliabilityParamTest(t, options)
|
||||
|
||||
// Check if parameters are correctly set
|
||||
assert.True(t, dc.Ordered, "Ordered should be set to true")
|
||||
if assert.NotNil(t, dc.MaxPacketLifeTime, "should not be nil") {
|
||||
assert.Equal(t, maxPacketLifeTime, *dc.MaxPacketLifeTime, "should match")
|
||||
}
|
||||
|
||||
answerPC.OnDataChannel(func(d *DataChannel) {
|
||||
// Check if parameters are correctly set
|
||||
assert.True(t, d.Ordered, "Ordered should be set to true")
|
||||
if assert.NotNil(t, d.MaxPacketLifeTime, "should not be nil") {
|
||||
assert.Equal(t, maxPacketLifeTime, *d.MaxPacketLifeTime, "should match")
|
||||
}
|
||||
done <- true
|
||||
})
|
||||
|
||||
closeReliabilityParamTest(t, offerPC, answerPC, done)
|
||||
})
|
||||
|
||||
t.Run("MaxRetransmits exchange", func(t *testing.T) {
|
||||
var ordered = false
|
||||
var maxRetransmits uint16 = 3000
|
||||
options := &DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
MaxRetransmits: &maxRetransmits,
|
||||
}
|
||||
|
||||
offerPC, answerPC, dc, done := setUpReliabilityParamTest(t, options)
|
||||
|
||||
// Check if parameters are correctly set
|
||||
assert.False(t, dc.Ordered, "Ordered should be set to false")
|
||||
if assert.NotNil(t, dc.MaxRetransmits, "should not be nil") {
|
||||
assert.Equal(t, maxRetransmits, *dc.MaxRetransmits, "should match")
|
||||
}
|
||||
|
||||
answerPC.OnDataChannel(func(d *DataChannel) {
|
||||
// Check if parameters are correctly set
|
||||
assert.False(t, d.Ordered, "Ordered should be set to false")
|
||||
if assert.NotNil(t, d.MaxRetransmits, "should not be nil") {
|
||||
assert.Equal(t, maxRetransmits, *d.MaxRetransmits, "should match")
|
||||
}
|
||||
done <- true
|
||||
})
|
||||
|
||||
closeReliabilityParamTest(t, offerPC, answerPC, done)
|
||||
})
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
|
8
package.json
Normal file
8
package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "webrtc",
|
||||
"repository": "git@github.com:pions/webrtc.git",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"wrtc": "^0.3.5"
|
||||
}
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
@@ -7,12 +9,10 @@ import (
|
||||
"github.com/pions/transport/test"
|
||||
)
|
||||
|
||||
// TestPeerConnection_Close is moved to it's on file because the tests
|
||||
// TestPeerConnection_Close is moved to it's own file because the tests
|
||||
// in rtcpeerconnection_test.go are leaky, making the goroutine report useless.
|
||||
|
||||
func TestPeerConnection_Close(t *testing.T) {
|
||||
api := NewAPI()
|
||||
|
||||
// Limit runtime in case of deadlocks
|
||||
lim := test.TimeOut(time.Second * 20)
|
||||
defer lim.Stop()
|
||||
@@ -20,7 +20,7 @@ func TestPeerConnection_Close(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
pcOffer, pcAnswer, err := api.newPair()
|
||||
pcOffer, pcAnswer, err := newPair()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
61
peerconnection_close_js_test.go
Normal file
61
peerconnection_close_js_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// +build js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pions/transport/test"
|
||||
)
|
||||
|
||||
// TestPeerConnection_Close is moved to it's own file because the tests
|
||||
// in rtcpeerconnection_test.go are leaky, making the goroutine report useless.
|
||||
// TODO(albrow): This test can be combined into a single test for both Go and
|
||||
// the WASM binding after the Go API has been updated to make Label a method
|
||||
// instead of a struct field.
|
||||
func TestPeerConnection_Close(t *testing.T) {
|
||||
// Limit runtime in case of deadlocks
|
||||
lim := test.TimeOut(time.Second * 20)
|
||||
defer lim.Stop()
|
||||
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
pcOffer, pcAnswer, err := newPair()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
awaitSetup := make(chan struct{})
|
||||
pcAnswer.OnDataChannel(func(d *DataChannel) {
|
||||
// Make sure this is the data channel we were looking for. (Not the one
|
||||
// created in signalPair).
|
||||
if d.Label() != "data" {
|
||||
return
|
||||
}
|
||||
close(awaitSetup)
|
||||
})
|
||||
|
||||
_, err = pcOffer.CreateDataChannel("data", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = signalPair(pcOffer, pcAnswer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
<-awaitSetup
|
||||
|
||||
err = pcOffer.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = pcAnswer.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
359
peerconnection_go_test.go
Normal file
359
peerconnection_go_test.go
Normal file
@@ -0,0 +1,359 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pions/transport/test"
|
||||
"github.com/pions/webrtc/internal/ice"
|
||||
"github.com/pions/webrtc/internal/mux"
|
||||
"github.com/pions/webrtc/pkg/rtcerr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// newPair creates two new peer connections (an offerer and an answerer) using
|
||||
// the api.
|
||||
func (api *API) newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, err error) {
|
||||
pca, err := api.NewPeerConnection(Configuration{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pcb, err := api.NewPeerConnection(Configuration{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pca, pcb, nil
|
||||
}
|
||||
|
||||
func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) error {
|
||||
offer, err := pcOffer.CreateOffer(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = pcOffer.SetLocalDescription(offer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pcAnswer.SetRemoteDescription(offer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
answer, err := pcAnswer.CreateAnswer(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = pcAnswer.SetLocalDescription(answer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pcOffer.SetRemoteDescription(answer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestNew_Go(t *testing.T) {
|
||||
api := NewAPI()
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.Nil(t, err)
|
||||
|
||||
certificate, err := GenerateCertificate(secretKey)
|
||||
assert.Nil(t, err)
|
||||
|
||||
pc, err := api.NewPeerConnection(Configuration{
|
||||
ICEServers: []ICEServer{
|
||||
{
|
||||
URLs: []string{
|
||||
"stun:stun.l.google.com:19302",
|
||||
"turns:google.de?transport=tcp",
|
||||
},
|
||||
Username: "unittest",
|
||||
Credential: OAuthCredential{
|
||||
MACKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=",
|
||||
AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==",
|
||||
},
|
||||
CredentialType: ICECredentialTypeOauth,
|
||||
},
|
||||
},
|
||||
ICETransportPolicy: ICETransportPolicyRelay,
|
||||
BundlePolicy: BundlePolicyMaxCompat,
|
||||
RTCPMuxPolicy: RTCPMuxPolicyNegotiate,
|
||||
PeerIdentity: "unittest",
|
||||
Certificates: []Certificate{*certificate},
|
||||
ICECandidatePoolSize: 5,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, pc)
|
||||
})
|
||||
t.Run("Failure", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
initialize func() (*PeerConnection, error)
|
||||
expectedErr error
|
||||
}{
|
||||
{func() (*PeerConnection, error) {
|
||||
secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.Nil(t, err)
|
||||
|
||||
certificate, err := NewCertificate(secretKey, x509.Certificate{
|
||||
Version: 2,
|
||||
SerialNumber: big.NewInt(1653),
|
||||
NotBefore: time.Now().AddDate(0, -2, 0),
|
||||
NotAfter: time.Now().AddDate(0, -1, 0),
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
return api.NewPeerConnection(Configuration{
|
||||
Certificates: []Certificate{*certificate},
|
||||
})
|
||||
}, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}},
|
||||
{func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{
|
||||
ICEServers: []ICEServer{
|
||||
{
|
||||
URLs: []string{
|
||||
"stun:stun.l.google.com:19302",
|
||||
"turns:google.de?transport=tcp",
|
||||
},
|
||||
Username: "unittest",
|
||||
},
|
||||
},
|
||||
})
|
||||
}, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredencials}},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
_, err := testCase.initialize()
|
||||
assert.EqualError(t, err, testCase.expectedErr.Error(),
|
||||
"testCase: %d %v", i, testCase,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPeerConnection_SetConfiguration_Go(t *testing.T) {
|
||||
// Note: this test includes all SetConfiguration features that are supported
|
||||
// by Go but not the WASM bindings, namely: ICEServer.Credential,
|
||||
// ICEServer.CredentialType, and Certificates.
|
||||
|
||||
api := NewAPI()
|
||||
|
||||
secretKey1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.Nil(t, err)
|
||||
|
||||
certificate1, err := GenerateCertificate(secretKey1)
|
||||
assert.Nil(t, err)
|
||||
|
||||
secretKey2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.Nil(t, err)
|
||||
|
||||
certificate2, err := GenerateCertificate(secretKey2)
|
||||
assert.Nil(t, err)
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
init func() (*PeerConnection, error)
|
||||
config Configuration
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
init: func() (*PeerConnection, error) {
|
||||
pc, err := api.NewPeerConnection(Configuration{
|
||||
PeerIdentity: "unittest",
|
||||
Certificates: []Certificate{*certificate1},
|
||||
ICECandidatePoolSize: 5,
|
||||
})
|
||||
if err != nil {
|
||||
return pc, err
|
||||
}
|
||||
|
||||
err = pc.SetConfiguration(Configuration{
|
||||
ICEServers: []ICEServer{
|
||||
{
|
||||
URLs: []string{
|
||||
"stun:stun.l.google.com:19302",
|
||||
"turns:google.de?transport=tcp",
|
||||
},
|
||||
Username: "unittest",
|
||||
Credential: OAuthCredential{
|
||||
MACKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=",
|
||||
AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==",
|
||||
},
|
||||
CredentialType: ICECredentialTypeOauth,
|
||||
},
|
||||
},
|
||||
ICETransportPolicy: ICETransportPolicyAll,
|
||||
BundlePolicy: BundlePolicyBalanced,
|
||||
RTCPMuxPolicy: RTCPMuxPolicyRequire,
|
||||
PeerIdentity: "unittest",
|
||||
Certificates: []Certificate{*certificate1},
|
||||
ICECandidatePoolSize: 5,
|
||||
})
|
||||
if err != nil {
|
||||
return pc, err
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
},
|
||||
config: Configuration{},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "update multiple certificates",
|
||||
init: func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{})
|
||||
},
|
||||
config: Configuration{
|
||||
Certificates: []Certificate{*certificate1, *certificate2},
|
||||
},
|
||||
wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates},
|
||||
},
|
||||
{
|
||||
name: "update certificate",
|
||||
init: func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{})
|
||||
},
|
||||
config: Configuration{
|
||||
Certificates: []Certificate{*certificate1},
|
||||
},
|
||||
wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates},
|
||||
},
|
||||
} {
|
||||
pc, err := test.init()
|
||||
if err != nil {
|
||||
t.Errorf("SetConfiguration %q: init failed: %v", test.name, err)
|
||||
}
|
||||
|
||||
err = pc.SetConfiguration(test.config)
|
||||
if got, want := err, test.wantErr; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("SetConfiguration %q: err = %v, want %v", test.name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - This unittest needs to be completed when CreateDataChannel is complete
|
||||
// func TestPeerConnection_CreateDataChannel(t *testing.T) {
|
||||
// pc, err := New(Configuration{})
|
||||
// assert.Nil(t, err)
|
||||
//
|
||||
// _, err = pc.CreateDataChannel("data", &DataChannelInit{
|
||||
//
|
||||
// })
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
func TestPeerConnection_EventHandlers_Go(t *testing.T) {
|
||||
// Note: When testing the Go event handlers we peer into the state a bit more
|
||||
// than what is possible for the environment agnostic (Go or WASM/JavaScript)
|
||||
// EventHandlers test.
|
||||
api := NewAPI()
|
||||
pc, err := api.NewPeerConnection(Configuration{})
|
||||
assert.Nil(t, err)
|
||||
|
||||
onTrackCalled := make(chan bool)
|
||||
onICEConnectionStateChangeCalled := make(chan bool)
|
||||
onDataChannelCalled := make(chan bool)
|
||||
|
||||
// Verify that the noop case works
|
||||
assert.NotPanics(t, func() { pc.onTrack(nil, nil) })
|
||||
assert.NotPanics(t, func() { pc.onICEConnectionStateChange(ice.ConnectionStateNew) })
|
||||
|
||||
pc.OnTrack(func(t *Track, r *RTPReceiver) {
|
||||
onTrackCalled <- true
|
||||
})
|
||||
|
||||
pc.OnICEConnectionStateChange(func(cs ICEConnectionState) {
|
||||
onICEConnectionStateChangeCalled <- true
|
||||
})
|
||||
|
||||
pc.OnDataChannel(func(dc *DataChannel) {
|
||||
onDataChannelCalled <- true
|
||||
})
|
||||
|
||||
// Verify that the handlers deal with nil inputs
|
||||
assert.NotPanics(t, func() { pc.onTrack(nil, nil) })
|
||||
assert.NotPanics(t, func() { go pc.onDataChannelHandler(nil) })
|
||||
|
||||
// Verify that the set handlers are called
|
||||
assert.NotPanics(t, func() { pc.onTrack(&Track{}, &RTPReceiver{}) })
|
||||
assert.NotPanics(t, func() { pc.onICEConnectionStateChange(ice.ConnectionStateNew) })
|
||||
assert.NotPanics(t, func() { go pc.onDataChannelHandler(&DataChannel{api: api}) })
|
||||
|
||||
allTrue := func(vals []bool) bool {
|
||||
for _, val := range vals {
|
||||
if !val {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
assert.True(t, allTrue([]bool{
|
||||
<-onTrackCalled,
|
||||
<-onICEConnectionStateChangeCalled,
|
||||
<-onDataChannelCalled,
|
||||
}))
|
||||
}
|
||||
|
||||
// This test asserts that nothing deadlocks we try to shutdown when DTLS is in flight
|
||||
// We ensure that DTLS is in flight by removing the mux func for it, so all inbound DTLS is lost
|
||||
func TestPeerConnection_ShutdownNoDTLS(t *testing.T) {
|
||||
dtlsMatchFunc := mux.MatchDTLS
|
||||
defer func() {
|
||||
mux.MatchDTLS = dtlsMatchFunc
|
||||
}()
|
||||
|
||||
// Drop all incoming DTLS traffic
|
||||
mux.MatchDTLS = func([]byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
lim := test.TimeOut(time.Second * 10)
|
||||
defer lim.Stop()
|
||||
|
||||
api := NewAPI()
|
||||
offerPC, answerPC, err := api.newPair()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = signalPair(offerPC, answerPC); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
iceComplete := make(chan interface{})
|
||||
answerPC.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
|
||||
if iceState == ICEConnectionStateConnected {
|
||||
time.Sleep(time.Second) // Give time for DTLS to start
|
||||
|
||||
select {
|
||||
case <-iceComplete:
|
||||
default:
|
||||
close(iceComplete)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
<-iceComplete
|
||||
if err = offerPC.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if err = answerPC.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
@@ -5,6 +5,8 @@ package webrtc
|
||||
|
||||
import (
|
||||
"syscall/js"
|
||||
|
||||
"github.com/pions/webrtc/pkg/rtcerr"
|
||||
)
|
||||
|
||||
// PeerConnection represents a WebRTC connection that establishes a
|
||||
@@ -69,13 +71,13 @@ func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) {
|
||||
// fix by keeping a mutex-protected list of all DataChannel references as a
|
||||
// property of this PeerConnection, but at the cost of additional overhead.
|
||||
dataChannel := &DataChannel{
|
||||
underlying: args[0],
|
||||
underlying: args[0].Get("channel"),
|
||||
}
|
||||
go f(dataChannel)
|
||||
return js.Undefined()
|
||||
})
|
||||
pc.onDataChannelHandler = &onDataChannelHandler
|
||||
pc.underlying.Set("onsignalingstatechange", onDataChannelHandler)
|
||||
pc.underlying.Set("ondatachannel", onDataChannelHandler)
|
||||
}
|
||||
|
||||
// OnTrack sets an event handler which is called when remote track
|
||||
@@ -99,6 +101,69 @@ func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnectionState))
|
||||
pc.underlying.Set("oniceconnectionstatechange", onICEConectionStateChangeHandler)
|
||||
}
|
||||
|
||||
func (pc *PeerConnection) checkConfiguration(configuration Configuration) error {
|
||||
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2)
|
||||
if pc.ConnectionState() == PeerConnectionStateClosed {
|
||||
return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
|
||||
}
|
||||
|
||||
existingConfig := pc.GetConfiguration()
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #3)
|
||||
if configuration.PeerIdentity != "" {
|
||||
if configuration.PeerIdentity != existingConfig.PeerIdentity {
|
||||
return &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Enable these checks once Certificates are supported.
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #4)
|
||||
// if len(configuration.Certificates) > 0 {
|
||||
// if len(configuration.Certificates) != len(existingConfiguration.Certificates) {
|
||||
// return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
|
||||
// }
|
||||
|
||||
// for i, certificate := range configuration.Certificates {
|
||||
// if !pc.configuration.Certificates[i].Equals(certificate) {
|
||||
// return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
|
||||
// }
|
||||
// }
|
||||
// pc.configuration.Certificates = configuration.Certificates
|
||||
// }
|
||||
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #5)
|
||||
if configuration.BundlePolicy != BundlePolicy(Unknown) {
|
||||
if configuration.BundlePolicy != existingConfig.BundlePolicy {
|
||||
return &rtcerr.InvalidModificationError{Err: ErrModifyingBundlePolicy}
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #6)
|
||||
if configuration.RTCPMuxPolicy != RTCPMuxPolicy(Unknown) {
|
||||
if configuration.RTCPMuxPolicy != existingConfig.RTCPMuxPolicy {
|
||||
return &rtcerr.InvalidModificationError{Err: ErrModifyingRTCPMuxPolicy}
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #7)
|
||||
if configuration.ICECandidatePoolSize != 0 {
|
||||
if configuration.ICECandidatePoolSize != existingConfig.ICECandidatePoolSize &&
|
||||
pc.LocalDescription() != nil {
|
||||
return &rtcerr.InvalidModificationError{Err: ErrModifyingICECandidatePoolSize}
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11)
|
||||
if len(configuration.ICEServers) > 0 {
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3)
|
||||
for _, server := range configuration.ICEServers {
|
||||
if _, err := server.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetConfiguration updates the configuration of this PeerConnection object.
|
||||
func (pc *PeerConnection) SetConfiguration(configuration Configuration) (err error) {
|
||||
defer func() {
|
||||
@@ -106,6 +171,9 @@ func (pc *PeerConnection) SetConfiguration(configuration Configuration) (err err
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
if err := pc.checkConfiguration(configuration); err != nil {
|
||||
return err
|
||||
}
|
||||
configMap := configurationToValue(configuration)
|
||||
pc.underlying.Call("setConfiguration", configMap)
|
||||
return nil
|
||||
@@ -421,9 +489,10 @@ func iceServersToValue(iceServers []ICEServer) js.Value {
|
||||
|
||||
func iceServerToValue(server ICEServer) js.Value {
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"urls": stringsToValue(server.URLs), // required
|
||||
"username": stringToValueOrUndefined(server.Username),
|
||||
"credential": interfaceToValueOrUndefined(server.Credential),
|
||||
"urls": stringsToValue(server.URLs), // required
|
||||
"username": stringToValueOrUndefined(server.Username),
|
||||
// TODO(albrow): credential is not currently supported.
|
||||
// "credential": interfaceToValueOrUndefined(server.Credential),
|
||||
"credentialType": stringEnumToValueOrUndefined(server.CredentialType.String()),
|
||||
})
|
||||
}
|
||||
@@ -436,7 +505,7 @@ func valueToConfiguration(configValue js.Value) Configuration {
|
||||
ICEServers: valueToICEServers(configValue.Get("iceServers")),
|
||||
ICETransportPolicy: newICETransportPolicy(valueToStringOrZero(configValue.Get("iceTransportPolicy"))),
|
||||
BundlePolicy: newBundlePolicy(valueToStringOrZero(configValue.Get("bundlePolicy"))),
|
||||
RTCPMuxPolicy: newRTCPMuxPolicy(valueToStringOrZero(configValue.Get("bundlePolicy"))),
|
||||
RTCPMuxPolicy: newRTCPMuxPolicy(valueToStringOrZero(configValue.Get("rtcpMuxPolicy"))),
|
||||
PeerIdentity: valueToStringOrZero(configValue.Get("peerIdentity")),
|
||||
ICECandidatePoolSize: valueToUint8OrZero(configValue.Get("iceCandidatePoolSize")),
|
||||
|
||||
@@ -521,9 +590,13 @@ func dataChannelInitToValue(options *DataChannelInit) js.Value {
|
||||
return js.Undefined()
|
||||
}
|
||||
|
||||
maxPacketLifeTime := uint16PointerToValue(options.MaxPacketLifeTime)
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"ordered": boolPointerToValue(options.Ordered),
|
||||
"maxPacketLifeTime": uint16PointerToValue(options.MaxPacketLifeTime),
|
||||
"maxPacketLifeTime": maxPacketLifeTime,
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=696681
|
||||
// Chrome calls this "maxRetransmitTime"
|
||||
"maxRetransmitTime": maxPacketLifeTime,
|
||||
"maxRetransmits": uint16PointerToValue(options.MaxRetransmits),
|
||||
"protocol": stringPointerToValue(options.Protocol),
|
||||
"negotiated": boolPointerToValue(options.Negotiated),
|
||||
|
57
peerconnection_js_test.go
Normal file
57
peerconnection_js_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// +build js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) (err error) {
|
||||
offerChan := make(chan SessionDescription)
|
||||
pcOffer.OnICECandidate(func(candidate *string) {
|
||||
if candidate == nil {
|
||||
offerChan <- *pcOffer.PendingLocalDescription()
|
||||
}
|
||||
})
|
||||
|
||||
// Note(albrow): We need to create a data channel in order to trigger ICE
|
||||
// candidate gathering in the background.
|
||||
if _, err := pcOffer.CreateDataChannel("initial_data_channel", nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offer, err := pcOffer.CreateOffer(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pcOffer.SetLocalDescription(offer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeout := time.After(3 * time.Second)
|
||||
select {
|
||||
case <-timeout:
|
||||
return fmt.Errorf("timed out waiting to receive offer")
|
||||
case offer := <-offerChan:
|
||||
if err := pcAnswer.SetRemoteDescription(offer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
answer, err := pcAnswer.CreateAnswer(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = pcAnswer.SetLocalDescription(answer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pcOffer.SetRemoteDescription(answer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
|
@@ -1,30 +1,24 @@
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pions/transport/test"
|
||||
"github.com/pions/webrtc/internal/ice"
|
||||
"github.com/pions/webrtc/internal/mux"
|
||||
|
||||
"github.com/pions/webrtc/pkg/rtcerr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func (api *API) newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, err error) {
|
||||
pca, err := api.NewPeerConnection(Configuration{})
|
||||
// newPair creates two new peer connections (an offerer and an answerer)
|
||||
// *without* using an api (i.e. using the default settings).
|
||||
func newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, err error) {
|
||||
pca, err := NewPeerConnection(Configuration{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pcb, err := api.NewPeerConnection(Configuration{})
|
||||
pcb, err := NewPeerConnection(Configuration{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -32,131 +26,30 @@ func (api *API) newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, er
|
||||
return pca, pcb, nil
|
||||
}
|
||||
|
||||
func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) error {
|
||||
offer, err := pcOffer.CreateOffer(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = pcOffer.SetLocalDescription(offer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pcAnswer.SetRemoteDescription(offer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
answer, err := pcAnswer.CreateAnswer(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = pcAnswer.SetLocalDescription(answer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pcOffer.SetRemoteDescription(answer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
api := NewAPI()
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.Nil(t, err)
|
||||
|
||||
certificate, err := GenerateCertificate(secretKey)
|
||||
assert.Nil(t, err)
|
||||
|
||||
pc, err := api.NewPeerConnection(Configuration{
|
||||
ICEServers: []ICEServer{
|
||||
{
|
||||
URLs: []string{
|
||||
"stun:stun.l.google.com:19302",
|
||||
"turns:google.de?transport=tcp",
|
||||
},
|
||||
Username: "unittest",
|
||||
Credential: OAuthCredential{
|
||||
MACKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=",
|
||||
AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==",
|
||||
},
|
||||
CredentialType: ICECredentialTypeOauth,
|
||||
pc, err := NewPeerConnection(Configuration{
|
||||
ICEServers: []ICEServer{
|
||||
{
|
||||
URLs: []string{
|
||||
"stun:stun.l.google.com:19302",
|
||||
},
|
||||
Username: "unittest",
|
||||
},
|
||||
ICETransportPolicy: ICETransportPolicyRelay,
|
||||
BundlePolicy: BundlePolicyMaxCompat,
|
||||
RTCPMuxPolicy: RTCPMuxPolicyNegotiate,
|
||||
PeerIdentity: "unittest",
|
||||
Certificates: []Certificate{*certificate},
|
||||
ICECandidatePoolSize: 5,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, pc)
|
||||
})
|
||||
t.Run("Failure", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
initialize func() (*PeerConnection, error)
|
||||
expectedErr error
|
||||
}{
|
||||
{func() (*PeerConnection, error) {
|
||||
secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.Nil(t, err)
|
||||
|
||||
certificate, err := NewCertificate(secretKey, x509.Certificate{
|
||||
Version: 2,
|
||||
SerialNumber: big.NewInt(1653),
|
||||
NotBefore: time.Now().AddDate(0, -2, 0),
|
||||
NotAfter: time.Now().AddDate(0, -1, 0),
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
return api.NewPeerConnection(Configuration{
|
||||
Certificates: []Certificate{*certificate},
|
||||
})
|
||||
}, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}},
|
||||
{func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{
|
||||
ICEServers: []ICEServer{
|
||||
{
|
||||
URLs: []string{
|
||||
"stun:stun.l.google.com:19302",
|
||||
"turns:google.de?transport=tcp",
|
||||
},
|
||||
Username: "unittest",
|
||||
},
|
||||
},
|
||||
})
|
||||
}, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredencials}},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
_, err := testCase.initialize()
|
||||
assert.EqualError(t, err, testCase.expectedErr.Error(),
|
||||
"testCase: %d %v", i, testCase,
|
||||
)
|
||||
}
|
||||
},
|
||||
ICETransportPolicy: ICETransportPolicyRelay,
|
||||
BundlePolicy: BundlePolicyMaxCompat,
|
||||
RTCPMuxPolicy: RTCPMuxPolicyNegotiate,
|
||||
PeerIdentity: "unittest",
|
||||
ICECandidatePoolSize: 5,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, pc)
|
||||
}
|
||||
|
||||
func TestPeerConnection_SetConfiguration(t *testing.T) {
|
||||
api := NewAPI()
|
||||
|
||||
secretKey1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.Nil(t, err)
|
||||
|
||||
certificate1, err := GenerateCertificate(secretKey1)
|
||||
assert.Nil(t, err)
|
||||
|
||||
secretKey2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.Nil(t, err)
|
||||
|
||||
certificate2, err := GenerateCertificate(secretKey2)
|
||||
assert.Nil(t, err)
|
||||
// Note: These tests don't include ICEServer.Credential,
|
||||
// ICEServer.CredentialType, or Certificates because those are not supported
|
||||
// in the WASM bindings.
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
@@ -167,9 +60,7 @@ func TestPeerConnection_SetConfiguration(t *testing.T) {
|
||||
{
|
||||
name: "valid",
|
||||
init: func() (*PeerConnection, error) {
|
||||
pc, err := api.NewPeerConnection(Configuration{
|
||||
PeerIdentity: "unittest",
|
||||
Certificates: []Certificate{*certificate1},
|
||||
pc, err := NewPeerConnection(Configuration{
|
||||
ICECandidatePoolSize: 5,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -181,21 +72,13 @@ func TestPeerConnection_SetConfiguration(t *testing.T) {
|
||||
{
|
||||
URLs: []string{
|
||||
"stun:stun.l.google.com:19302",
|
||||
"turns:google.de?transport=tcp",
|
||||
},
|
||||
Username: "unittest",
|
||||
Credential: OAuthCredential{
|
||||
MACKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=",
|
||||
AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==",
|
||||
},
|
||||
CredentialType: ICECredentialTypeOauth,
|
||||
},
|
||||
},
|
||||
ICETransportPolicy: ICETransportPolicyAll,
|
||||
BundlePolicy: BundlePolicyBalanced,
|
||||
RTCPMuxPolicy: RTCPMuxPolicyRequire,
|
||||
PeerIdentity: "unittest",
|
||||
Certificates: []Certificate{*certificate1},
|
||||
ICECandidatePoolSize: 5,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -210,7 +93,7 @@ func TestPeerConnection_SetConfiguration(t *testing.T) {
|
||||
{
|
||||
name: "closed connection",
|
||||
init: func() (*PeerConnection, error) {
|
||||
pc, err := api.NewPeerConnection(Configuration{})
|
||||
pc, err := NewPeerConnection(Configuration{})
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = pc.Close()
|
||||
@@ -223,37 +106,17 @@ func TestPeerConnection_SetConfiguration(t *testing.T) {
|
||||
{
|
||||
name: "update PeerIdentity",
|
||||
init: func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{})
|
||||
return NewPeerConnection(Configuration{})
|
||||
},
|
||||
config: Configuration{
|
||||
PeerIdentity: "unittest",
|
||||
},
|
||||
wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity},
|
||||
},
|
||||
{
|
||||
name: "update multiple certificates",
|
||||
init: func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{})
|
||||
},
|
||||
config: Configuration{
|
||||
Certificates: []Certificate{*certificate1, *certificate2},
|
||||
},
|
||||
wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates},
|
||||
},
|
||||
{
|
||||
name: "update certificate",
|
||||
init: func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{})
|
||||
},
|
||||
config: Configuration{
|
||||
Certificates: []Certificate{*certificate1},
|
||||
},
|
||||
wantErr: &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates},
|
||||
},
|
||||
{
|
||||
name: "update BundlePolicy",
|
||||
init: func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{})
|
||||
return NewPeerConnection(Configuration{})
|
||||
},
|
||||
config: Configuration{
|
||||
BundlePolicy: BundlePolicyMaxCompat,
|
||||
@@ -263,7 +126,7 @@ func TestPeerConnection_SetConfiguration(t *testing.T) {
|
||||
{
|
||||
name: "update RTCPMuxPolicy",
|
||||
init: func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{})
|
||||
return NewPeerConnection(Configuration{})
|
||||
},
|
||||
config: Configuration{
|
||||
RTCPMuxPolicy: RTCPMuxPolicyNegotiate,
|
||||
@@ -273,7 +136,7 @@ func TestPeerConnection_SetConfiguration(t *testing.T) {
|
||||
{
|
||||
name: "update ICECandidatePoolSize",
|
||||
init: func() (*PeerConnection, error) {
|
||||
pc, err := api.NewPeerConnection(Configuration{
|
||||
pc, err := NewPeerConnection(Configuration{
|
||||
ICECandidatePoolSize: 0,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -297,7 +160,7 @@ func TestPeerConnection_SetConfiguration(t *testing.T) {
|
||||
{
|
||||
name: "update ICEServers, no TURN credentials",
|
||||
init: func() (*PeerConnection, error) {
|
||||
return api.NewPeerConnection(Configuration{})
|
||||
return NewPeerConnection(Configuration{})
|
||||
},
|
||||
config: Configuration{
|
||||
ICEServers: []ICEServer{
|
||||
@@ -315,20 +178,19 @@ func TestPeerConnection_SetConfiguration(t *testing.T) {
|
||||
} {
|
||||
pc, err := test.init()
|
||||
if err != nil {
|
||||
t.Fatalf("SetConfiguration %q: init failed: %v", test.name, err)
|
||||
t.Errorf("SetConfiguration %q: init failed: %v", test.name, err)
|
||||
}
|
||||
|
||||
err = pc.SetConfiguration(test.config)
|
||||
if got, want := err, test.wantErr; !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("SetConfiguration %q: err = %v, want %v", test.name, got, want)
|
||||
t.Errorf("SetConfiguration %q: err = %v, want %v", test.name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPeerConnection_GetConfiguration(t *testing.T) {
|
||||
api := NewAPI()
|
||||
pc, err := api.NewPeerConnection(Configuration{})
|
||||
assert.Nil(t, err)
|
||||
pc, err := NewPeerConnection(Configuration{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := Configuration{
|
||||
ICEServers: []ICEServer{},
|
||||
@@ -344,41 +206,33 @@ func TestPeerConnection_GetConfiguration(t *testing.T) {
|
||||
assert.Equal(t, expected.ICETransportPolicy, actual.ICETransportPolicy)
|
||||
assert.Equal(t, expected.BundlePolicy, actual.BundlePolicy)
|
||||
assert.Equal(t, expected.RTCPMuxPolicy, actual.RTCPMuxPolicy)
|
||||
assert.NotEqual(t, len(expected.Certificates), len(actual.Certificates))
|
||||
// TODO(albrow): Uncomment this after #513 is fixed.
|
||||
// See: https://github.com/pions/webrtc/issues/513.
|
||||
// assert.Equal(t, len(expected.Certificates), len(actual.Certificates))
|
||||
assert.Equal(t, expected.ICECandidatePoolSize, actual.ICECandidatePoolSize)
|
||||
}
|
||||
|
||||
// TODO - This unittest needs to be completed when CreateDataChannel is complete
|
||||
// func TestPeerConnection_CreateDataChannel(t *testing.T) {
|
||||
// pc, err := New(Configuration{})
|
||||
// assert.Nil(t, err)
|
||||
//
|
||||
// _, err = pc.CreateDataChannel("data", &DataChannelInit{
|
||||
//
|
||||
// })
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// TODO Fix this test
|
||||
const minimalOffer = `v=0
|
||||
o=- 7193157174393298413 2 IN IP4 127.0.0.1
|
||||
o=- 4596489990601351948 2 IN IP4 127.0.0.1
|
||||
s=-
|
||||
t=0 0
|
||||
a=group:BUNDLE video
|
||||
m=video 43858 UDP/TLS/RTP/SAVPF 96
|
||||
c=IN IP4 172.17.0.1
|
||||
a=candidate:3885250869 1 udp 1 127.0.0.1 1 typ host
|
||||
a=ice-ufrag:OgYk
|
||||
a=ice-pwd:G0ka4ts7hRhMLNljuuXzqnOF
|
||||
a=fingerprint:sha-256 D7:06:10:DE:69:66:B1:53:0E:02:33:45:63:F8:AF:78:B2:C7:CE:AF:8E:FD:E5:13:20:50:74:93:CD:B5:C8:69
|
||||
a=setup:active
|
||||
a=mid:video
|
||||
a=sendrecv
|
||||
a=rtpmap:96 VP8/90000
|
||||
a=msid-semantic: WMS
|
||||
m=application 47299 DTLS/SCTP 5000
|
||||
c=IN IP4 192.168.20.129
|
||||
a=candidate:1966762134 1 udp 2122260223 192.168.20.129 47299 typ host generation 0
|
||||
a=candidate:211962667 1 udp 2122194687 10.0.3.1 40864 typ host generation 0
|
||||
a=candidate:1002017894 1 tcp 1518280447 192.168.20.129 0 typ host tcptype active generation 0
|
||||
a=candidate:1109506011 1 tcp 1518214911 10.0.3.1 0 typ host tcptype active generation 0
|
||||
a=ice-ufrag:1/MvHwjAyVf27aLu
|
||||
a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
|
||||
a=ice-options:google-ice
|
||||
a=fingerprint:sha-256 75:74:5A:A6:A4:E5:52:F4:A7:67:4C:01:C7:EE:91:3F:21:3D:A2:E3:53:7B:6F:30:86:F2:30:AA:65:FB:04:24
|
||||
a=setup:actpass
|
||||
a=mid:data
|
||||
a=sctpmap:5000 webrtc-datachannel 1024
|
||||
`
|
||||
|
||||
func TestSetRemoteDescription(t *testing.T) {
|
||||
api := NewAPI()
|
||||
testCases := []struct {
|
||||
desc SessionDescription
|
||||
}{
|
||||
@@ -386,7 +240,7 @@ func TestSetRemoteDescription(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
peerConn, err := api.NewPeerConnection(Configuration{})
|
||||
peerConn, err := NewPeerConnection(Configuration{})
|
||||
if err != nil {
|
||||
t.Errorf("Case %d: got error: %v", i, err)
|
||||
}
|
||||
@@ -398,8 +252,7 @@ func TestSetRemoteDescription(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateOfferAnswer(t *testing.T) {
|
||||
api := NewAPI()
|
||||
offerPeerConn, err := api.NewPeerConnection(Configuration{})
|
||||
offerPeerConn, err := NewPeerConnection(Configuration{})
|
||||
if err != nil {
|
||||
t.Errorf("New PeerConnection: got error: %v", err)
|
||||
}
|
||||
@@ -410,7 +263,7 @@ func TestCreateOfferAnswer(t *testing.T) {
|
||||
if err = offerPeerConn.SetLocalDescription(offer); err != nil {
|
||||
t.Errorf("SetLocalDescription: got error: %v", err)
|
||||
}
|
||||
answerPeerConn, err := api.NewPeerConnection(Configuration{})
|
||||
answerPeerConn, err := NewPeerConnection(Configuration{})
|
||||
if err != nil {
|
||||
t.Errorf("New PeerConnection: got error: %v", err)
|
||||
}
|
||||
@@ -432,98 +285,75 @@ func TestCreateOfferAnswer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPeerConnection_EventHandlers(t *testing.T) {
|
||||
api := NewAPI()
|
||||
pc, err := api.NewPeerConnection(Configuration{})
|
||||
assert.Nil(t, err)
|
||||
pcOffer, err := NewPeerConnection(Configuration{})
|
||||
assert.NoError(t, err)
|
||||
pcAnswer, err := NewPeerConnection(Configuration{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
onTrackCalled := make(chan bool)
|
||||
onICEConnectionStateChangeCalled := make(chan bool)
|
||||
onDataChannelCalled := make(chan bool)
|
||||
// wasCalled is a list of event handlers that were called.
|
||||
wasCalled := []string{}
|
||||
wasCalledMut := &sync.Mutex{}
|
||||
// wg is used to wait for all event handlers to be called.
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(4)
|
||||
|
||||
// Verify that the noop case works
|
||||
assert.NotPanics(t, func() { pc.onTrack(nil, nil) })
|
||||
assert.NotPanics(t, func() { pc.onICEConnectionStateChange(ice.ConnectionStateNew) })
|
||||
// Each sync.Once is used to ensure that we call wg.Done once for each event
|
||||
// handler and don't add multiple entries to wasCalled. The event handlers can
|
||||
// be called more than once in some cases.
|
||||
onceOffererOnICEConnectionStateChange := &sync.Once{}
|
||||
onceOffererOnSignalingStateChange := &sync.Once{}
|
||||
onceAnswererOnICEConnectionStateChange := &sync.Once{}
|
||||
onceAnswererOnSignalingStateChange := &sync.Once{}
|
||||
|
||||
pc.OnTrack(func(t *Track, r *RTPReceiver) {
|
||||
onTrackCalled <- true
|
||||
// Register all the event handlers.
|
||||
pcOffer.OnICEConnectionStateChange(func(ICEConnectionState) {
|
||||
onceOffererOnICEConnectionStateChange.Do(func() {
|
||||
wasCalledMut.Lock()
|
||||
defer wasCalledMut.Unlock()
|
||||
wasCalled = append(wasCalled, "offerer OnICEConnectionStateChange")
|
||||
wg.Done()
|
||||
})
|
||||
})
|
||||
pcOffer.OnSignalingStateChange(func(SignalingState) {
|
||||
onceOffererOnSignalingStateChange.Do(func() {
|
||||
wasCalledMut.Lock()
|
||||
defer wasCalledMut.Unlock()
|
||||
wasCalled = append(wasCalled, "offerer OnSignalingStateChange")
|
||||
wg.Done()
|
||||
})
|
||||
})
|
||||
pcAnswer.OnICEConnectionStateChange(func(ICEConnectionState) {
|
||||
onceAnswererOnICEConnectionStateChange.Do(func() {
|
||||
wasCalledMut.Lock()
|
||||
defer wasCalledMut.Unlock()
|
||||
wasCalled = append(wasCalled, "answerer OnICEConnectionStateChange")
|
||||
wg.Done()
|
||||
})
|
||||
})
|
||||
pcAnswer.OnSignalingStateChange(func(SignalingState) {
|
||||
onceAnswererOnSignalingStateChange.Do(func() {
|
||||
wasCalledMut.Lock()
|
||||
defer wasCalledMut.Unlock()
|
||||
wasCalled = append(wasCalled, "answerer OnSignalingStateChange")
|
||||
wg.Done()
|
||||
})
|
||||
})
|
||||
|
||||
pc.OnICEConnectionStateChange(func(cs ICEConnectionState) {
|
||||
onICEConnectionStateChangeCalled <- true
|
||||
})
|
||||
// Use signalPair to establish a connection between pcOffer and pcAnswer. This
|
||||
// process should trigger the above event handlers.
|
||||
assert.NoError(t, signalPair(pcOffer, pcAnswer))
|
||||
|
||||
pc.OnDataChannel(func(dc *DataChannel) {
|
||||
onDataChannelCalled <- true
|
||||
})
|
||||
|
||||
// Verify that the handlers deal with nil inputs
|
||||
assert.NotPanics(t, func() { pc.onTrack(nil, nil) })
|
||||
assert.NotPanics(t, func() { go pc.onDataChannelHandler(nil) })
|
||||
|
||||
// Verify that the set handlers are called
|
||||
assert.NotPanics(t, func() { pc.onTrack(&Track{}, &RTPReceiver{}) })
|
||||
assert.NotPanics(t, func() { pc.onICEConnectionStateChange(ice.ConnectionStateNew) })
|
||||
assert.NotPanics(t, func() { go pc.onDataChannelHandler(&DataChannel{api: api}) })
|
||||
|
||||
allTrue := func(vals []bool) bool {
|
||||
for _, val := range vals {
|
||||
if !val {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
assert.True(t, allTrue([]bool{
|
||||
<-onTrackCalled,
|
||||
<-onICEConnectionStateChangeCalled,
|
||||
<-onDataChannelCalled,
|
||||
}))
|
||||
}
|
||||
|
||||
// This test asserts that nothing deadlocks we try to shutdown when DTLS is in flight
|
||||
// We ensure that DTLS is in flight by removing the mux func for it, so all inbound DTLS is lost
|
||||
func TestPeerConnection_ShutdownNoDTLS(t *testing.T) {
|
||||
dtlsMatchFunc := mux.MatchDTLS
|
||||
defer func() {
|
||||
mux.MatchDTLS = dtlsMatchFunc
|
||||
// Wait for all of the event handlers to be triggered.
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
// Drop all incoming DTLS traffic
|
||||
mux.MatchDTLS = func([]byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
lim := test.TimeOut(time.Second * 10)
|
||||
defer lim.Stop()
|
||||
|
||||
api := NewAPI()
|
||||
offerPC, answerPC, err := api.newPair()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = signalPair(offerPC, answerPC); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
iceComplete := make(chan interface{})
|
||||
answerPC.OnICEConnectionStateChange(func(iceState ICEConnectionState) {
|
||||
if iceState == ICEConnectionStateConnected {
|
||||
time.Sleep(time.Second) // Give time for DTLS to start
|
||||
|
||||
select {
|
||||
case <-iceComplete:
|
||||
default:
|
||||
close(iceComplete)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
<-iceComplete
|
||||
if err = offerPC.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if err = answerPC.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
timeout := time.After(5 * time.Second)
|
||||
select {
|
||||
case <-done:
|
||||
break
|
||||
case <-timeout:
|
||||
t.Fatalf("timed out waiting for one or more events handlers to be called (these *were* called: %+v)", wasCalled)
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
|
27
test-wasm/LICENSE
Normal file
27
test-wasm/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
16
test-wasm/go_js_wasm_exec
Executable file
16
test-wasm/go_js_wasm_exec
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2018 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.
|
||||
# Modified work copyright 2019 Alex Browne.
|
||||
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
while [ -h "$SOURCE" ]; do
|
||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||
SOURCE="$(readlink "$SOURCE")"
|
||||
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
|
||||
done
|
||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||
|
||||
# We changed this line to require our node_shim.js.
|
||||
exec node --require=./test-wasm/node_shim.js "$(go env GOROOT)/misc/wasm/wasm_exec.js" "$@"
|
10
test-wasm/node_shim.js
Normal file
10
test-wasm/node_shim.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// This file adds RTCPeerConnection to the global context, making Node.js more
|
||||
// closely match the browser API for WebRTC.
|
||||
|
||||
const wrtc = require("wrtc");
|
||||
|
||||
global.window = {
|
||||
RTCPeerConnection: wrtc.RTCPeerConnection
|
||||
};
|
||||
|
||||
global.RTCPeerConnection = wrtc.RTCPeerConnection;
|
@@ -1,3 +1,5 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
|
774
yarn.lock
Normal file
774
yarn.lock
Normal file
@@ -0,0 +1,774 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
abbrev@1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||
|
||||
ansi-regex@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
|
||||
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
|
||||
|
||||
ansi-regex@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
|
||||
|
||||
aproba@^1.0.3:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
|
||||
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
|
||||
|
||||
are-we-there-yet@~1.1.2:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
|
||||
integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
|
||||
dependencies:
|
||||
delegates "^1.0.0"
|
||||
readable-stream "^2.0.6"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
camelcase@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
|
||||
integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
|
||||
|
||||
canvas@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.3.1.tgz#da0c8a916505aa34f9365d6b77d28b969241bfd0"
|
||||
integrity sha512-jSxwf4V9AGD6t6yBC600xFZKjrfKTR0T0RUNlX/AODs/ifrfJHIQjFEK8iF2euNy6z7K3GNv82DJgTjYZZktqA==
|
||||
dependencies:
|
||||
nan "^2.12.1"
|
||||
node-pre-gyp "^0.11.0"
|
||||
|
||||
chownr@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494"
|
||||
integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==
|
||||
|
||||
cliui@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
|
||||
integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
|
||||
dependencies:
|
||||
string-width "^1.0.1"
|
||||
strip-ansi "^3.0.1"
|
||||
wrap-ansi "^2.0.0"
|
||||
|
||||
code-point-at@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
debug@^2.1.2:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
decamelize@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||
|
||||
deep-extend@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
|
||||
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
|
||||
|
||||
delegates@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
|
||||
|
||||
detect-libc@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
||||
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
|
||||
|
||||
error-ex@^1.2.0:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
||||
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
|
||||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
find-up@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
|
||||
integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=
|
||||
dependencies:
|
||||
path-exists "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
fs-minipass@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
|
||||
integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==
|
||||
dependencies:
|
||||
minipass "^2.2.1"
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
gauge@~2.7.3:
|
||||
version "2.7.4"
|
||||
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
|
||||
integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
|
||||
dependencies:
|
||||
aproba "^1.0.3"
|
||||
console-control-strings "^1.0.0"
|
||||
has-unicode "^2.0.0"
|
||||
object-assign "^4.1.0"
|
||||
signal-exit "^3.0.0"
|
||||
string-width "^1.0.1"
|
||||
strip-ansi "^3.0.1"
|
||||
wide-align "^1.1.0"
|
||||
|
||||
get-caller-file@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
|
||||
integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
|
||||
|
||||
glob@^7.1.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
||||
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
graceful-fs@^4.1.2:
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
|
||||
has-unicode@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
|
||||
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
|
||||
|
||||
hosted-git-info@^2.1.4:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
|
||||
integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==
|
||||
|
||||
iconv-lite@^0.4.4:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
ignore-walk@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8"
|
||||
integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==
|
||||
dependencies:
|
||||
minimatch "^3.0.4"
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
ini@~1.3.0:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
||||
|
||||
invert-kv@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
||||
integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY=
|
||||
|
||||
is-arrayish@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
||||
|
||||
is-fullwidth-code-point@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
|
||||
integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
|
||||
dependencies:
|
||||
number-is-nan "^1.0.0"
|
||||
|
||||
is-fullwidth-code-point@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
|
||||
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
|
||||
|
||||
is-utf8@^0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
|
||||
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
|
||||
|
||||
isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
||||
|
||||
lcid@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
|
||||
integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=
|
||||
dependencies:
|
||||
invert-kv "^1.0.0"
|
||||
|
||||
load-json-file@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
|
||||
integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
parse-json "^2.2.0"
|
||||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
strip-bom "^2.0.0"
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||
|
||||
minimist@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
||||
|
||||
minipass@^2.2.1, minipass@^2.3.4:
|
||||
version "2.3.5"
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
|
||||
integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
|
||||
dependencies:
|
||||
safe-buffer "^5.1.2"
|
||||
yallist "^3.0.0"
|
||||
|
||||
minizlib@^1.1.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614"
|
||||
integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
|
||||
dependencies:
|
||||
minipass "^2.2.1"
|
||||
|
||||
mkdirp@^0.5.0, mkdirp@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
nan@*, nan@^2.12.1, nan@^2.3.2:
|
||||
version "2.12.1"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
|
||||
integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==
|
||||
|
||||
needle@^2.2.1:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e"
|
||||
integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==
|
||||
dependencies:
|
||||
debug "^2.1.2"
|
||||
iconv-lite "^0.4.4"
|
||||
sax "^1.2.4"
|
||||
|
||||
node-cmake@2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/node-cmake/-/node-cmake-2.3.2.tgz#e0fbc54b11405b07705e4d6d41865ae95ad289d0"
|
||||
integrity sha1-4PvFSxFAWwdwXk1tQYZa6VrSidA=
|
||||
dependencies:
|
||||
nan "*"
|
||||
which "^1.2.14"
|
||||
yargs "^7.0.2"
|
||||
|
||||
node-pre-gyp@0.11.x, node-pre-gyp@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054"
|
||||
integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==
|
||||
dependencies:
|
||||
detect-libc "^1.0.2"
|
||||
mkdirp "^0.5.1"
|
||||
needle "^2.2.1"
|
||||
nopt "^4.0.1"
|
||||
npm-packlist "^1.1.6"
|
||||
npmlog "^4.0.2"
|
||||
rc "^1.2.7"
|
||||
rimraf "^2.6.1"
|
||||
semver "^5.3.0"
|
||||
tar "^4"
|
||||
|
||||
nopt@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
|
||||
integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
|
||||
dependencies:
|
||||
abbrev "1"
|
||||
osenv "^0.1.4"
|
||||
|
||||
normalize-package-data@^2.3.2:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
|
||||
dependencies:
|
||||
hosted-git-info "^2.1.4"
|
||||
resolve "^1.10.0"
|
||||
semver "2 || 3 || 4 || 5"
|
||||
validate-npm-package-license "^3.0.1"
|
||||
|
||||
npm-bundled@^1.0.1:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
|
||||
integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==
|
||||
|
||||
npm-packlist@^1.1.6:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc"
|
||||
integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==
|
||||
dependencies:
|
||||
ignore-walk "^3.0.1"
|
||||
npm-bundled "^1.0.1"
|
||||
|
||||
npmlog@^4.0.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
|
||||
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
|
||||
dependencies:
|
||||
are-we-there-yet "~1.1.2"
|
||||
console-control-strings "~1.1.0"
|
||||
gauge "~2.7.3"
|
||||
set-blocking "~2.0.0"
|
||||
|
||||
number-is-nan@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
|
||||
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
|
||||
|
||||
object-assign@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
os-homedir@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
||||
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
|
||||
|
||||
os-locale@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
|
||||
integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=
|
||||
dependencies:
|
||||
lcid "^1.0.0"
|
||||
|
||||
os-tmpdir@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
osenv@^0.1.4:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
|
||||
integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
|
||||
dependencies:
|
||||
os-homedir "^1.0.0"
|
||||
os-tmpdir "^1.0.0"
|
||||
|
||||
parse-json@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
|
||||
integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
|
||||
dependencies:
|
||||
error-ex "^1.2.0"
|
||||
|
||||
path-exists@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
|
||||
integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=
|
||||
dependencies:
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
path-parse@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
|
||||
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
|
||||
|
||||
path-type@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
|
||||
integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
pify@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
|
||||
|
||||
pinkie-promise@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
|
||||
integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
|
||||
dependencies:
|
||||
pinkie "^2.0.0"
|
||||
|
||||
pinkie@^2.0.0:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
||||
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
|
||||
integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==
|
||||
|
||||
rc@^1.2.7:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
|
||||
dependencies:
|
||||
deep-extend "^0.6.0"
|
||||
ini "~1.3.0"
|
||||
minimist "^1.2.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
|
||||
read-pkg-up@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
||||
integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=
|
||||
dependencies:
|
||||
find-up "^1.0.0"
|
||||
read-pkg "^1.0.0"
|
||||
|
||||
read-pkg@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
|
||||
integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=
|
||||
dependencies:
|
||||
load-json-file "^1.0.0"
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^1.0.0"
|
||||
|
||||
readable-stream@^2.0.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
require-directory@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
|
||||
|
||||
require-main-filename@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
|
||||
integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
|
||||
|
||||
resolve@^1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba"
|
||||
integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==
|
||||
dependencies:
|
||||
path-parse "^1.0.6"
|
||||
|
||||
rimraf@^2.6.1:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sax@^1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.3.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
|
||||
|
||||
set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||
|
||||
signal-exit@^3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
|
||||
|
||||
spdx-correct@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
|
||||
integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
|
||||
dependencies:
|
||||
spdx-expression-parse "^3.0.0"
|
||||
spdx-license-ids "^3.0.0"
|
||||
|
||||
spdx-exceptions@^2.1.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
|
||||
integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
|
||||
|
||||
spdx-expression-parse@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
|
||||
integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
|
||||
dependencies:
|
||||
spdx-exceptions "^2.1.0"
|
||||
spdx-license-ids "^3.0.0"
|
||||
|
||||
spdx-license-ids@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e"
|
||||
integrity sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==
|
||||
|
||||
string-width@^1.0.1, string-width@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
||||
integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
|
||||
dependencies:
|
||||
code-point-at "^1.0.0"
|
||||
is-fullwidth-code-point "^1.0.0"
|
||||
strip-ansi "^3.0.0"
|
||||
|
||||
"string-width@^1.0.2 || 2":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
|
||||
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
|
||||
dependencies:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^4.0.0"
|
||||
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
|
||||
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
|
||||
dependencies:
|
||||
ansi-regex "^2.0.0"
|
||||
|
||||
strip-ansi@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
|
||||
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
|
||||
dependencies:
|
||||
ansi-regex "^3.0.0"
|
||||
|
||||
strip-bom@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
|
||||
integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=
|
||||
dependencies:
|
||||
is-utf8 "^0.2.0"
|
||||
|
||||
strip-json-comments@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
||||
|
||||
tar@^4:
|
||||
version "4.4.8"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
|
||||
integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
|
||||
dependencies:
|
||||
chownr "^1.1.1"
|
||||
fs-minipass "^1.2.5"
|
||||
minipass "^2.3.4"
|
||||
minizlib "^1.1.1"
|
||||
mkdirp "^0.5.0"
|
||||
safe-buffer "^5.1.2"
|
||||
yallist "^3.0.2"
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
validate-npm-package-license@^3.0.1:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
||||
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
|
||||
dependencies:
|
||||
spdx-correct "^3.0.0"
|
||||
spdx-expression-parse "^3.0.0"
|
||||
|
||||
which-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
|
||||
integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=
|
||||
|
||||
which@^1.2.14:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
wide-align@^1.1.0:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
|
||||
integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
|
||||
dependencies:
|
||||
string-width "^1.0.2 || 2"
|
||||
|
||||
wrap-ansi@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
|
||||
integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
|
||||
dependencies:
|
||||
string-width "^1.0.1"
|
||||
strip-ansi "^3.0.1"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
wrtc@^0.3.5:
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/wrtc/-/wrtc-0.3.5.tgz#c766f17f56c30685595224b5728a231ccf0b00cf"
|
||||
integrity sha512-kzrZpQIrMfcX6qDpDeOyUHm61I9ku6BPEH6PU1Y6+SULQj3yAxqrUCk4PIrqN7ED4OFmk48jk6Qo8oh8ZDepqg==
|
||||
dependencies:
|
||||
nan "^2.3.2"
|
||||
node-cmake "2.3.2"
|
||||
node-pre-gyp "0.11.x"
|
||||
optionalDependencies:
|
||||
canvas "^2.3.0"
|
||||
|
||||
y18n@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
|
||||
integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
|
||||
|
||||
yallist@^3.0.0, yallist@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
|
||||
integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
|
||||
|
||||
yargs-parser@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
|
||||
integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=
|
||||
dependencies:
|
||||
camelcase "^3.0.0"
|
||||
|
||||
yargs@^7.0.2:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
|
||||
integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=
|
||||
dependencies:
|
||||
camelcase "^3.0.0"
|
||||
cliui "^3.2.0"
|
||||
decamelize "^1.1.1"
|
||||
get-caller-file "^1.0.1"
|
||||
os-locale "^1.4.0"
|
||||
read-pkg-up "^1.0.1"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^1.0.1"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^1.0.2"
|
||||
which-module "^1.0.0"
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^5.0.0"
|
Reference in New Issue
Block a user