From f13b6aad4fc049b6b6d4778815f600076477b71f Mon Sep 17 00:00:00 2001 From: Nguyen Kien Trung Date: Tue, 11 Sep 2018 11:25:24 -0400 Subject: [PATCH] Travis an unit test fixes (#519) * Used Tessera as a fall back when Constellation is not available in the host * Used OSX 10.12 instead of 10.13 to avoid Kernel Extension Consent which is not available in CI environment. Can revert back once Travis CI has ability to disable the consent * Merged upstream PR/Code to fix tests which have intermittent failures * Cleaned up .travis.yml build matrix --- .travis.yml | 178 +----------------------- accounts/keystore/account_cache_test.go | 12 +- core/private_state_test.go | 143 ++++++++++++++++++- eth/downloader/downloader_test.go | 61 ++++++-- eth/protocol_test.go | 2 +- 5 files changed, 208 insertions(+), 188 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8a7f65e3d..ce4e52962 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,46 +1,23 @@ +# simplifed version of the upstream travis configuration + language: go go_import_path: github.com/ethereum/go-ethereum sudo: false matrix: include: - - os: linux - dist: trusty - sudo: required - go: 1.7.x - script: - - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse - - sudo modprobe fuse - - sudo chmod 666 /dev/fuse - - sudo chown root:$USER /etc/fuse.conf - - go run build/ci.go install - - go run build/ci.go test -coverage - - - os: linux - dist: trusty - sudo: required - go: 1.8.x - script: - - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse - - sudo modprobe fuse - - sudo chmod 666 /dev/fuse - - sudo chown root:$USER /etc/fuse.conf - - go run build/ci.go install - - go run build/ci.go test -coverage - - # These are the latest Go versions. - os: linux dist: trusty sudo: required go: 1.9.x script: - - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse - sudo modprobe fuse - sudo chmod 666 /dev/fuse - sudo chown root:$USER /etc/fuse.conf - go run build/ci.go install - - go run build/ci.go test -coverage -misspell + - go run build/ci.go test -coverage $TEST_PACKAGES - os: osx + osx_image: xcode9.2 # so we don't have to deal with Kernel Extension Consent UI which is never possible in CI go: 1.9.x sudo: required script: @@ -48,149 +25,4 @@ matrix: - brew install caskroom/cask/brew-cask - brew cask install osxfuse - go run build/ci.go install - - go run build/ci.go test -coverage -misspell - - # This builder does the Ubuntu PPA and Linux Azure uploads - - os: linux - dist: trusty - sudo: required - go: 1.9.x - env: - - ubuntu-ppa - - azure-linux - addons: - apt: - packages: - - devscripts - - debhelper - - dput - - gcc-multilib - - fakeroot - script: - # Build for the primary platforms that Trusty can manage - - go run build/ci.go debsrc -signer "Go Ethereum Linux Builder " -upload ppa:ethereum/ethereum - - go run build/ci.go install - - go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - go run build/ci.go install -arch 386 - - go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - # Switch over GCC to cross compilation (breaks 386, hence why do it here only) - - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - - sudo ln -s /usr/include/asm-generic /usr/include/asm - - - GOARM=5 CC=arm-linux-gnueabi-gcc go run build/ci.go install -arch arm - - GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - GOARM=6 CC=arm-linux-gnueabi-gcc go run build/ci.go install -arch arm - - GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - GOARM=7 CC=arm-linux-gnueabihf-gcc go run build/ci.go install -arch arm - - GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64 - - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - # This builder does the Linux Azure MIPS xgo uploads - - os: linux - dist: trusty - sudo: required - services: - - docker - go: 1.9.x - env: - - azure-linux-mips - script: - - go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v - - for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done - - go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - - go run build/ci.go xgo --alltools -- --targets=linux/mipsle --ldflags '-extldflags "-static"' -v - - for bin in build/bin/*-linux-mipsle; do mv -f "${bin}" "${bin/-linux-mipsle/}"; done - - go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - - go run build/ci.go xgo --alltools -- --targets=linux/mips64 --ldflags '-extldflags "-static"' -v - - for bin in build/bin/*-linux-mips64; do mv -f "${bin}" "${bin/-linux-mips64/}"; done - - go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - - go run build/ci.go xgo --alltools -- --targets=linux/mips64le --ldflags '-extldflags "-static"' -v - - for bin in build/bin/*-linux-mips64le; do mv -f "${bin}" "${bin/-linux-mips64le/}"; done - - go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - # This builder does the Android Maven and Azure uploads - - os: linux - dist: precise # Needed for the android tools - addons: - apt: - packages: - - oracle-java8-installer - - oracle-java8-set-default - language: android - android: - components: - - platform-tools - - tools - - android-15 - - android-19 - - android-24 - env: - - azure-android - - maven-android - before_install: - - curl https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz | tar -xz - - export PATH=`pwd`/go/bin:$PATH - - export GOROOT=`pwd`/go - - export GOPATH=$HOME/go - script: - # Build the Android archive and upload it to Maven Central and Azure - - curl https://dl.google.com/android/repository/android-ndk-r14b-linux-x86_64.zip -o android-ndk-r14b.zip - - unzip -q android-ndk-r14b.zip && rm android-ndk-r14b.zip - - mv android-ndk-r14b $HOME - - export ANDROID_NDK=$HOME/android-ndk-r14b - - - mkdir -p $GOPATH/src/github.com/ethereum - - ln -s `pwd` $GOPATH/src/github.com/ethereum - - go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds - - # This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads - - os: osx - go: 1.9.x - env: - - azure-osx - - azure-ios - - cocoapods-ios - script: - - go run build/ci.go install - - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds - - # Build the iOS framework and upload it to CocoaPods and Azure - - gem uninstall cocoapods -a -x - - gem install cocoapods - - - mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak - - sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb - - if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master && pod setup --verbose; fi - - - xctool -version - - xcrun simctl list - - - go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds - - # This builder does the Azure archive purges to avoid accumulating junk - - os: linux - dist: trusty - sudo: required - go: 1.9.x - env: - - azure-purge - script: - - go run build/ci.go purge -store gethstore/builds -days 14 - -install: - - go get golang.org/x/tools/cmd/cover -script: - - go run build/ci.go install - - go run build/ci.go test -coverage - -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/e09ccdce1048c5e03445 - on_success: change - on_failure: always + - go run build/ci.go test -coverage $TEST_PACKAGES diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go index e3dc31065..6ecb02aab 100644 --- a/accounts/keystore/account_cache_test.go +++ b/accounts/keystore/account_cache_test.go @@ -299,7 +299,7 @@ func TestCacheFind(t *testing.T) { func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error { var list []accounts.Account - for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 { + for d := 200 * time.Millisecond; d < 16*time.Second; d *= 2 { list = ks.Accounts() if reflect.DeepEqual(list, wantAccounts) { // ks should have also received change notifications @@ -349,6 +349,9 @@ func TestUpdatedKeyfileContents(t *testing.T) { return } + // needed so that modTime of `file` is different to its current value after forceCopyFile + time.Sleep(1000 * time.Millisecond) + // Now replace file contents if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil { t.Fatal(err) @@ -362,6 +365,9 @@ func TestUpdatedKeyfileContents(t *testing.T) { return } + // needed so that modTime of `file` is different to its current value after forceCopyFile + time.Sleep(1000 * time.Millisecond) + // Now replace file contents again if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil { t.Fatal(err) @@ -374,6 +380,10 @@ func TestUpdatedKeyfileContents(t *testing.T) { t.Error(err) return } + + // needed so that modTime of `file` is different to its current value after ioutil.WriteFile + time.Sleep(1000 * time.Millisecond) + // Now replace file contents with crap if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil { t.Fatal(err) diff --git a/core/private_state_test.go b/core/private_state_test.go index ce9022fce..8776067cc 100644 --- a/core/private_state_test.go +++ b/core/private_state_test.go @@ -2,13 +2,18 @@ package core import ( "bytes" + "errors" "fmt" "html/template" "io/ioutil" "math/big" + "net" + "net/http" "os" osExec "os/exec" "path" + "path/filepath" + "strings" "testing" "time" @@ -126,6 +131,136 @@ func runConstellation() (*osExec.Cmd, error) { return constellationCmd, nil } +func runTessera() (*osExec.Cmd, error) { + tesseraVersion := "0.6" + // make sure JRE is available + if err := osExec.Command("java").Start(); err != nil { + return nil, fmt.Errorf("runTessera: java not available - %s", err.Error()) + } + // download binary from github/release + dir, err := ioutil.TempDir("", "tessera") + if err != nil { + return nil, err + } + defer os.RemoveAll(dir) + resp, err := http.Get(fmt.Sprintf("https://github.com/jpmorganchase/tessera/releases/download/tessera-%s/tessera-app-%s-app.jar", tesseraVersion, tesseraVersion)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + tesseraJar := filepath.Join(dir, "tessera.jar") + if err := ioutil.WriteFile(tesseraJar, data, os.FileMode(0644)); err != nil { + return nil, err + } + // create config.json file + here, err := os.Getwd() + if err != nil { + return nil, err + } + if err = os.MkdirAll(path.Join(dir, "qdata"), 0755); err != nil { + return nil, err + } + tmIPCFile := filepath.Join(dir, "qdata", "tm.ipc") + keyData, err := ioutil.ReadFile(filepath.Join(here, "constellation-test-keys", "tm1.key")) + if err != nil { + return nil, err + } + publicKeyData, err := ioutil.ReadFile(filepath.Join(here, "constellation-test-keys", "tm1.pub")) + if err != nil { + return nil, err + } + tesseraConfigFile := filepath.Join(dir, "config.json") + if err := ioutil.WriteFile(tesseraConfigFile, []byte(fmt.Sprintf(` +{ + "useWhiteList": false, + "jdbc": { + "username": "sa", + "password": "", + "url": "jdbc:h2:./qdata/c0/db0;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0" + }, + "server": { + "port": 9000, + "hostName": "http://localhost", + "sslConfig": { + "tls": "OFF", + "generateKeyStoreIfNotExisted": true, + "serverKeyStore": "./qdata/c1/server1-keystore", + "serverKeyStorePassword": "quorum", + "serverTrustStore": "./qdata/c1/server-truststore", + "serverTrustStorePassword": "quorum", + "serverTrustMode": "TOFU", + "knownClientsFile": "./qdata/c1/knownClients", + "clientKeyStore": "./c1/client1-keystore", + "clientKeyStorePassword": "quorum", + "clientTrustStore": "./c1/client-truststore", + "clientTrustStorePassword": "quorum", + "clientTrustMode": "TOFU", + "knownServersFile": "./qdata/c1/knownServers" + } + }, + "peer": [ + { + "url": "http://localhost:9000" + } + ], + "keys": { + "passwords": [], + "keyData": [ + { + "config": %s, + "publicKey": "%s" + } + ] + }, + "alwaysSendTo": [], + "unixSocketFile": "%s" +} +`, string(keyData), string(publicKeyData), tmIPCFile)), os.FileMode(0644)); err != nil { + return nil, err + } + + cmdStatusChan := make(chan error) + cmd := osExec.Command("java", "-Xms128M", "-Xmx128M", "-jar", tesseraJar, "-configFile", tesseraConfigFile) + // run tessera + go func() { + err := cmd.Start() + cmdStatusChan <- err + }() + // wait for tessera to come up + go func() { + waitingErr := errors.New("waiting") + checkFunc := func() error { + conn, err := net.Dial("unix", tmIPCFile) + if err != nil { + return waitingErr + } + if _, err := conn.Write([]byte("GET /upcheck HTTP/1.0\r\n\r\n")); err != nil { + return waitingErr + } + result, err := ioutil.ReadAll(conn) + if err != nil || string(result) != "I'm up!" { + return waitingErr + } + return nil + } + for { + time.Sleep(3 * time.Second) + if err := checkFunc(); err != nil && err != waitingErr { + cmdStatusChan <- err + } + } + }() + if err := <-cmdStatusChan; err != nil { + return nil, err + } + // wait until tessera is up + return cmd, nil +} + // 600a600055600060006001a1 // [1] PUSH1 0x0a (store value) // [3] PUSH1 0x00 (store addr) @@ -146,7 +281,13 @@ func TestPrivateTransaction(t *testing.T) { constellationCmd, err := runConstellation() if err != nil { - t.Fatal(err) + if strings.Contains(err.Error(), "executable file not found") { + if constellationCmd, err = runTessera(); err != nil { + t.Fatal(err) + } + } else { + t.Fatal(err) + } } defer constellationCmd.Process.Kill() diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 58f6e9a62..7d1cc8c34 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -704,6 +704,7 @@ func TestThrottling64Full(t *testing.T) { testThrottling(t, 64, FullSync) } func TestThrottling64Fast(t *testing.T) { testThrottling(t, 64, FastSync) } func testThrottling(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() tester := newTester() defer tester.terminate() @@ -1166,6 +1167,8 @@ func TestShiftedHeaderAttack64Fast(t *testing.T) { testShiftedHeaderAttack(t, 6 func TestShiftedHeaderAttack64Light(t *testing.T) { testShiftedHeaderAttack(t, 64, LightSync) } func testShiftedHeaderAttack(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + tester := newTester() defer tester.terminate() @@ -1198,6 +1201,8 @@ func TestInvalidHeaderRollback64Fast(t *testing.T) { testInvalidHeaderRollback( func TestInvalidHeaderRollback64Light(t *testing.T) { testInvalidHeaderRollback(t, 64, LightSync) } func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + tester := newTester() defer tester.terminate() @@ -1310,6 +1315,8 @@ func TestBlockHeaderAttackerDropping63(t *testing.T) { testBlockHeaderAttackerDr func TestBlockHeaderAttackerDropping64(t *testing.T) { testBlockHeaderAttackerDropping(t, 64) } func testBlockHeaderAttackerDropping(t *testing.T, protocol int) { + t.Parallel() + // Define the disconnection requirement for individual hash fetch errors tests := []struct { result error @@ -1665,12 +1672,26 @@ func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) { // This test reproduces an issue where unexpected deliveries would // block indefinitely if they arrived at the right time. -func TestDeliverHeadersHang62(t *testing.T) { testDeliverHeadersHang(t, 62, FullSync) } -func TestDeliverHeadersHang63Full(t *testing.T) { testDeliverHeadersHang(t, 63, FullSync) } -func TestDeliverHeadersHang63Fast(t *testing.T) { testDeliverHeadersHang(t, 63, FastSync) } -func TestDeliverHeadersHang64Full(t *testing.T) { testDeliverHeadersHang(t, 64, FullSync) } -func TestDeliverHeadersHang64Fast(t *testing.T) { testDeliverHeadersHang(t, 64, FastSync) } -func TestDeliverHeadersHang64Light(t *testing.T) { testDeliverHeadersHang(t, 64, LightSync) } +// We use data driven subtests to manage this so that it will be parallel on its own +// and not with the other tests, avoiding intermittent failures. +func TestDeliverHeadersHang(t *testing.T) { + testCases := []struct { + protocol int + syncMode SyncMode + }{ + {62, FullSync}, + {63, FullSync}, + {63, FastSync}, + {64, FullSync}, + {64, FastSync}, + {64, LightSync}, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("protocol %d mode %v", tc.protocol, tc.syncMode), func(t *testing.T) { + testDeliverHeadersHang(t, tc.protocol, tc.syncMode) + }) + } +} type floodingTestPeer struct { peer Peer @@ -1703,7 +1724,7 @@ func (ftp *floodingTestPeer) RequestHeadersByNumber(from uint64, count, skip int // Deliver the actual requested headers. go ftp.peer.RequestHeadersByNumber(from, count, skip, reverse) // None of the extra deliveries should block. - timeout := time.After(15 * time.Second) + timeout := time.After(60 * time.Second) for i := 0; i < cap(deliveriesDone); i++ { select { case <-deliveriesDone: @@ -1732,7 +1753,6 @@ func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) { tester.downloader.peers.peers["peer"].peer, tester, } - if err := tester.sync("peer", nil, mode); err != nil { t.Errorf("sync failed: %v", err) } @@ -1742,12 +1762,28 @@ func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) { // Tests that if fast sync aborts in the critical section, it can restart a few // times before giving up. -func TestFastCriticalRestartsFail63(t *testing.T) { testFastCriticalRestarts(t, 63, false) } -func TestFastCriticalRestartsFail64(t *testing.T) { testFastCriticalRestarts(t, 64, false) } -func TestFastCriticalRestartsCont63(t *testing.T) { testFastCriticalRestarts(t, 63, true) } -func TestFastCriticalRestartsCont64(t *testing.T) { testFastCriticalRestarts(t, 64, true) } +// We use data driven subtests to manage this so that it will be parallel on its own +// and not with the other tests, avoiding intermittent failures. +func TestFastCriticalRestarts(t *testing.T) { + testCases := []struct { + protocol int + progress bool + }{ + {63, false}, + {64, false}, + {63, true}, + {64, true}, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("protocol %d progress %v", tc.protocol, tc.progress), func(t *testing.T) { + testFastCriticalRestarts(t, tc.protocol, tc.progress) + }) + } +} func testFastCriticalRestarts(t *testing.T, protocol int, progress bool) { + t.Parallel() + tester := newTester() defer tester.terminate() @@ -1776,6 +1812,7 @@ func testFastCriticalRestarts(t *testing.T, protocol int, progress bool) { // If it's the first failure, pivot should be locked => reenable all others to detect pivot changes if i == 0 { + time.Sleep(150 * time.Millisecond) // Make sure no in-flight requests remain if tester.downloader.fsPivotLock == nil { time.Sleep(400 * time.Millisecond) // Make sure the first huge timeout expires too t.Fatalf("pivot block not locked in after critical section failure") diff --git a/eth/protocol_test.go b/eth/protocol_test.go index d3a44ae91..b93792b4b 100644 --- a/eth/protocol_test.go +++ b/eth/protocol_test.go @@ -60,7 +60,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) { }, { code: StatusMsg, data: statusData{uint32(protocol), 999, td, currentBlock, genesis}, - wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"), + wantError: errResp(ErrNetworkIdMismatch, "999 (!= %d)", DefaultConfig.NetworkId), }, { code: StatusMsg, data: statusData{uint32(protocol), DefaultConfig.NetworkId, td, currentBlock, common.Hash{3}},