1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
7	"flag"
8	"fmt"
9	"math/rand"
10	"sync"
11	"testing"
12
13	"github.com/keybase/client/go/libkb"
14	"github.com/stretchr/testify/require"
15	context "golang.org/x/net/context"
16)
17
18var runConc = flag.Bool("conc", false, "run (expensive) concurrency tests")
19
20func TestConcurrentLogin(t *testing.T) {
21	if !*runConc {
22		t.Skip("Skipping ConcurrentLogin test")
23	}
24	tc := SetupEngineTest(t, "login")
25	defer tc.Cleanup()
26
27	u := CreateAndSignupFakeUser(tc, "login")
28
29	var lwg sync.WaitGroup
30	var mwg sync.WaitGroup
31
32	done := make(chan bool)
33
34	for i := 0; i < 10; i++ {
35		lwg.Add(1)
36		go func(index int) {
37			defer lwg.Done()
38			for j := 0; j < 4; j++ {
39				Logout(tc)
40				err := u.Login(tc.G)
41				require.NoError(t, err)
42			}
43			fmt.Printf("logout/login #%d done\n", index)
44		}(i)
45
46		mwg.Add(1)
47		go func(index int) {
48			defer mwg.Done()
49			for {
50				select {
51				case <-done:
52					fmt.Printf("func caller %d done\n", index)
53					return
54				default:
55					_, err := tc.G.ActiveDevice.NIST(context.Background())
56					require.NoError(t, err)
57					tc.G.ActiveDevice.UID()
58					tc.G.ActiveDevice.Valid()
59				}
60			}
61		}(i)
62	}
63
64	lwg.Wait()
65	close(done)
66	mwg.Wait()
67}
68
69// TestConcurrentGetPassphraseStream tries calling logout, login,
70// and GetPassphraseStream to check for race conditions.
71// Use the -race flag to test it.
72func TestConcurrentGetPassphraseStream(t *testing.T) {
73	if !*runConc {
74		t.Skip("Skipping ConcurrentGetPassphraseStream test")
75	}
76	tc := SetupEngineTest(t, "login")
77	defer tc.Cleanup()
78
79	u := CreateAndSignupFakeUser(tc, "login")
80
81	var lwg sync.WaitGroup
82	var mwg sync.WaitGroup
83
84	done := make(chan bool)
85
86	for i := 0; i < 10; i++ {
87		lwg.Add(1)
88		go func(index int) {
89			defer lwg.Done()
90			for j := 0; j < 4; j++ {
91				Logout(tc)
92				err := u.Login(tc.G)
93				require.NoError(t, err)
94			}
95			fmt.Printf("logout/login #%d done\n", index)
96		}(i)
97
98		mwg.Add(1)
99		go func(index int) {
100			defer mwg.Done()
101			for {
102				select {
103				case <-done:
104					fmt.Printf("func caller %d done\n", index)
105					return
106				default:
107					tc.G.ActiveDevice.PassphraseStream()
108				}
109			}
110		}(i)
111	}
112
113	lwg.Wait()
114	close(done)
115	mwg.Wait()
116}
117
118// TestConcurrentLogin tries calling logout, login, and many of
119// the exposed methods in ActiveDevice concurrently.  Use the
120// -race flag to test it.
121func TestConcurrentSignup(t *testing.T) {
122	if !*runConc {
123		t.Skip("Skipping ConcurrentSignup test")
124	}
125	tc := SetupEngineTest(t, "login")
126	defer tc.Cleanup()
127
128	u := CreateAndSignupFakeUser(tc, "login")
129
130	var lwg sync.WaitGroup
131	var mwg sync.WaitGroup
132
133	done := make(chan bool)
134
135	for i := 0; i < 10; i++ {
136		lwg.Add(1)
137		go func(index int) {
138			defer lwg.Done()
139			for j := 0; j < 4; j++ {
140				Logout(tc)
141				err := u.Login(tc.G)
142				require.NoError(t, err)
143				Logout(tc)
144			}
145			fmt.Printf("logout/login #%d done\n", index)
146		}(i)
147
148		mwg.Add(1)
149		go func(index int) {
150			defer mwg.Done()
151			_, err := CreateAndSignupFakeUserSafe(tc.G, "login")
152			require.NoError(t, err)
153			Logout(tc)
154			fmt.Printf("func caller %d done\n", index)
155		}(i)
156	}
157
158	lwg.Wait()
159	close(done)
160	mwg.Wait()
161}
162
163// TestConcurrentGlobals tries to find race conditions in
164// everything in GlobalContext.
165func TestConcurrentGlobals(t *testing.T) {
166	if !*runConc {
167		t.Skip("Skipping ConcurrentGlobals")
168	}
169	tc := SetupEngineTest(t, "login")
170	defer tc.Cleanup()
171
172	fns := []func(*libkb.GlobalContext){
173		genv,
174	}
175	var wg sync.WaitGroup
176	for i := 0; i < 10; i++ {
177		wg.Add(1)
178		go func(index int) {
179			for j := 0; j < 10; j++ {
180				f := fns[rand.Intn(len(fns))]
181				f(tc.G)
182			}
183			wg.Done()
184		}(i)
185	}
186	wg.Wait()
187}
188
189func genv(g *libkb.GlobalContext) {
190	g.Env.GetConfig()
191	g.Env.GetConfigWriter()
192	g.Env.GetCommandLine()
193	cf := libkb.NewJSONConfigFile(g, "")
194	g.Env.SetConfig(cf, cf)
195}
196