/extras/hs-test/hs-test
/extras/hs-test/http_server
/extras/hs-test/.build.ok
+/extras/hs-test/summary/
# ./configure
/CMakeFiles
DEB_DEPENDS += iperf ethtool # for 'make test TEST=vm_vpp_interfaces'
DEB_DEPENDS += libpcap-dev
DEB_DEPENDS += tshark
+DEB_DEPENDS += jq # for extracting test summary from .json report (hs-test)
LIBFFI=libffi6 # works on all but 20.04 and debian-testing
.PHONY: test
test: .deps.ok .build.ok
- @bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \
+ -bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \
--unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS) \
--vppsrc=$(VPPSRC) --parallel=$(PARALLEL) --repeat=$(REPEAT)
+ $(call jq-summary)
+ @bash ./script/compress.sh
.PHONY: test-debug
test-debug: .deps.ok .build_debug.ok
fixstyle:
@gofmt -w .
@go mod tidy
+
+# splitting this into multiple lines breaks the command
+jq-summary = @jq -r '.[0] | .SpecReports[] | select(.State == "failed") | select(.Failure != null) | "TestName: \(.LeafNodeText)\nMessage:\n\(.Failure.Message)\n Full Stack Trace:\n\(.Failure.Location.FullStackTrace)\n"' summary/report.json > summary/failed-summary.log \
+ && echo "Summary generated -> failed-summary.log"
}
if _, ok := yamlInput["volumes"]; ok {
- workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + container.suite.pid + volumeDir
+ workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + volumeDir
workDirReplacer := strings.NewReplacer("$HST_DIR", workDir)
volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir)
for _, volu := range yamlInput["volumes"].([]interface{}) {
"errors"
"flag"
"fmt"
- "log/slog"
+ "io"
+ "log"
"os"
"os/exec"
"strings"
cpuContexts []*CpuContext
cpuPerVpp int
pid string
+ logger *log.Logger
+ logFile *os.File
}
func (s *HstSuite) SetupSuite() {
+ s.createLogger()
+ s.log("Suite Setup")
+ RegisterFailHandler(func(message string, callerSkip ...int) {
+ s.hstFail()
+ Fail(message, callerSkip...)
+ })
var err error
s.pid = fmt.Sprint(os.Getpid())
s.cpuAllocator, err = CpuAllocator()
}
func (s *HstSuite) TearDownSuite() {
+ defer s.logFile.Close()
+ s.log("Suite Teardown")
s.unconfigureNetworkTopology()
}
func (s *HstSuite) TearDownTest() {
+ s.log("Test Teardown")
if *isPersistent {
return
}
}
func (s *HstSuite) SetupTest() {
- RegisterFailHandler(func(message string, callerSkip ...int) {
- s.hstFail()
- Fail(message, callerSkip...)
- })
+ s.log("Test Setup")
s.skipIfUnconfiguring()
s.setupVolumes()
s.setupContainers()
}
}
-func logVppInstance(container *Container, maxLines int) {
+func (s *HstSuite) logVppInstance(container *Container, maxLines int) {
if container.vppInstance == nil {
return
}
}
}
- fmt.Println("vvvvvvvvvvvvvvv " + container.name + " [VPP instance]:")
+ s.log("vvvvvvvvvvvvvvv " + container.name + " [VPP instance]:")
for _, line := range lines {
- fmt.Println(line)
+ s.log(line)
}
- fmt.Printf("^^^^^^^^^^^^^^^\n\n")
+ s.log("^^^^^^^^^^^^^^^\n\n")
}
func (s *HstSuite) hstFail() {
- fmt.Println("Containers: " + fmt.Sprint(s.containers))
+ s.log("Containers: " + fmt.Sprint(s.containers))
for _, container := range s.containers {
out, err := container.log(20)
if err != nil {
fmt.Printf("An error occured while obtaining '%s' container logs: %s\n", container.name, fmt.Sprint(err))
continue
}
- fmt.Printf("\nvvvvvvvvvvvvvvv " +
+ s.log("\nvvvvvvvvvvvvvvv " +
container.name + ":\n" +
out +
"^^^^^^^^^^^^^^^\n\n")
- logVppInstance(container, 20)
+ s.logVppInstance(container, 20)
}
}
Expect(object).ToNot(BeEmpty(), msgAndArgs...)
}
+func (s *HstSuite) createLogger(){
+ suiteName := CurrentSpecReport().ContainerHierarchyTexts[0]
+ var err error
+ s.logFile, err = os.Create("summary/" + suiteName + ".log")
+ if err != nil {
+ Fail("Unable to create log file.")
+ }
+ s.logger = log.New(io.Writer(s.logFile), "", log.LstdFlags)
+}
+
+// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter
+// to keep console tidy
func (s *HstSuite) log(arg any) {
+ logs := strings.Split(fmt.Sprint(arg), "\n")
+ for _, line := range logs {
+ s.logger.Println(line)
+ }
if *isVerbose {
- slog.Info(fmt.Sprint(arg))
+ GinkgoWriter.Println(arg)
}
}
for _, elem := range yamlTopo.Volumes {
volumeMap := elem["volume"].(VolumeConfig)
hostDir := volumeMap["host-dir"].(string)
- workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + s.pid + volumeDir
+ workingVolumeDir := logDir + CurrentSpecReport().LeafNodeText + volumeDir
volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir)
hostDir = volDirReplacer.Replace(hostDir)
s.volumes = append(s.volumes, hostDir)
package main
func init() {
- registerVethTests(VppEchoQuicTest, VppEchoTcpTest, VppEchoUdpTest)
+ registerVethTests(VppEchoQuicTest, VppEchoTcpTest)
}
func VppEchoQuicTest(s *VethsSuite) {
s.testVppEcho("quic")
}
-// udp echo currently broken in vpp, skipping
+// TODO: udp echo currently broken in vpp
func VppEchoUdpTest(s *VethsSuite) {
- s.skip("Broken")
s.testVppEcho("udp")
}
--- /dev/null
+#!/usr/bin/env bash
+
+if [ "${COMPRESS_FAILED_TEST_LOGS}" == "yes" -a -s "${HS_SUMMARY}/failed-summary.log" ]
+then
+ echo -n "Copying docker logs..."
+ dirs=$(jq -r '.[0] | .SpecReports[] | select(.State == "failed") | .LeafNodeText' ${HS_SUMMARY}/report.json)
+ for dirName in $dirs; do
+ logDir=/tmp/hs-test/$dirName
+ if [ -d "$logDir" ]; then
+ mkdir -p $WORKSPACE/archives/summary
+ cp -r $logDir $WORKSPACE/archives/summary/
+ fi
+ done
+ echo "Done."
+
+ if [ -n "$WORKSPACE" ]
+ then
+ echo -n "Copying failed test logs into build log archive directory ($WORKSPACE/archives)... "
+ mkdir -p $WORKSPACE/archives/summary
+ cp -a ${HS_SUMMARY}/* $WORKSPACE/archives/summary
+ echo "Done."
+ fi
+
+ echo -n "Compressing files in $WORKSPACE/archives from test runs... "
+ cd $WORKSPACE/archives
+ find . -type f \( -name "*.json" -o -name "*.log" \) -exec gzip {} \;
+ echo "Done."
+
+else
+ echo "Not compressing files in temporary directories from test runs."
+ exit 0
+fi
+
+exit 1
\ No newline at end of file
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
AfterEach(func() {
s.TearDownTest()
})
+
for _, test := range nginxSoloTests {
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
})
AfterAll(func() {
s.TearDownSuite()
-
})
AfterEach(func() {
s.TearDownTest()
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
- It(strings.Split(funcValue.Name(), ".")[2], Label("SOLO"), func(ctx SpecContext) {
+ testName := strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(time.Minute*5))
}
exit 1
fi
-sudo -E go run github.com/onsi/ginkgo/v2/ginkgo --no-color --trace $ginkgo_args -- $args
+mkdir -p summary
+
+sudo -E go run github.com/onsi/ginkgo/v2/ginkgo --no-color --trace --json-report=summary/report.json $ginkgo_args -- $args
import (
"fmt"
+ "io"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
+ "github.com/sirupsen/logrus"
"github.com/edwarnicke/exechelper"
. "github.com/onsi/ginkgo/v2"
}
func (vpp *VppInstance) start() error {
+ // Replace default logger in govpp with our own
+ govppLogger := logrus.New()
+ govppLogger.SetOutput(io.MultiWriter(vpp.getSuite().logger.Writer(), GinkgoWriter))
+ core.SetLogger(govppLogger)
// Create folders
containerWorkDir := vpp.container.getContainerWorkDir()