-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathintegration_test.go
332 lines (288 loc) · 10 KB
/
integration_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build integration,go1.7
package testing
import (
"bytes"
"flag"
"fmt"
"os"
"runtime"
"strings"
"testing"
"text/template"
"time"
"cloud.google.com/go/profiler/proftest"
"golang.org/x/net/context"
"golang.org/x/oauth2/google"
compute "google.golang.org/api/compute/v1"
)
var (
repo = flag.String("repo", "https://github.com/googleapis/cloud-profiler-nodejs.git", "git repo to test")
branch = flag.String("branch", "", "git branch to test")
commit = flag.String("commit", "", "git commit to test")
pr = flag.Int("pr", 0, "git pull request to test")
runOnlyV8CanaryTest = flag.Bool("run_only_v8_canary_test", false, "if true test will be run only with the v8-canary build, otherwise, no tests will be run with v8 canary")
binaryHost = flag.String("binary_host", "", "host from which to download precompiled binaries; if no value is specified, binaries will be built from source.")
runID = strings.Replace(time.Now().Format("2006-01-02-15-04-05.000000-0700"), ".", "-", -1)
benchFinishString = "busybench finished profiling"
errorString = "failed to set up or run the benchmark"
)
const cloudScope = "https://www.googleapis.com/auth/cloud-platform"
const startupTemplate = `
#! /bin/bash
(
# Signal any unexpected error.
trap 'echo "{{.ErrorString}}"' ERR
# Shut down the VM in 5 minutes after this script exits
# to stop accounting the VM for billing and cores quota.
trap "sleep 300 && poweroff" EXIT
retry() {
for i in {1..3}; do
"${@}" && return 0
done
return 1
}
# Fail on any error
set -eo pipefail
# Display commands being run
set -x
# Install git
retry apt-get update >/dev/null
retry apt-get -y -q install git {{if not .BinaryHost}}build-essential{{end}} >/dev/null
# Install desired version of Node.js
retry curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash >/dev/null
export NVM_DIR="$HOME/.nvm" >/dev/null
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" >/dev/null
# nvm install writes to stderr and stdout on successful install, so both are
# redirected.
{{if .NVMMirror}}NVM_NODEJS_ORG_MIRROR={{.NVMMirror}}{{end}} retry nvm install {{.NodeVersion}} &>/dev/null
npm -v
node -v
NODEDIR=$(dirname $(dirname $(which node)))
# Install agent
retry git clone {{.Repo}}
cd cloud-profiler-nodejs
retry git fetch origin {{if .PR}}pull/{{.PR}}/head{{else}}{{.Branch}}{{end}}:pull_branch
git checkout pull_branch
git reset --hard {{.Commit}}
retry npm install --nodedir="$NODEDIR" {{if.BinaryHost}}--fallback-to-build=false --google_cloud_profiler_binary_host_mirror={{.BinaryHost}}{{end}} >/dev/null
# TODO: remove this workaround.
# For v8-canary tests, we need to use the version of NAN on github, which
# contains unreleased fixes which allows the native component to be compiled
# with Node 11.
{{if .NVMMirror}} retry npm install https://github.com/nodejs/nan.git {{end}}
npm run compile
npm pack --nodedir="$NODEDIR" >/dev/null
VERSION=$(node -e "console.log(require('./package.json').version);")
PROFILER="$HOME/cloud-profiler-nodejs/google-cloud-profiler-$VERSION.tgz"
TESTDIR="$HOME/test"
mkdir -p "$TESTDIR"
cp -r "testing/busybench" "$TESTDIR"
cd "$TESTDIR/busybench"
retry npm install node-pre-gyp
{{if .BinaryHost}}
retry npm install --nodedir="$NODEDIR" --fallback-to-build=false --google_cloud_profiler_binary_host_mirror={{.BinaryHost}} "$PROFILER" typescript gts >/dev/null
{{else}}
retry npm install --nodedir="$NODEDIR" --build-from-source=google_cloud_profiler "$PROFILER" typescript gts >/dev/null
{{end}}
npm run compile
# Run benchmark with agent
GCLOUD_PROFILER_LOGLEVEL=5 GAE_SERVICE={{.Service}} node --trace-warnings build/src/busybench.js 600
# Indicate to test that script has finished running
echo "{{.FinishString}}"
# Write output to serial port 2 with timestamp.
) 2>&1 | while read line; do echo "$(date): ${line}"; done >/dev/ttyS1
`
type profileSummary struct {
profileType string
functionName string
sourceFile string
}
type nodeGCETestCase struct {
proftest.InstanceConfig
name string
nodeVersion string
nvmMirror string
wantProfiles []profileSummary
}
func (tc *nodeGCETestCase) initializeStartUpScript(template *template.Template) error {
var buf bytes.Buffer
err := template.Execute(&buf,
struct {
Service string
NodeVersion string
NVMMirror string
Repo string
PR int
Branch string
Commit string
FinishString string
ErrorString string
BinaryHost string
}{
Service: tc.name,
NodeVersion: tc.nodeVersion,
NVMMirror: tc.nvmMirror,
Repo: *repo,
PR: *pr,
Branch: *branch,
Commit: *commit,
FinishString: benchFinishString,
ErrorString: errorString,
BinaryHost: *binaryHost,
})
if err != nil {
return fmt.Errorf("failed to render startup script for %s: %v", tc.name, err)
}
tc.StartupScript = buf.String()
return nil
}
func TestAgentIntegration(t *testing.T) {
projectID := os.Getenv("GCLOUD_TESTS_NODEJS_PROJECT_ID")
if projectID == "" {
t.Fatalf("Getenv(GCLOUD_TESTS_NODEJS_PROJECT_ID) got empty string")
}
zone := os.Getenv("GCLOUD_TESTS_NODEJS_ZONE")
if zone == "" {
t.Fatalf("Getenv(GCLOUD_TESTS_NODEJS_ZONE) got empty string")
}
if *commit == "" {
t.Fatal("commit flag is not set")
}
ctx := context.Background()
client, err := google.DefaultClient(ctx, cloudScope)
if err != nil {
t.Fatalf("failed to get default client: %v", err)
}
computeService, err := compute.New(client)
if err != nil {
t.Fatalf("failed to initialize compute Service: %v", err)
}
template, err := template.New("startupScript").Parse(startupTemplate)
if err != nil {
t.Fatalf("failed to parse startup script template: %v", err)
}
gceTr := proftest.GCETestRunner{
TestRunner: proftest.TestRunner{
Client: client,
},
ComputeService: computeService,
}
wantProfiles := []profileSummary{
{"WALL", "busyLoop", "busybench.ts"},
{"HEAP", "benchmark", "busybench.ts"},
}
testcases := []nodeGCETestCase{
{
InstanceConfig: proftest.InstanceConfig{
ProjectID: projectID,
Zone: zone,
Name: fmt.Sprintf("profiler-test-node6-%s", runID),
MachineType: "n1-standard-1",
},
name: fmt.Sprintf("profiler-test-node6-%s-gce", runID),
wantProfiles: wantProfiles,
nodeVersion: "6",
},
{
InstanceConfig: proftest.InstanceConfig{
ProjectID: projectID,
Zone: zone,
Name: fmt.Sprintf("profiler-test-node8-%s", runID),
MachineType: "n1-standard-1",
},
name: fmt.Sprintf("profiler-test-node8-%s-gce", runID),
wantProfiles: wantProfiles,
nodeVersion: "8",
},
{
InstanceConfig: proftest.InstanceConfig{
ProjectID: projectID,
Zone: zone,
Name: fmt.Sprintf("profiler-test-node10-%s", runID),
MachineType: "n1-standard-1",
},
name: fmt.Sprintf("profiler-test-node10-%s-gce", runID),
wantProfiles: wantProfiles,
nodeVersion: "10",
},
{
InstanceConfig: proftest.InstanceConfig{
ProjectID: projectID,
Zone: zone,
Name: fmt.Sprintf("profiler-test-node11-%s", runID),
MachineType: "n1-standard-1",
},
name: fmt.Sprintf("profiler-test-node11-%s-gce", runID),
wantProfiles: wantProfiles,
nodeVersion: "11",
},
}
if *runOnlyV8CanaryTest {
testcases = []nodeGCETestCase{{
InstanceConfig: proftest.InstanceConfig{
ProjectID: projectID,
Zone: zone,
Name: fmt.Sprintf("profiler-test-v8-canary-%s", runID),
MachineType: "n1-standard-1",
},
name: fmt.Sprintf("profiler-test-v8-canary-%s-gce", runID),
wantProfiles: wantProfiles,
nodeVersion: "node", // install latest version of node
nvmMirror: "https://nodejs.org/download/v8-canary",
}}
}
// Allow test cases to run in parallel.
runtime.GOMAXPROCS(len(testcases))
for _, tc := range testcases {
tc := tc // capture range variable
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if err := tc.initializeStartUpScript(template); err != nil {
t.Fatalf("failed to initialize startup script: %v", err)
}
gceTr.StartInstance(ctx, &tc.InstanceConfig)
defer func() {
if gceTr.DeleteInstance(ctx, &tc.InstanceConfig); err != nil {
t.Fatal(err)
}
}()
timeoutCtx, cancel := context.WithTimeout(ctx, time.Minute*25)
defer cancel()
if err := gceTr.PollForSerialOutput(timeoutCtx, &tc.InstanceConfig, benchFinishString, errorString); err != nil {
t.Fatal(err)
}
timeNow := time.Now()
endTime := timeNow.Format(time.RFC3339)
startTime := timeNow.Add(-1 * time.Hour).Format(time.RFC3339)
for _, wantProfile := range tc.wantProfiles {
pr, err := gceTr.TestRunner.QueryProfiles(tc.ProjectID, tc.name, startTime, endTime, wantProfile.profileType)
if err != nil {
t.Errorf("QueryProfiles(%s, %s, %s, %s, %s) got error: %v", tc.ProjectID, tc.name, startTime, endTime, wantProfile.profileType, err)
continue
}
if wantProfile.sourceFile != "" {
if err := pr.HasFunctionInFile(wantProfile.functionName, wantProfile.sourceFile); err != nil {
t.Errorf("Function %s not found in source file %s in profiles of type %s: %v", wantProfile.functionName, wantProfile.sourceFile, wantProfile.profileType, err)
}
continue
}
if err := pr.HasFunction(wantProfile.functionName); err != nil {
t.Errorf("Function %s not found in profiles of type %s: %v", wantProfile.functionName, wantProfile.profileType, err)
}
}
})
}
}