diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/apiv2/20-containers.at | 25 | ||||
-rw-r--r-- | test/e2e/common_test.go | 4 | ||||
-rw-r--r-- | test/e2e/create_staticip_test.go | 27 | ||||
-rw-r--r-- | test/e2e/create_staticmac_test.go | 12 | ||||
-rw-r--r-- | test/e2e/libpod_suite_remote_test.go | 5 | ||||
-rw-r--r-- | test/e2e/libpod_suite_test.go | 6 | ||||
-rw-r--r-- | test/e2e/pod_create_test.go | 35 | ||||
-rw-r--r-- | test/e2e/pod_inspect_test.go | 21 | ||||
-rw-r--r-- | test/e2e/run_networking_test.go | 14 | ||||
-rw-r--r-- | test/system/030-run.bats | 42 | ||||
-rw-r--r-- | test/system/075-exec.bats | 22 | ||||
-rw-r--r-- | test/system/200-pod.bats | 8 | ||||
-rw-r--r-- | test/system/400-unprivileged-access.bats | 65 | ||||
-rw-r--r-- | test/system/410-selinux.bats | 9 | ||||
-rw-r--r-- | test/system/helpers.bash | 32 | ||||
-rwxr-xr-x | test/system/helpers.t | 70 |
16 files changed, 334 insertions, 63 deletions
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 9a1db5154..4bb00398e 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -51,17 +51,19 @@ cid=$(jq -r '.[0].Id' <<<"$output") t DELETE libpod/containers/$cid 204 -# Ensure that API does not occur: Create Container creates an invalid and the container fails to start -# https://github.com/containers/libpod/issues/6799 -CNAME=testArgs -t POST libpod/containers/create?name=${CNAME} Image=${IMAGE} 201 \ +# Issue #6799: it should be possible to start a container, even w/o args. +t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \ .Id~[0-9a-f]\\{64\\} -t GET libpod/containers/json?limit=1 200 \ - length=1 \ - .[0].Id~[0-9a-f]\\{64\\} -cid=$(jq -r '.[0].Id' <<<"$output") -# This step should start the container properly -t POST libpod/containers/${cid}/start '' 204 +cid=$(jq -r '.Id' <<<"$output") +# Prior to the fix in #6835, this would fail 500 "args must not be empty" +t POST libpod/containers/${cid}/start '' 204 +# Container should exit almost immediately. Wait for it, confirm successful run +t POST libpod/containers/${cid}/wait '' 200 '0' +t GET libpod/containers/${cid}/json 200 \ + .Id=$cid \ + .State.Status~\\\(exited\\\|stopped\\\) \ + .State.Running=false \ + .State.ExitCode=0 t DELETE libpod/containers/$cid 204 CNAME=myfoo @@ -75,7 +77,8 @@ t POST "libpod/commit?container=nonesuch" '' 404 # Comment can only be used with docker format, not OCI cparam="repo=newrepo&comment=foo&author=bob" -t POST "libpod/commit?container=$CNAME&$cparam" '' 500 +t POST "libpod/commit?container=$CNAME&$cparam" '' 500 \ + .cause="messages are only compatible with the docker image format (-f docker)" # Commit a new image from the container t POST "libpod/commit?container=$CNAME" '' 200 \ diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 51f290159..aa0e9635a 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -595,3 +595,7 @@ func SkipIfNotFedora() { ginkgo.Skip("Test can only run on Fedora") } } + +func isRootless() bool { + return os.Geteuid() != 0 +} diff --git a/test/e2e/create_staticip_test.go b/test/e2e/create_staticip_test.go index e52b37417..a1a08045a 100644 --- a/test/e2e/create_staticip_test.go +++ b/test/e2e/create_staticip_test.go @@ -6,6 +6,7 @@ import ( "os" "time" + "github.com/containers/libpod/v2/pkg/rootless" . "github.com/containers/libpod/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -19,7 +20,6 @@ var _ = Describe("Podman create with --ip flag", func() { ) BeforeEach(func() { - SkipIfRootless() tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) @@ -39,18 +39,21 @@ var _ = Describe("Podman create with --ip flag", func() { }) It("Podman create --ip with garbage address", func() { + SkipIfRootless() result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "114232346", ALPINE, "ls"}) result.WaitWithDefaultTimeout() Expect(result).To(ExitWithError()) }) It("Podman create --ip with v6 address", func() { + SkipIfRootless() result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "2001:db8:bad:beef::1", ALPINE, "ls"}) result.WaitWithDefaultTimeout() Expect(result).To(ExitWithError()) }) It("Podman create --ip with non-allocatable IP", func() { + SkipIfRootless() result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "203.0.113.124", ALPINE, "ls"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) @@ -64,19 +67,25 @@ var _ = Describe("Podman create with --ip flag", func() { ip := GetRandomIPAddress() result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", ip, ALPINE, "ip", "addr"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + // Rootless static ip assignment should error + if rootless.IsRootless() { + Expect(result.ExitCode()).To(Equal(125)) + } else { + Expect(result.ExitCode()).To(Equal(0)) - result = podmanTest.Podman([]string{"start", "test"}) - result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + result = podmanTest.Podman([]string{"start", "test"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) - result = podmanTest.Podman([]string{"logs", "test"}) - result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) - Expect(result.OutputToString()).To(ContainSubstring(ip + "/16")) + result = podmanTest.Podman([]string{"logs", "test"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(ip + "/16")) + } }) It("Podman create two containers with the same IP", func() { + SkipIfRootless() ip := GetRandomIPAddress() result := podmanTest.Podman([]string{"create", "--name", "test1", "--ip", ip, ALPINE, "sleep", "999"}) result.WaitWithDefaultTimeout() diff --git a/test/e2e/create_staticmac_test.go b/test/e2e/create_staticmac_test.go index fbe11440c..33675d607 100644 --- a/test/e2e/create_staticmac_test.go +++ b/test/e2e/create_staticmac_test.go @@ -1,10 +1,9 @@ -// +build !remote - package integration import ( "os" + "github.com/containers/libpod/v2/pkg/rootless" . "github.com/containers/libpod/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -18,7 +17,6 @@ var _ = Describe("Podman run with --mac-address flag", func() { ) BeforeEach(func() { - SkipIfRootless() tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) @@ -40,7 +38,11 @@ var _ = Describe("Podman run with --mac-address flag", func() { It("Podman run --mac-address", func() { result := podmanTest.Podman([]string{"run", "--mac-address", "92:d0:c6:0a:29:34", ALPINE, "ip", "addr"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) - Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:0a:29:34")) + if rootless.IsRootless() { + Expect(result.ExitCode()).To(Equal(125)) + } else { + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:0a:29:34")) + } }) }) diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go index 7a067e861..13f4e1aef 100644 --- a/test/e2e/libpod_suite_remote_test.go +++ b/test/e2e/libpod_suite_remote_test.go @@ -28,11 +28,6 @@ func SkipIfRootless() { ginkgo.Skip("This function is not enabled for rootless podman") } } -func SkipIfRootlessV2() { - if os.Geteuid() != 0 { - ginkgo.Skip("This function is not enabled for v2 rootless podman") - } -} // Podman is the exec call to podman on the filesystem func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index 105fcf6a8..29ad01363 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -41,12 +41,6 @@ func SkipIfRootless() { } } -func SkipIfRootlessV2() { - if os.Geteuid() != 0 { - Skip("This function is not enabled for v2 rootless podman") - } -} - // Podman is the exec call to podman on the filesystem func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { podmanSession := p.PodmanBase(args, false, false) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 57737ad59..016eaaa99 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/containers/libpod/v2/pkg/rootless" . "github.com/containers/libpod/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -238,17 +239,20 @@ var _ = Describe("Podman pod create", func() { }) It("podman create pod with IP address", func() { - SkipIfRootless() name := "test" ip := GetRandomIPAddress() podCreate := podmanTest.Podman([]string{"pod", "create", "--ip", ip, "--name", name}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) - - podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) - podResolvConf.WaitWithDefaultTimeout() - Expect(podResolvConf.ExitCode()).To(Equal(0)) - Expect(strings.Contains(podResolvConf.OutputToString(), ip)).To(BeTrue()) + // Rootless should error + if rootless.IsRootless() { + Expect(podCreate.ExitCode()).To(Equal(125)) + } else { + Expect(podCreate.ExitCode()).To(Equal(0)) + podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) + podResolvConf.WaitWithDefaultTimeout() + Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(strings.Contains(podResolvConf.OutputToString(), ip)).To(BeTrue()) + } }) It("podman create pod with IP address and no infra should fail", func() { @@ -262,17 +266,20 @@ var _ = Describe("Podman pod create", func() { It("podman create pod with MAC address", func() { SkipIfRemote() - SkipIfRootless() name := "test" mac := "92:d0:c6:0a:29:35" podCreate := podmanTest.Podman([]string{"pod", "create", "--mac-address", mac, "--name", name}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) - - podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) - podResolvConf.WaitWithDefaultTimeout() - Expect(podResolvConf.ExitCode()).To(Equal(0)) - Expect(strings.Contains(podResolvConf.OutputToString(), mac)).To(BeTrue()) + // Rootless should error + if rootless.IsRootless() { + Expect(podCreate.ExitCode()).To(Equal(125)) + } else { + Expect(podCreate.ExitCode()).To(Equal(0)) + podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) + podResolvConf.WaitWithDefaultTimeout() + Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(strings.Contains(podResolvConf.OutputToString(), mac)).To(BeTrue()) + } }) It("podman create pod with MAC address and no infra should fail", func() { diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go index 5e3634435..16bf1c4c9 100644 --- a/test/e2e/pod_inspect_test.go +++ b/test/e2e/pod_inspect_test.go @@ -1,8 +1,11 @@ package integration import ( + "encoding/json" "os" + "github.com/containers/libpod/v2/libpod/define" + . "github.com/containers/libpod/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -79,4 +82,22 @@ var _ = Describe("Podman pod inspect", func() { index := len(inspectCreateCommand) - len(createCommand) Expect(inspectCreateCommand[index:]).To(Equal(createCommand)) }) + + It("podman pod inspect outputs port bindings", func() { + podName := "testPod" + create := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "8080:80"}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspectOut := podmanTest.Podman([]string{"pod", "inspect", podName}) + inspectOut.WaitWithDefaultTimeout() + Expect(inspectOut.ExitCode()).To(Equal(0)) + + inspectJSON := new(define.InspectPodData) + err := json.Unmarshal(inspectOut.Out.Contents(), inspectJSON) + Expect(err).To(BeNil()) + Expect(inspectJSON.InfraConfig).To(Not(BeNil())) + Expect(len(inspectJSON.InfraConfig.PortBindings["80/tcp"])).To(Equal(1)) + Expect(inspectJSON.InfraConfig.PortBindings["80/tcp"][0].HostPort).To(Equal("8080")) + }) }) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 93057f428..317f760db 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -88,6 +88,20 @@ var _ = Describe("Podman run networking", func() { Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) }) + It("podman run -p 8080:80/TCP", func() { + name := "testctr" + // "TCP" in upper characters + session := podmanTest.Podman([]string{"create", "-t", "-p", "8080:80/TCP", "--name", name, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + inspectOut := podmanTest.InspectContainer(name) + Expect(len(inspectOut)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) + // "tcp" in lower characters + Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8080")) + Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) + }) + It("podman run -p 80/udp", func() { name := "testctr" session := podmanTest.Podman([]string{"create", "-t", "-p", "80/udp", "--name", name, ALPINE, "/bin/sh"}) diff --git a/test/system/030-run.bats b/test/system/030-run.bats index bc6347012..13fec20ad 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -242,4 +242,46 @@ echo $rand | 0 | $rand run_podman rmi myi } +# #6735 : complex interactions with multiple user namespaces +# The initial report has to do with bind mounts, but that particular +# symptom only manifests on a fedora container image -- we have no +# reproducer on alpine. Checking directory ownership is good enough. +@test "podman run : user namespace preserved root ownership" { + for priv in "" "--privileged"; do + for user in "--user=0" "--user=100"; do + for keepid in "" "--userns=keep-id"; do + opts="$priv $user $keepid" + + for dir in /etc /usr;do + run_podman run --rm $opts $IMAGE stat -c '%u:%g:%n' $dir + remove_same_dev_warning # grumble + is "$output" "0:0:$dir" "run $opts ($dir)" + done + done + done + done +} + +# #6829 : add username to /etc/passwd inside container if --userns=keep-id +@test "podman run : add username to /etc/passwd if --userns=keep-id" { + # Default: always run as root + run_podman run --rm $IMAGE id -un + is "$output" "root" "id -un on regular container" + + # This would always work on root, but is new behavior on rootless: #6829 + # adds a user entry to /etc/passwd + run_podman run --rm --userns=keep-id $IMAGE id -un + is "$output" "$(id -un)" "username on container with keep-id" + + # --privileged should make no difference + run_podman run --rm --privileged --userns=keep-id $IMAGE id -un + remove_same_dev_warning # grumble + is "$output" "$(id -un)" "username on container with keep-id" + + # ...but explicitly setting --user should override keep-id + run_podman run --rm --privileged --userns=keep-id --user=0 $IMAGE id -un + remove_same_dev_warning # grumble + is "$output" "root" "--user=0 overrides keep-id" +} + # vim: filetype=sh diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats index f8c7f2766..945bcfa2d 100644 --- a/test/system/075-exec.bats +++ b/test/system/075-exec.bats @@ -6,8 +6,6 @@ load helpers @test "podman exec - basic test" { - skip_if_remote - rand_filename=$(random_string 20) rand_content=$(random_string 50) @@ -80,4 +78,24 @@ load helpers run_podman rm $cid } +# #6829 : add username to /etc/passwd inside container if --userns=keep-id +# #6593 : doesn't actually work with podman exec +@test "podman exec - with keep-id" { + skip "Please enable once #6593 is fixed" + + run_podman run -d --userns=keep-id $IMAGE sh -c \ + "echo READY;while [ ! -f /stop ]; do sleep 1; done" + cid="$output" + wait_for_ready $cid + + run_podman exec $cid id -un + is "$output" "$(id -un)" "container is running as current user" + + # Until #6593 gets fixed, this just hangs. The server process barfs with: + # unable to find user <username>: no matching entries in passwd file + run_podman exec --user=$(id -un) $cid touch /stop + run_podman wait $cid + run_podman rm $cid +} + # vim: filetype=sh diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index 0e9d9132e..0ad555305 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -165,9 +165,13 @@ function random_ip() { # Create a pod with all the desired options # FIXME: --ip=$ip fails: # Error adding network: failed to allocate all requested IPs + local mac_option="--mac-address=$mac" + if is_rootless; then + mac_option= + fi run_podman pod create --name=mypod \ --pod-id-file=$pod_id_file \ - --mac-address=$mac \ + $mac_option \ --hostname=$hostname \ --add-host "$add_host_n:$add_host_ip" \ --dns "$dns_server" \ @@ -181,7 +185,7 @@ function random_ip() { is "$(<$pod_id_file)" "$pod_id" "contents of pod-id-file" # Check each of the options - if ! is_rootless; then + if [ -n "$mac_option" ]; then run_podman run --rm --pod mypod $IMAGE ip link show # 'ip' outputs hex in lower-case, ${expr,,} converts UC to lc is "$output" ".* link/ether ${mac,,} " "requested MAC address was set" diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats index 98f8b8211..1384c0ab8 100644 --- a/test/system/400-unprivileged-access.bats +++ b/test/system/400-unprivileged-access.bats @@ -1,6 +1,7 @@ #!/usr/bin/env bats -*- bats -*- # # Tests #2730 - regular users are not able to read/write container storage +# Tests #6957 - /sys/dev (et al) are masked from unprivileged containers # load helpers @@ -97,4 +98,68 @@ EOF run_podman rm c_uidmap c_uidmap_v } + +# #6957 - mask out /proc/acpi, /sys/dev, and other sensitive system files +@test "sensitive mount points are masked without --privileged" { + # FIXME: this should match the list in pkg/specgen/generate/config_linux.go + local -a mps=( + /proc/acpi + /proc/kcore + /proc/keys + /proc/latency_stats + /proc/timer_list + /proc/timer_stats + /proc/sched_debug + /proc/scsi + /sys/firmware + /sys/fs/selinux + /sys/dev + ) + + # Some of the above may not exist on our host. Find only the ones that do. + local -a subset=() + for mp in ${mps[@]}; do + if [ -e $mp ]; then + subset+=($mp) + fi + done + + # Run 'stat' on all the files, plus /dev/null. Get path, file type, + # number of links, major, and minor (see below for why). Do it all + # in one go, to avoid multiple podman-runs + run_podman run --rm $IMAGE stat -c'%n:%F:%h:%T:%t' /dev/null ${subset[@]} + local devnull= + for result in "${lines[@]}"; do + # e.g. /proc/acpi:character special file:1:3:1 + local IFS=: + read path type nlinks major minor <<<"$result" + + if [[ $path = "/dev/null" ]]; then + # /dev/null is our reference point: masked *files* (not directories) + # will be created as /dev/null clones. + # This depends on 'stat' returning results in argv order, + # so /dev/null is first, so we have a reference for others. + # If that ever breaks, this test will have to be done in two passes. + devnull="$major:$minor" + elif [[ $type = "character special file" ]]; then + # Container file is a character device: it must match /dev/null + is "$major:$minor" "$devnull" "$path: major/minor matches /dev/null" + elif [[ $type = "directory" ]]; then + # Directories: must be empty (only two links). + # FIXME: this is a horrible almost-worthless test! It does not + # actually check for files in the directory (expect: zero), + # merely for the nonexistence of any subdirectories! It relies + # on the observed (by Ed) fact that all the masked directories + # contain further subdirectories on the host. If there's ever + # a new masked directory that contains only files, this test + # will silently pass without any indication of error. + # If you can think of a better way to do this check, + # please feel free to fix it. + is "$nlinks" "2" "$path: directory link count" + else + die "$path: Unknown file type '$type'" + fi + done +} + # vim: filetype=sh diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats index 1769730f0..497e29b3e 100644 --- a/test/system/410-selinux.bats +++ b/test/system/410-selinux.bats @@ -19,15 +19,8 @@ function check_label() { # FIXME: on some CI systems, 'run --privileged' emits a spurious # warning line about dup devices. Ignore it. + remove_same_dev_warning local context="$output" - if [ ${#lines[@]} -gt 1 ]; then - if expr "${lines[0]}" : "WARNING: .* type, major" >/dev/null; then - echo "# ${lines[0]} [ignored]" >&3 - context="${lines[1]}" - else - die "FAILED: too much output, expected one single line" - fi - fi is "$context" ".*_u:system_r:.*" "SELinux role should always be system_r" diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 5301644d6..4239ef876 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -392,5 +392,37 @@ function find_exec_pid_files() { find $storage_path -type f -iname 'exec_pid_*' fi } + + +############################# +# remove_same_dev_warning # Filter out useless warning from output +############################# +# +# On some CI systems, 'podman run --privileged' emits a useless warning: +# +# WARNING: The same type, major and minor should not be used for multiple devices. +# +# This obviously screws us up when we look at output results. +# +# This function removes the warning from $output and $lines +# +function remove_same_dev_warning() { + # No input arguments. We operate in-place on $output and $lines + + local i=0 + local -a new_lines=() + while [[ $i -lt ${#lines[@]} ]]; do + if expr "${lines[$i]}" : 'WARNING: .* same type, major.* multiple' >/dev/null; then + : + else + new_lines+=("${lines[$i]}") + fi + i=$(( i + 1 )) + done + + lines=("${new_lines[@]}") + output=$(printf '%s\n' "${lines[@]}") +} + # END miscellaneous tools ############################################################################### diff --git a/test/system/helpers.t b/test/system/helpers.t index 7b4e48a84..a022f11c4 100755 --- a/test/system/helpers.t +++ b/test/system/helpers.t @@ -23,7 +23,8 @@ rc=0 function check_result { testnum=$(expr $testnum + 1) if [ "$1" = "$2" ]; then - echo "ok $testnum $3 = $1" + # Multi-level echo flattens newlines, makes success messages readable + echo $(echo "ok $testnum $3 = $1") else echo "not ok $testnum $3" echo "# expected: $2" @@ -141,5 +142,72 @@ done < <(parse_table "$table") # END dprint ############################################################################### +# BEGIN remove_same_dev_warning + +# Test-helper function: runs remove_same_dev_warning, compares resulting +# value of $lines and $output to expected values given on command line +function check_same_dev() { + local testname="$1"; shift + local -a expect_lines=("$@") + local nl=" +" + + remove_same_dev_warning + + # After processing, check the expected number of lines + check_result "${#lines[@]}" "${#@}" "$testname: expected # of lines" + + # ...and each expected line + local expect_output="" + local i=0 + while [ $i -lt ${#expect_lines[@]} ]; do + check_result "${lines[$i]}" "${expect_lines[$i]}" "$testname: line $i" + expect_output+="${expect_lines[$i]}$nl" + i=$(( i + 1 )) + done + + # ...and the possibly-multi-line $output + check_result "$output" "${expect_output%%$nl}" "$testname: output" +} + +# Simplest case: nothing removed. +declare -a lines=("a b c" "d" "e f") +check_same_dev "abc" "a b c" "d" "e f" + +# Confirm that the warning message is removed from the beginning +declare -a lines=( + "WARNING: The same type, major and minor should not be used for multiple devices." + "a" + "b" + "c" +) +check_same_dev "warning is removed" a b c + +# ...and from the middle (we do not expect to see this) +declare -a lines=( + "WARNING: The same type, major and minor should not be used for multiple devices." + "a" + "b" + "WARNING: The same type, major and minor should not be used for multiple devices." + "c" +) +check_same_dev "multiple warnings removed" a b c + +# Corner case: two lines of output, only one of which we care about +declare -a lines=( + "WARNING: The same type, major and minor should not be used for multiple devices." + "this is the only line we care about" +) +check_same_dev "one-line output" "this is the only line we care about" + +# Corner case: one line of output, but we expect zero. +declare -a lines=( + "WARNING: The same type, major and minor should not be used for multiple devices." +) +check_same_dev "zero-line output" + + +# END remove_same_dev_warning +############################################################################### exit $rc |