使用 Golang exec 命令 运行 时,Firebase 模拟器不会停止
Firebase emulator does not stop when run with Golang exec command
我正在使用此 article 中的信息来使用 firebase 模拟器为 Firestore 构建测试。
模拟器正确启动,测试 运行,但是尽管 SIGKILL(我尝试了其他信号)模拟器在测试完成后仍未清理。
这是我的 main_test.go
的样子:
package main
import (
"fmt"
"io"
"log"
"os"
"os/exec"
"strings"
"syscall"
"testing"
"time"
)
func TestMain(m *testing.M) {
// command to start firestore emulator
cmd := exec.Command("firebase", "emulators:start")
// this makes it killable
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
// we need to capture it's output to know when it's started
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
defer stdout.Close()
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
var result int
defer func() {
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
os.Exit(result)
}()
started := make(chan bool)
go func() {
buf := make([]byte, 512, 512)
for {
n, err := stdout.Read(buf[:])
if err != nil {
if err == io.EOF {
break
}
log.Fatalf("reading stdout %v", err)
}
if n > 0 {
d := string(buf[:n])
// only required if we want to see the emulator output
fmt.Printf("%s", d)
// checking for the message that it's started
if strings.Contains(d, "All emulators ready") {
started <- true
break
}
}
}
}()
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
select {
case <-time.After(10 * time.Second):
log.Fatal("Failed to start the command for 10 seconds")
case err := <-done:
log.Fatalf("------\nCommand has finished unexpectedly with error: %v", err)
case <-started:
fmt.Println("--------")
log.Print("Command started successully... running tests")
}
log.Print("BEFORE running tests")
result = m.Run()
time.Sleep(time.Minute) // to simulate that it take some times to run tests
log.Print("AFTER running tests")
}
go test . -v
的输出看起来不错:
i emulators: Starting emulators: firestore
i firestore: Firestore Emulator logging to firestore-debug.log
i ui: Emulator UI logging to ui-debug.log
┌───────────────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! View status and logs at http://localhost:4000 │
└────────────────────────────────────────────────────────────--------
2020/10/13 16:20:29 Command started successully... running tests
2020/10/13 16:20:29 BEFORE running tests
testing: warning: no tests to run
PASS
2020/10/13 16:20:29 AFTER running tests
ok go/src/test (cached) [no tests to run]
但是 firebase 模拟器没有正确清理(如果我再次启动相同的命令,它会失败并抱怨端口 4000 已被使用)。发现以下进程挥之不去:
### snippet from netstat -anp:
tcp 0 0 127.0.0.1:4000 0.0.0.0:* LISTEN 25187/firebase
tcp6 0 0 127.0.0.1:8080 :::* LISTEN 25160/cloud-firesto
### snippet from ps axu:
user 25187 /usr/local/bin/firebase /home/.cache/firebase/emulators/ui-v1.1.1/server.bundle.js
...
user 25160 /home/.cache/firebase/emulators/cloud-firestore-emulator-v1.11.7.jar --launcher_javabase=/usr/local/buildtools/java/jdk11 -Duser.language=en run --host localhost --port 8080 --rules /home/firestore.rules
有什么问题吗?
...或关于如何测试我的主要功能(使用 Firestore 作为后端)的其他想法?
简短回答:Go exec
不会终止子进程。此处有更多详细信息:Why won't Go kill a child process correctly?
根据 here 中的建议,我决定使用:
firebase emulators:exec "go test"
测试完成后正确清除模拟器。
我正在使用此 article 中的信息来使用 firebase 模拟器为 Firestore 构建测试。
模拟器正确启动,测试 运行,但是尽管 SIGKILL(我尝试了其他信号)模拟器在测试完成后仍未清理。
这是我的 main_test.go
的样子:
package main
import (
"fmt"
"io"
"log"
"os"
"os/exec"
"strings"
"syscall"
"testing"
"time"
)
func TestMain(m *testing.M) {
// command to start firestore emulator
cmd := exec.Command("firebase", "emulators:start")
// this makes it killable
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
// we need to capture it's output to know when it's started
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
defer stdout.Close()
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
var result int
defer func() {
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
os.Exit(result)
}()
started := make(chan bool)
go func() {
buf := make([]byte, 512, 512)
for {
n, err := stdout.Read(buf[:])
if err != nil {
if err == io.EOF {
break
}
log.Fatalf("reading stdout %v", err)
}
if n > 0 {
d := string(buf[:n])
// only required if we want to see the emulator output
fmt.Printf("%s", d)
// checking for the message that it's started
if strings.Contains(d, "All emulators ready") {
started <- true
break
}
}
}
}()
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
select {
case <-time.After(10 * time.Second):
log.Fatal("Failed to start the command for 10 seconds")
case err := <-done:
log.Fatalf("------\nCommand has finished unexpectedly with error: %v", err)
case <-started:
fmt.Println("--------")
log.Print("Command started successully... running tests")
}
log.Print("BEFORE running tests")
result = m.Run()
time.Sleep(time.Minute) // to simulate that it take some times to run tests
log.Print("AFTER running tests")
}
go test . -v
的输出看起来不错:
i emulators: Starting emulators: firestore
i firestore: Firestore Emulator logging to firestore-debug.log
i ui: Emulator UI logging to ui-debug.log
┌───────────────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! View status and logs at http://localhost:4000 │
└────────────────────────────────────────────────────────────--------
2020/10/13 16:20:29 Command started successully... running tests
2020/10/13 16:20:29 BEFORE running tests
testing: warning: no tests to run
PASS
2020/10/13 16:20:29 AFTER running tests
ok go/src/test (cached) [no tests to run]
但是 firebase 模拟器没有正确清理(如果我再次启动相同的命令,它会失败并抱怨端口 4000 已被使用)。发现以下进程挥之不去:
### snippet from netstat -anp:
tcp 0 0 127.0.0.1:4000 0.0.0.0:* LISTEN 25187/firebase
tcp6 0 0 127.0.0.1:8080 :::* LISTEN 25160/cloud-firesto
### snippet from ps axu:
user 25187 /usr/local/bin/firebase /home/.cache/firebase/emulators/ui-v1.1.1/server.bundle.js
...
user 25160 /home/.cache/firebase/emulators/cloud-firestore-emulator-v1.11.7.jar --launcher_javabase=/usr/local/buildtools/java/jdk11 -Duser.language=en run --host localhost --port 8080 --rules /home/firestore.rules
有什么问题吗? ...或关于如何测试我的主要功能(使用 Firestore 作为后端)的其他想法?
简短回答:Go exec
不会终止子进程。此处有更多详细信息:Why won't Go kill a child process correctly?
根据 here 中的建议,我决定使用:
firebase emulators:exec "go test"
测试完成后正确清除模拟器。