From e0ba8b082d52dec2bf2c8e27e593acd2ac477444 Mon Sep 17 00:00:00 2001 From: Lucas Clemente Date: Mon, 13 Mar 2017 22:24:41 +0100 Subject: [PATCH] Replace docker with a directly launched Chrome for integration tests The benefits: - Speed up chrome tests (from 160s to 34s on my machine). - Allow us to remove docker, which caused quite a few headaches. - Gets us a more current Chrome, so now we run tests against all supported versions. - We don't touch the FS anymore. - We no longer depend on sudo for travis, which means we can use their new container-based infrastructure. This also speeds up our tests :) Tests are now implemented in javascript; the server detects when they are complete and kills Chrome. Fixes #417. --- .travis.yml | 7 +- integrationtests/chrome_test.go | 261 ++++---------- .../integrationtests_suite_test.go | 325 +++++++----------- 3 files changed, 204 insertions(+), 389 deletions(-) diff --git a/.travis.yml b/.travis.yml index 627fdc0d..a41a0677 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -sudo: required +dist: trusty addons: hosts: @@ -6,9 +6,6 @@ addons: language: go -services: - - docker - go: - 1.7.x - 1.8.x @@ -30,6 +27,8 @@ before_install: - go get github.com/onsi/gomega - export GOARCH=$TRAVIS_GOARCH - go env # for debugging + - "export DISPLAY=:99.0" + - "Xvfb $DISPLAY &" script: - .travis/script.sh diff --git a/integrationtests/chrome_test.go b/integrationtests/chrome_test.go index 5398cc4a..e1ac8702 100644 --- a/integrationtests/chrome_test.go +++ b/integrationtests/chrome_test.go @@ -2,234 +2,115 @@ package integrationtests import ( "fmt" - "io" "io/ioutil" - "net/http" "os" - "path/filepath" - "strconv" - - "github.com/tebeka/selenium" + "os/exec" + "runtime" + "strings" + "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" ) -const nImgs = 200 -const imgSize = 40 +func getChromePath() string { + if runtime.GOOS == "darwin" { + return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + } + return "google-chrome" +} -func init() { - http.HandleFunc("/tile", func(w http.ResponseWriter, r *http.Request) { - // Small 40x40 png - w.Write([]byte{ - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, - 0x01, 0x03, 0x00, 0x00, 0x00, 0xb6, 0x30, 0x2a, 0x2e, 0x00, 0x00, 0x00, - 0x03, 0x50, 0x4c, 0x54, 0x45, 0x5a, 0xc3, 0x5a, 0xad, 0x38, 0xaa, 0xdb, - 0x00, 0x00, 0x00, 0x0b, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0x63, 0x18, - 0x61, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0xe2, 0xb8, 0x75, 0x22, 0x00, - 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, - }) - }) +func chromeTest(version protocol.VersionNumber, url string, blockUntilDone func()) { + userDataDir, err := ioutil.TempDir("", "quic-go-test-chrome-dir") + Expect(err).NotTo(HaveOccurred()) + defer os.RemoveAll(userDataDir) - http.HandleFunc("/tiles", func(w http.ResponseWriter, r *http.Request) { - io.WriteString(w, "") - for i := 0; i < nImgs; i++ { - fmt.Fprintf(w, ``, i) - } - io.WriteString(w, "") - }) + path := getChromePath() + args := []string{ + "--disable-gpu", + "--no-first-run=true", + "--no-default-browser-check=true", + "--user-data-dir=" + userDataDir, + "--enable-quic=true", + "--no-proxy-server=true", + "--origin-to-force-quic-on=quic.clemente.io:443", + fmt.Sprintf(`--host-resolver-rules=MAP quic.clemente.io:443 localhost:%s`, port), + fmt.Sprintf("--quic-version=QUIC_VERSION_%d", version), + url, + } + utils.Infof("Running chrome: %s '%s'", getChromePath(), strings.Join(args, "' '")) + command := exec.Command(path, args...) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + defer session.Kill() + blockUntilDone() } var _ = Describe("Chrome tests", func() { - // test disabled since it doesn't work with the configurable QUIC version in the server - PIt("does not work with mismatching versions", func() { - versionForUs := protocol.SupportedVersions[0] - versionForChrome := protocol.SupportedVersions[1] - - supportedVersionsBefore := protocol.SupportedVersions - protocol.SupportedVersions = []protocol.VersionNumber{versionForUs} - wd := getWebdriverForVersion(versionForChrome) - - defer func() { - protocol.SupportedVersions = supportedVersionsBefore - wd.Close() - }() - - err := wd.Get("https://quic.clemente.io/hello") - Expect(err).NotTo(HaveOccurred()) - source, err := wd.PageSource() - Expect(err).NotTo(HaveOccurred()) - Expect(source).ToNot(ContainSubstring("Hello, World!\n")) - }) - for i := range protocol.SupportedVersions { version := protocol.SupportedVersions[i] Context(fmt.Sprintf("with quic version %d", version), func() { - var ( - wd selenium.WebDriver - supportedVersionsBefore []protocol.VersionNumber - ) + supportedVersionsBefore := protocol.SupportedVersions BeforeEach(func() { - if version == protocol.Version37 { - Skip("Skipping Chrome test with QUIC version 37") - } - supportedVersionsBefore = protocol.SupportedVersions protocol.SupportedVersions = []protocol.VersionNumber{version} }) - JustBeforeEach(func() { - wd = getWebdriverForVersion(version) - }) - AfterEach(func() { - wd.Close() protocol.SupportedVersions = supportedVersionsBefore }) - It("loads a simple hello world page using quic", func(done Done) { - err := wd.Get("https://quic.clemente.io/hello") - Expect(err).NotTo(HaveOccurred()) - source, err := wd.PageSource() - Expect(err).NotTo(HaveOccurred()) - Expect(source).To(ContainSubstring("Hello, World!\n")) - close(done) - }, 5) - - It("loads a large number of files", func(done Done) { - err := wd.Get("https://quic.clemente.io/tiles") - Expect(err).NotTo(HaveOccurred()) - Eventually(func() error { - imgs, err := wd.FindElements("tag name", "img") - if err != nil { - return err - } - if len(imgs) != nImgs { - return fmt.Errorf("expected number of images to be %d, got %d", nImgs, len(imgs)) - } - for i, img := range imgs { - size, err := img.Size() - if err != nil { - return err - } - if size.Height != imgSize || size.Width != imgSize { - return fmt.Errorf("image %d did not have expected size", i) - } - } - return nil - }, 5).ShouldNot(HaveOccurred()) - close(done) - }, 20) - It("downloads a small file", func() { - dataMan.GenerateData(dataLen) - err := wd.Get("https://quic.clemente.io/data") - Expect(err).NotTo(HaveOccurred()) - Eventually(func() int { return getDownloadSize("data") }, 30, 0.1).Should(Equal(dataLen)) - Expect(getDownloadMD5("data")).To(Equal(dataMan.GetMD5())) - }, 60) + chromeTest( + version, + fmt.Sprintf("https://quic.clemente.io/downloadtest?num=1&len=%d", dataLen), + waitForDone, + ) + }) It("downloads a large file", func() { - dataMan.GenerateData(dataLongLen) - err := wd.Get("https://quic.clemente.io/data") - Expect(err).NotTo(HaveOccurred()) - Eventually(func() int { return getDownloadSize("data") }, 90, 0.5).Should(Equal(dataLongLen)) - Expect(getDownloadMD5("data")).To(Equal(dataMan.GetMD5())) - }, 100) + chromeTest( + version, + fmt.Sprintf("https://quic.clemente.io/downloadtest?num=1&len=%d", dataLongLen), + waitForDone, + ) + }) + + It("loads a large number of files", func() { + chromeTest( + version, + "https://quic.clemente.io/downloadtest?num=4&len=100", + waitForDone, + ) + }) It("uploads a small file", func() { - dataMan.GenerateData(dataLen) - data := dataMan.GetData() - dir, err := ioutil.TempDir("", "quic-upload-src") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(dir) - tmpfn := filepath.Join(dir, "data.dat") - err = ioutil.WriteFile(tmpfn, data, 0777) - Expect(err).ToNot(HaveOccurred()) - copyFileToDocker(tmpfn) - - err = wd.Get("https://quic.clemente.io/uploadform?num=1") - Expect(err).NotTo(HaveOccurred()) - elem, err := wd.FindElement(selenium.ByCSSSelector, "#upload_0") - Expect(err).ToNot(HaveOccurred()) - err = elem.SendKeys("/home/seluser/data.dat") - Expect(err).ToNot(HaveOccurred()) - Eventually(func() error { return elem.Submit() }, 30, 0.1).ShouldNot(HaveOccurred()) - - file := filepath.Join(uploadDir, "data.dat") - Expect(getFileSize(file)).To(Equal(dataLen)) - Expect(getFileMD5(file)).To(Equal(dataMan.GetMD5())) + chromeTest( + version, + fmt.Sprintf("https://quic.clemente.io/uploadtest?num=1&len=%d", dataLen), + waitForNUploaded(1), + ) }) It("uploads a large file", func() { - dataMan.GenerateData(dataLongLen) - data := dataMan.GetData() - dir, err := ioutil.TempDir("", "quic-upload-src") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(dir) - tmpfn := filepath.Join(dir, "data.dat") - err = ioutil.WriteFile(tmpfn, data, 0777) - Expect(err).ToNot(HaveOccurred()) - copyFileToDocker(tmpfn) - - err = wd.Get("https://quic.clemente.io/uploadform?num=1") - Expect(err).NotTo(HaveOccurred()) - elem, err := wd.FindElement(selenium.ByCSSSelector, "#upload_0") - Expect(err).ToNot(HaveOccurred()) - err = elem.SendKeys("/home/seluser/data.dat") - Expect(err).ToNot(HaveOccurred()) - Eventually(func() error { return elem.Submit() }, 90, 0.5).ShouldNot(HaveOccurred()) - - file := filepath.Join(uploadDir, "data.dat") - Expect(getFileSize(file)).To(Equal(dataLongLen)) - Expect(getFileMD5(file)).To(Equal(dataMan.GetMD5())) + chromeTest( + version, + fmt.Sprintf("https://quic.clemente.io/uploadtest?num=1&len=%d", dataLongLen), + waitForNUploaded(1), + ) }) - // this test takes a long time because it copies every file into the docker container one by one - // unfortunately, docker doesn't support copying multiple files at once - // see https://github.com/docker/docker/issues/7710 It("uploads many small files", func() { num := protocol.MaxStreamsPerConnection + 20 - - dir, err := ioutil.TempDir("", "quic-upload-src") - Expect(err).ToNot(HaveOccurred()) - defer os.RemoveAll(dir) - - var md5s [][]byte - - for i := 0; i < num; i++ { - dataMan.GenerateData(dataLen) - data := dataMan.GetData() - md5s = append(md5s, dataMan.GetMD5()) - tmpfn := filepath.Join(dir, "data_"+strconv.Itoa(i)+".dat") - err = ioutil.WriteFile(tmpfn, data, 0777) - Expect(err).ToNot(HaveOccurred()) - copyFileToDocker(tmpfn) - } - - err = wd.Get("https://quic.clemente.io/uploadform?num=" + strconv.Itoa(num)) - Expect(err).NotTo(HaveOccurred()) - - for i := 0; i < num; i++ { - var elem selenium.WebElement - elem, err = wd.FindElement(selenium.ByCSSSelector, "#upload_"+strconv.Itoa(i)) - Expect(err).ToNot(HaveOccurred()) - err = elem.SendKeys("/home/seluser/data_" + strconv.Itoa(i) + ".dat") - Expect(err).ToNot(HaveOccurred()) - } - - elem, err := wd.FindElement(selenium.ByCSSSelector, "#form") - Expect(err).ToNot(HaveOccurred()) - Eventually(func() error { return elem.Submit() }, 30, 0.1).ShouldNot(HaveOccurred()) - - for i := 0; i < num; i++ { - file := filepath.Join(uploadDir, "data_"+strconv.Itoa(i)+".dat") - Expect(getFileSize(file)).To(Equal(dataLen)) - Expect(getFileMD5(file)).To(Equal(md5s[i])) - } + chromeTest( + version, + fmt.Sprintf("https://quic.clemente.io/uploadtest?num=%d&len=%d", num, dataLen), + waitForNUploaded(num), + ) }) }) } diff --git a/integrationtests/integrationtests_suite_test.go b/integrationtests/integrationtests_suite_test.go index 3c661f5a..705be2ee 100644 --- a/integrationtests/integrationtests_suite_test.go +++ b/integrationtests/integrationtests_suite_test.go @@ -2,35 +2,27 @@ package integrationtests import ( "bytes" - "crypto/md5" - "encoding/hex" "flag" "fmt" "io" "io/ioutil" "log" - "mime/multipart" "net" "net/http" "os" - "os/exec" - "path" "path/filepath" "runtime" + "strings" + "sync/atomic" "strconv" - "time" "github.com/lucas-clemente/quic-go/h2quic" "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/testdata" - "github.com/tebeka/selenium" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gbytes" - "github.com/onsi/gomega/gexec" "testing" ) @@ -41,17 +33,16 @@ const ( ) var ( - server *h2quic.Server - dataMan dataManager - port string - uploadDir string - clientPath string // path of the quic_client - serverPath string // path of the quic_server + server *h2quic.Server + dataMan dataManager + port string + clientPath string + serverPath string + nFilesUploaded int32 + doneCalled bool logFileName string // the log file set in the ginkgo flags logFile *os.File - - docker *gexec.Session ) func TestIntegration(t *testing.T) { @@ -59,14 +50,7 @@ func TestIntegration(t *testing.T) { RunSpecs(t, "Integration Tests Suite") } -var _ = BeforeSuite(func() { - setupHTTPHandlers() - setupSelenium() -}) - -var _ = AfterSuite(func() { - stopSelenium() -}, 10) +var _ = BeforeSuite(setupHTTPHandlers) // read the logfile command line flag // to set call ginkgo -- -logfile=log.txt @@ -77,14 +61,6 @@ func init() { var _ = BeforeEach(func() { // set custom time format for logs utils.SetLogTimeFormat("15:04:05.000") - - // create a new uploadDir for every test - var err error - uploadDir, err = ioutil.TempDir("", "quic-upload-dest") - Expect(err).ToNot(HaveOccurred()) - err = os.MkdirAll(uploadDir, os.ModeDir|0777) - Expect(err).ToNot(HaveOccurred()) - _, thisfile, _, ok := runtime.Caller(0) if !ok { Fail("Failed to get current path") @@ -93,6 +69,7 @@ var _ = BeforeEach(func() { serverPath = filepath.Join(thisfile, fmt.Sprintf("../../../quic-clients/server-%s-debug", runtime.GOOS)) if len(logFileName) > 0 { + var err error logFile, err = os.Create("./log.txt") Expect(err).ToNot(HaveOccurred()) log.SetOutput(logFile) @@ -105,18 +82,12 @@ var _ = JustBeforeEach(startQuicServer) var _ = AfterEach(func() { stopQuicServer() - // remove uploadDir - if len(uploadDir) < 20 { - panic("uploadDir too short") - } - os.RemoveAll(uploadDir) - - // remove downloaded file in docker container - removeDownload("data") - if len(logFileName) > 0 { _ = logFile.Close() } + + nFilesUploaded = 0 + doneCalled = false }) func setupHTTPHandlers() { @@ -136,6 +107,16 @@ func setupHTTPHandlers() { Expect(err).NotTo(HaveOccurred()) }) + http.HandleFunc("/prdata", func(w http.ResponseWriter, r *http.Request) { + defer GinkgoRecover() + sl := r.URL.Query().Get("len") + l, err := strconv.Atoi(sl) + Expect(err).NotTo(HaveOccurred()) + data := generatePRData(l) + _, err = w.Write(data) + Expect(err).NotTo(HaveOccurred()) + }) + http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() body, err := ioutil.ReadAll(r.Body) @@ -144,46 +125,43 @@ func setupHTTPHandlers() { Expect(err).NotTo(HaveOccurred()) }) - // requires the num GET parameter, e.g. /uploadform?num=2 - // will create num input fields for uploading files - http.HandleFunc("/uploadform", func(w http.ResponseWriter, r *http.Request) { + // Requires the len & num GET parameters, e.g. /uploadtest?len=100&num=1 + http.HandleFunc("/uploadtest", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() - num, err := strconv.Atoi(r.URL.Query().Get("num")) - Expect(err).ToNot(HaveOccurred()) - response := "\n
" - for i := 0; i < num; i++ { - response += "" - } - response += "
" - _, err = io.WriteString(w, response) + response := uploadHTML + response = strings.Replace(response, "LENGTH", r.URL.Query().Get("len"), -1) + response = strings.Replace(response, "NUM", r.URL.Query().Get("num"), -1) + _, err := io.WriteString(w, response) + Expect(err).NotTo(HaveOccurred()) + }) + + // Requires the len & num GET parameters, e.g. /downloadtest?len=100&num=1 + http.HandleFunc("/downloadtest", func(w http.ResponseWriter, r *http.Request) { + defer GinkgoRecover() + response := downloadHTML + response = strings.Replace(response, "LENGTH", r.URL.Query().Get("len"), -1) + response = strings.Replace(response, "NUM", r.URL.Query().Get("num"), -1) + _, err := io.WriteString(w, response) Expect(err).NotTo(HaveOccurred()) }) http.HandleFunc("/uploadhandler", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() - err := r.ParseMultipartForm(100 * (1 << 20)) // max. 100 MB - Expect(err).ToNot(HaveOccurred()) - - count := 0 - for { - var file multipart.File - var handler *multipart.FileHeader - file, handler, err = r.FormFile("uploadfile_" + strconv.Itoa(count)) - if err != nil { - break - } - count++ - f, err2 := os.OpenFile(path.Join(uploadDir, handler.Filename), os.O_WRONLY|os.O_CREATE, 0666) - Expect(err2).ToNot(HaveOccurred()) - io.Copy(f, file) - f.Close() - file.Close() - } - Expect(count).ToNot(BeZero()) // there have been at least one uploaded file in this request - - _, err = io.WriteString(w, "") + l, err := strconv.Atoi(r.URL.Query().Get("len")) Expect(err).NotTo(HaveOccurred()) + + defer r.Body.Close() + actual, err := ioutil.ReadAll(r.Body) + Expect(err).NotTo(HaveOccurred()) + + Expect(bytes.Equal(actual, generatePRData(l))).To(BeTrue()) + + atomic.AddInt32(&nFilesUploaded, 1) + }) + + http.HandleFunc("/done", func(w http.ResponseWriter, r *http.Request) { + doneCalled = true }) } @@ -210,137 +188,94 @@ func stopQuicServer() { Expect(server.Close()).NotTo(HaveOccurred()) } -func setupSelenium() { - var err error - pullCmd := exec.Command("docker", "pull", "lclemente/standalone-chrome:dev") - pull, err := gexec.Start(pullCmd, GinkgoWriter, GinkgoWriter) - Expect(err).NotTo(HaveOccurred()) - // Assuming a download at 10 Mbit/s - Eventually(pull, 10*time.Minute).Should(gexec.Exit(0)) - - dockerCmd := exec.Command( - "docker", - "run", - "-i", - "--rm", - "-p=4444:4444", - "--name", "quic-test-selenium", - "lclemente/standalone-chrome:dev", - ) - docker, err = gexec.Start(dockerCmd, GinkgoWriter, GinkgoWriter) - Expect(err).NotTo(HaveOccurred()) - Eventually(docker.Out, 10).Should(gbytes.Say("Selenium Server is up and running")) +func waitForDone() { + Eventually(func() bool { return doneCalled }, 60).Should(BeTrue()) } -func stopSelenium() { - docker.Interrupt().Wait(10) -} - -func getWebdriverForVersion(version protocol.VersionNumber) selenium.WebDriver { - caps := selenium.Capabilities{ - "browserName": "chrome", - "chromeOptions": map[string]interface{}{ - "args": []string{ - "--enable-quic", - "--no-proxy-server", - "--origin-to-force-quic-on=quic.clemente.io:443", - fmt.Sprintf(`--host-resolver-rules=MAP quic.clemente.io:443 %s:%s`, GetLocalIP(), port), - fmt.Sprintf(`--quic-version=QUIC_VERSION_%d`, version), - }, - }, +func waitForNUploaded(expected int) func() { + return func() { + Eventually(func() int32 { + return atomic.LoadInt32(&nFilesUploaded) + }, 60).Should(BeEquivalentTo(expected)) } - wd, err := selenium.NewRemote(caps, "http://localhost:4444/wd/hub") - Expect(err).NotTo(HaveOccurred()) - return wd } -func GetLocalIP() string { - // First, try finding interface docker0 - i, err := net.InterfaceByName("docker0") - if err == nil { - var addrs []net.Addr - addrs, err = i.Addrs() - Expect(err).NotTo(HaveOccurred()) - return addrs[0].(*net.IPNet).IP.String() - } +const commonJS = ` +var buf = new ArrayBuffer(LENGTH); +var prng = new Uint8Array(buf); +var seed = 1; +for (var i = 0; i < LENGTH; i++) { + // https://en.wikipedia.org/wiki/Lehmer_random_number_generator + seed = seed * 48271 % 2147483647; + prng[i] = seed; +} +` - addrs, err := net.InterfaceAddrs() - Expect(err).NotTo(HaveOccurred()) - for _, address := range addrs { - if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String() - } +const uploadHTML = ` + + + + + +` + +const downloadHTML = ` + + + + + +` -// getFileSize gets the file size of a file on the local file system -func getFileSize(filename string) int { - file, err := os.Open(filename) - Expect(err).ToNot(HaveOccurred()) - fi, err := file.Stat() - Expect(err).ToNot(HaveOccurred()) - return int(fi.Size()) -} - -// getDownloadMD5 gets the md5 sum file of a file in the /home/seluser/Downloads folder in the docker container -func getDownloadMD5(filename string) []byte { - var out bytes.Buffer - cmd := exec.Command("docker", "exec", "-i", "quic-test-selenium", "md5sum", "/home/seluser/Downloads/"+filename) - session, err := gexec.Start(cmd, &out, GinkgoWriter) - Expect(err).NotTo(HaveOccurred()) - Eventually(session, 5).Should(gexec.Exit()) - if session.ExitCode() != 0 { - return nil +// Same as in the JS code, see +// https://en.wikipedia.org/wiki/Lehmer_random_number_generator +func generatePRData(l int) []byte { + res := make([]byte, l) + seed := uint64(1) + for i := 0; i < l; i++ { + seed = seed * 48271 % 2147483647 + res[i] = byte(seed) } - Expect(out.Bytes()).ToNot(BeEmpty()) - res, err := hex.DecodeString(string(out.Bytes()[0:32])) - Expect(err).NotTo(HaveOccurred()) return res } - -// getFileMD5 gets the md5 sum of a file on the local file system -func getFileMD5(filepath string) []byte { - var result []byte - file, err := os.Open(filepath) - Expect(err).ToNot(HaveOccurred()) - defer file.Close() - - hash := md5.New() - _, err = io.Copy(hash, file) - Expect(err).ToNot(HaveOccurred()) - return hash.Sum(result) -} - -// copyFileToDocker copies a file from the local file system into the /home/seluser/ directory in the docker container -func copyFileToDocker(filepath string) { - cmd := exec.Command("docker", "cp", filepath, "quic-test-selenium:/home/seluser/") - session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) - Expect(err).NotTo(HaveOccurred()) - Eventually(session, 5).Should(gexec.Exit(0)) -}