diff options
Diffstat (limited to 'test/endpoint')
-rw-r--r-- | test/endpoint/commit.go | 49 | ||||
-rw-r--r-- | test/endpoint/config.go | 24 | ||||
-rw-r--r-- | test/endpoint/endpoint.go | 225 | ||||
-rw-r--r-- | test/endpoint/endpoint_suite_test.go | 72 | ||||
-rw-r--r-- | test/endpoint/exists_test.go | 68 | ||||
-rw-r--r-- | test/endpoint/pull_test.go | 46 | ||||
-rw-r--r-- | test/endpoint/setup.go | 214 | ||||
-rw-r--r-- | test/endpoint/version_test.go | 43 |
8 files changed, 0 insertions, 741 deletions
diff --git a/test/endpoint/commit.go b/test/endpoint/commit.go deleted file mode 100644 index 97e10efb0..000000000 --- a/test/endpoint/commit.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "encoding/json" - "os" - - . "github.com/containers/podman/v2/test/utils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Podman commit", func() { - var ( - tempdir string - err error - endpointTest *EndpointTestIntegration - ) - - BeforeEach(func() { - tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - endpointTest = Setup(tempdir) - endpointTest.StartVarlinkWithCache() - }) - - AfterEach(func() { - endpointTest.Cleanup() - - }) - - It("ensure commit with uppercase image name does not panic", func() { - body := make(map[string]string) - body["image_name"] = "FOO" - body["format"] = "oci" - body["name"] = "top" - b, err := json.Marshal(body) - Expect(err).To(BeNil()) - // run the container to be committed - _ = endpointTest.startTopContainer("top") - result := endpointTest.Varlink("Commit", string(b), false) - // This indicates an error occurred - Expect(len(result.StdErrToString())).To(BeNumerically(">", 0)) - }) - -}) diff --git a/test/endpoint/config.go b/test/endpoint/config.go deleted file mode 100644 index 9d2a5a239..000000000 --- a/test/endpoint/config.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build varlink - -package endpoint - -import "encoding/json" - -var ( - STORAGE_FS = "vfs" - STORAGE_OPTIONS = "--storage-driver vfs" - ROOTLESS_STORAGE_FS = "vfs" - ROOTLESS_STORAGE_OPTIONS = "--storage-driver vfs" - CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, infra, labels} - nginx = "quay.io/libpod/alpine_nginx:latest" - BB_GLIBC = "docker.io/library/busybox:glibc" - registry = "docker.io/library/registry:2.6" - labels = "quay.io/libpod/alpine_labels:latest" -) - -func makeNameMessage(name string) string { - n := make(map[string]string) - n["name"] = name - b, _ := json.Marshal(n) - return string(b) -} diff --git a/test/endpoint/endpoint.go b/test/endpoint/endpoint.go deleted file mode 100644 index d2c143824..000000000 --- a/test/endpoint/endpoint.go +++ /dev/null @@ -1,225 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "os/exec" - "strconv" - "strings" - "syscall" - "time" - - "github.com/containers/podman/v2/pkg/rootless" - iopodman "github.com/containers/podman/v2/pkg/varlink" - . "github.com/onsi/ginkgo" - "github.com/onsi/gomega/gexec" -) - -var ( - ARTIFACT_DIR = "/tmp/.artifacts" - CGROUP_MANAGER = "systemd" - defaultWaitTimeout = 90 - //RESTORE_IMAGES = []string{ALPINE, BB} - INTEGRATION_ROOT string - ImageCacheDir = "/tmp/podman/imagecachedir" - VarlinkBinary = "/usr/bin/varlink" - ALPINE = "docker.io/library/alpine:latest" - infra = "k8s.gcr.io/pause:3.2" - BB = "docker.io/library/busybox:latest" - redis = "docker.io/library/redis:alpine" - fedoraMinimal = "quay.io/libpod/fedora-minimal:latest" -) - -type EndpointTestIntegration struct { - ArtifactPath string - CNIConfigDir string - CgroupManager string - ConmonBinary string - CrioRoot string - //Host HostOS - ImageCacheDir string - ImageCacheFS string - OCIRuntime string - PodmanBinary string - RemoteTest bool - RunRoot string - SignaturePolicyPath string - StorageOptions string - TmpDir string - Timings []string - VarlinkBinary string - VarlinkCommand *exec.Cmd - VarlinkEndpoint string - VarlinkSession *os.Process -} - -func (p *EndpointTestIntegration) StartVarlink() { - p.startVarlink(false) -} - -func (p *EndpointTestIntegration) StartVarlinkWithCache() { - p.startVarlink(true) -} - -func (p *EndpointTestIntegration) startVarlink(useImageCache bool) { - var ( - counter int - ) - if os.Geteuid() == 0 { - os.MkdirAll("/run/podman", 0755) - } - varlinkEndpoint := p.VarlinkEndpoint - //p.SetVarlinkAddress(p.RemoteSocket) - - args := []string{"varlink", "--time", "0", varlinkEndpoint} - podmanOptions := getVarlinkOptions(p, args) - if useImageCache { - cacheOptions := []string{"--storage-opt", fmt.Sprintf("%s.imagestore=%s", p.ImageCacheFS, p.ImageCacheDir)} - podmanOptions = append(cacheOptions, podmanOptions...) - } - command := exec.Command(p.PodmanBinary, podmanOptions...) - fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " ")) - command.Start() - command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - p.VarlinkCommand = command - p.VarlinkSession = command.Process - for { - if result := p.endpointReady(); result == 0 { - break - } - fmt.Println("Waiting for varlink connection to become active", counter) - time.Sleep(250 * time.Millisecond) - counter++ - if counter > 40 { - Fail("varlink endpoint never became ready") - } - } -} - -func (p *EndpointTestIntegration) endpointReady() int { - session := p.Varlink("GetVersion", "", false) - return session.ExitCode() -} - -func (p *EndpointTestIntegration) StopVarlink() { - var out bytes.Buffer - var pids []int - varlinkSession := p.VarlinkSession - - if !rootless.IsRootless() { - if err := varlinkSession.Kill(); err != nil { - fmt.Fprintf(os.Stderr, "error on varlink stop-kill %q", err) - } - if _, err := varlinkSession.Wait(); err != nil { - fmt.Fprintf(os.Stderr, "error on varlink stop-wait %q", err) - } - - } else { - //p.ResetVarlinkAddress() - parentPid := fmt.Sprintf("%d", p.VarlinkSession.Pid) - pgrep := exec.Command("pgrep", "-P", parentPid) - fmt.Printf("running: pgrep %s\n", parentPid) - pgrep.Stdout = &out - err := pgrep.Run() - if err != nil { - fmt.Fprint(os.Stderr, "unable to find varlink pid") - } - - for _, s := range strings.Split(out.String(), "\n") { - if len(s) == 0 { - continue - } - p, err := strconv.Atoi(s) - if err != nil { - fmt.Fprintf(os.Stderr, "unable to convert %s to int", s) - } - if p != 0 { - pids = append(pids, p) - } - } - - pids = append(pids, p.VarlinkSession.Pid) - for _, pid := range pids { - syscall.Kill(pid, syscall.SIGKILL) - } - } - socket := strings.Split(p.VarlinkEndpoint, ":")[1] - if err := os.Remove(socket); err != nil { - fmt.Println(err) - } -} - -type EndpointSession struct { - *gexec.Session -} - -func getVarlinkOptions(p *EndpointTestIntegration, args []string) []string { - podmanOptions := strings.Split(fmt.Sprintf("--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s", - p.CrioRoot, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager), " ") - if os.Getenv("HOOK_OPTION") != "" { - podmanOptions = append(podmanOptions, os.Getenv("HOOK_OPTION")) - } - podmanOptions = append(podmanOptions, strings.Split(p.StorageOptions, " ")...) - podmanOptions = append(podmanOptions, args...) - return podmanOptions -} - -func (p *EndpointTestIntegration) Varlink(endpoint, message string, more bool) *EndpointSession { - //call unix:/run/user/1000/podman/io.podman/io.podman.GetContainerStats '{"name": "foobar" }' - var ( - command *exec.Cmd - ) - - args := []string{"call"} - if more { - args = append(args, "-m") - } - args = append(args, []string{fmt.Sprintf("%s/io.podman.%s", p.VarlinkEndpoint, endpoint)}...) - if len(message) > 0 { - args = append(args, message) - } - command = exec.Command(p.VarlinkBinary, args...) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - if err != nil { - Fail(fmt.Sprintf("unable to run varlink command: %s\n%v", strings.Join(args, " "), err)) - } - session.Wait(defaultWaitTimeout) - return &EndpointSession{session} -} - -func (s *EndpointSession) StdErrToString() string { - fields := strings.Fields(string(s.Err.Contents())) - return strings.Join(fields, " ") -} - -func (s *EndpointSession) OutputToString() string { - fields := strings.Fields(string(s.Out.Contents())) - return strings.Join(fields, " ") -} - -func (s *EndpointSession) OutputToBytes() []byte { - out := s.OutputToString() - return []byte(out) -} - -func (s *EndpointSession) OutputToStringMap() map[string]string { - var out map[string]string - json.Unmarshal(s.OutputToBytes(), &out) - return out -} - -func (s *EndpointSession) OutputToMapToInt() map[string]int { - var out map[string]int - json.Unmarshal(s.OutputToBytes(), &out) - return out -} - -func (s *EndpointSession) OutputToMoreResponse() iopodman.MoreResponse { - out := make(map[string]iopodman.MoreResponse) - json.Unmarshal(s.OutputToBytes(), &out) - return out["reply"] -} diff --git a/test/endpoint/endpoint_suite_test.go b/test/endpoint/endpoint_suite_test.go deleted file mode 100644 index 1cd5c2b9d..000000000 --- a/test/endpoint/endpoint_suite_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestEndpoint(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Endpoint Suite") -} - -var LockTmpDir string - -var _ = SynchronizedBeforeSuite(func() []byte { - // Cache images - cwd, _ := os.Getwd() - INTEGRATION_ROOT = filepath.Join(cwd, "../../") - podman := Setup("/tmp") - podman.ArtifactPath = ARTIFACT_DIR - if _, err := os.Stat(ARTIFACT_DIR); os.IsNotExist(err) { - if err = os.Mkdir(ARTIFACT_DIR, 0777); err != nil { - fmt.Printf("%q\n", err) - os.Exit(1) - } - } - - // make cache dir - if err := os.MkdirAll(ImageCacheDir, 0777); err != nil { - fmt.Printf("%q\n", err) - os.Exit(1) - } - - podman.StartVarlink() - for _, image := range CACHE_IMAGES { - podman.createArtifact(image) - } - podman.StopVarlink() - // If running localized tests, the cache dir is created and populated. if the - // tests are remote, this is a no-op - populateCache(podman) - - path, err := ioutil.TempDir("", "libpodlock") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - return []byte(path) -}, func(data []byte) { - LockTmpDir = string(data) -}) - -var _ = SynchronizedAfterSuite(func() {}, - func() { - podman := Setup("/tmp") - if err := os.RemoveAll(podman.CrioRoot); err != nil { - fmt.Printf("%q\n", err) - os.Exit(1) - } - if err := os.RemoveAll(podman.ImageCacheDir); err != nil { - fmt.Printf("%q\n", err) - os.Exit(1) - } - }) diff --git a/test/endpoint/exists_test.go b/test/endpoint/exists_test.go deleted file mode 100644 index f7fa8eb44..000000000 --- a/test/endpoint/exists_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "os" - - . "github.com/containers/podman/v2/test/utils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Podman exists", func() { - var ( - tempdir string - err error - endpointTest *EndpointTestIntegration - ) - - BeforeEach(func() { - tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - endpointTest = Setup(tempdir) - endpointTest.StartVarlinkWithCache() - }) - - AfterEach(func() { - endpointTest.Cleanup() - //f := CurrentGinkgoTestDescription() - //processTestResult(f) - - }) - - It("image exists in local storage", func() { - result := endpointTest.Varlink("ImageExists", makeNameMessage(ALPINE), false) - Expect(result.ExitCode()).To(BeZero()) - - output := result.OutputToMapToInt() - Expect(output["exists"]).To(BeZero()) - }) - - It("image exists in local storage by shortname", func() { - result := endpointTest.Varlink("ImageExists", makeNameMessage("alpine"), false) - Expect(result.ExitCode()).To(BeZero()) - - output := result.OutputToMapToInt() - Expect(output["exists"]).To(BeZero()) - }) - - It("image does not exist in local storage", func() { - result := endpointTest.Varlink("ImageExists", makeNameMessage("alpineforest"), false) - Expect(result.ExitCode()).To(BeZero()) - - output := result.OutputToMapToInt() - Expect(output["exists"]).To(Equal(1)) - }) - - It("container exists in local storage by name", func() { - _ = endpointTest.startTopContainer("top") - result := endpointTest.Varlink("ContainerExists", makeNameMessage("top"), false) - Expect(result.ExitCode()).To(BeZero()) - output := result.OutputToMapToInt() - Expect(output["exists"]).To(BeZero()) - }) - -}) diff --git a/test/endpoint/pull_test.go b/test/endpoint/pull_test.go deleted file mode 100644 index b3683b7db..000000000 --- a/test/endpoint/pull_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "os" - - . "github.com/containers/podman/v2/test/utils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Podman pull", func() { - var ( - tempdir string - err error - endpointTest *EndpointTestIntegration - ) - - BeforeEach(func() { - tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - endpointTest = Setup(tempdir) - endpointTest.StartVarlink() - }) - - AfterEach(func() { - endpointTest.Cleanup() - //f := CurrentGinkgoTestDescription() - //processTestResult(f) - - }) - - It("podman pull", func() { - session := endpointTest.Varlink("PullImage", makeNameMessage(ALPINE), false) - Expect(session.ExitCode()).To(BeZero()) - - result := endpointTest.Varlink("ImageExists", makeNameMessage(ALPINE), false) - Expect(result.ExitCode()).To(BeZero()) - - output := result.OutputToMapToInt() - Expect(output["exists"]).To(BeZero()) - }) -}) diff --git a/test/endpoint/setup.go b/test/endpoint/setup.go deleted file mode 100644 index 6bbc8d2bc..000000000 --- a/test/endpoint/setup.go +++ /dev/null @@ -1,214 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/containers/podman/v2/pkg/rootless" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/containers/storage/pkg/stringid" - "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/sirupsen/logrus" -) - -func Setup(tempDir string) *EndpointTestIntegration { - var ( - endpoint string - ) - cwd, _ := os.Getwd() - INTEGRATION_ROOT = filepath.Join(cwd, "../../") - - podmanBinary := filepath.Join(cwd, "../../bin/podman") - if os.Getenv("PODMAN_BINARY") != "" { - podmanBinary = os.Getenv("PODMAN_BINARY") - } - conmonBinary := filepath.Join("/usr/libexec/podman/conmon") - altConmonBinary := "/usr/bin/conmon" - if _, err := os.Stat(conmonBinary); os.IsNotExist(err) { - conmonBinary = altConmonBinary - } - if os.Getenv("CONMON_BINARY") != "" { - conmonBinary = os.Getenv("CONMON_BINARY") - } - storageOptions := STORAGE_OPTIONS - if os.Getenv("STORAGE_OPTIONS") != "" { - storageOptions = os.Getenv("STORAGE_OPTIONS") - } - cgroupManager := CGROUP_MANAGER - if rootless.IsRootless() { - cgroupManager = "cgroupfs" - } - if os.Getenv("CGROUP_MANAGER") != "" { - cgroupManager = os.Getenv("CGROUP_MANAGER") - } - - ociRuntime := os.Getenv("OCI_RUNTIME") - if ociRuntime == "" { - ociRuntime = "runc" - } - os.Setenv("DISABLE_HC_SYSTEMD", "true") - CNIConfigDir := "/etc/cni/net.d" - - storageFs := STORAGE_FS - if rootless.IsRootless() { - storageFs = ROOTLESS_STORAGE_FS - } - - uuid := stringid.GenerateNonCryptoID() - if !rootless.IsRootless() { - endpoint = fmt.Sprintf("unix:/run/podman/io.podman-%s", uuid) - } else { - runtimeDir := os.Getenv("XDG_RUNTIME_DIR") - socket := fmt.Sprintf("io.podman-%s", uuid) - fqpath := filepath.Join(runtimeDir, socket) - endpoint = fmt.Sprintf("unix:%s", fqpath) - } - - eti := EndpointTestIntegration{ - ArtifactPath: ARTIFACT_DIR, - CNIConfigDir: CNIConfigDir, - CgroupManager: cgroupManager, - ConmonBinary: conmonBinary, - CrioRoot: filepath.Join(tempDir, "crio"), - ImageCacheDir: ImageCacheDir, - ImageCacheFS: storageFs, - OCIRuntime: ociRuntime, - PodmanBinary: podmanBinary, - RunRoot: filepath.Join(tempDir, "crio-run"), - SignaturePolicyPath: filepath.Join(INTEGRATION_ROOT, "test/policy.json"), - StorageOptions: storageOptions, - TmpDir: tempDir, - // Timings: nil, - VarlinkBinary: VarlinkBinary, - VarlinkCommand: nil, - VarlinkEndpoint: endpoint, - VarlinkSession: nil, - } - return &eti -} - -func (p *EndpointTestIntegration) Cleanup() { - // Remove all containers - // TODO Make methods to do all this? - - p.stopAllContainers() - - // TODO need to make stop all pods - - p.StopVarlink() - // Nuke tempdir - if err := os.RemoveAll(p.TmpDir); err != nil { - fmt.Printf("%q\n", err) - } - - // Clean up the registries configuration file ENV variable set in Create - resetRegistriesConfigEnv() -} - -func (p *EndpointTestIntegration) listContainers() []iopodman.Container { - containers := p.Varlink("ListContainers", "", false) - var varlinkContainers map[string][]iopodman.Container - if err := json.Unmarshal(containers.OutputToBytes(), &varlinkContainers); err != nil { - logrus.Error("failed to unmarshal containers") - } - return varlinkContainers["containers"] -} - -func (p *EndpointTestIntegration) stopAllContainers() { - containers := p.listContainers() - for _, container := range containers { - p.stopContainer(container.Id) - } -} - -func (p *EndpointTestIntegration) stopContainer(cid string) { - p.Varlink("StopContainer", fmt.Sprintf("{\"name\":\"%s\", \"timeout\":0}", cid), false) -} - -func resetRegistriesConfigEnv() { - os.Setenv("REGISTRIES_CONFIG_PATH", "") -} - -func (p *EndpointTestIntegration) createArtifact(image string) { - if os.Getenv("NO_TEST_CACHE") != "" { - return - } - dest := strings.Split(image, "/") - destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1)) - fmt.Printf("Caching %s at %s...", image, destName) - if _, err := os.Stat(destName); os.IsNotExist(err) { - pull := p.Varlink("PullImage", fmt.Sprintf("{\"name\":\"%s\"}", image), false) - Expect(pull.ExitCode()).To(Equal(0)) - - imageSave := iopodman.ImageSaveOptions{ - // Name:image, - // Output: destName, - // Format: "oci-archive", - } - imageSave.Name = image - imageSave.Output = destName - imageSave.Format = "oci-archive" - foo := make(map[string]iopodman.ImageSaveOptions) - foo["options"] = imageSave - f, _ := json.Marshal(foo) - save := p.Varlink("ImageSave", string(f), false) - result := save.OutputToMoreResponse() - Expect(save.ExitCode()).To(Equal(0)) - Expect(os.Rename(result.Id, destName)).To(BeNil()) - fmt.Printf("\n") - } else { - fmt.Printf(" already exists.\n") - } -} - -func populateCache(p *EndpointTestIntegration) { - p.CrioRoot = p.ImageCacheDir - p.StartVarlink() - for _, image := range CACHE_IMAGES { - p.RestoreArtifactToCache(image) - } - p.StopVarlink() -} - -func (p *EndpointTestIntegration) RestoreArtifactToCache(image string) error { - fmt.Printf("Restoring %s...\n", image) - dest := strings.Split(image, "/") - destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1)) - // fmt.Println(destName, p.ImageCacheDir) - load := p.Varlink("LoadImage", fmt.Sprintf("{\"name\": \"%s\", \"inputFile\": \"%s\"}", image, destName), false) - Expect(load.ExitCode()).To(BeZero()) - return nil -} - -func (p *EndpointTestIntegration) startTopContainer(name string) string { - t := true - args := iopodman.Create{ - Args: []string{"docker.io/library/alpine:latest", "top"}, - Tty: &t, - Detach: &t, - } - if len(name) > 0 { - args.Name = &name - } - b, err := json.Marshal(args) - if err != nil { - ginkgo.Fail("failed to marshal data for top container") - } - input := fmt.Sprintf("{\"create\":%s}", string(b)) - top := p.Varlink("CreateContainer", input, false) - if top.ExitCode() != 0 { - ginkgo.Fail("failed to start top container") - } - start := p.Varlink("StartContainer", fmt.Sprintf("{\"name\":\"%s\"}", name), false) - if start.ExitCode() != 0 { - ginkgo.Fail("failed to start top container") - } - return start.OutputToString() -} diff --git a/test/endpoint/version_test.go b/test/endpoint/version_test.go deleted file mode 100644 index b1c8ad867..000000000 --- a/test/endpoint/version_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "os" - - . "github.com/containers/podman/v2/test/utils" - "github.com/containers/podman/v2/version" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Podman version", func() { - var ( - tempdir string - err error - endpointTest *EndpointTestIntegration - ) - - BeforeEach(func() { - tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - endpointTest = Setup(tempdir) - endpointTest.StartVarlink() - }) - - AfterEach(func() { - endpointTest.Cleanup() - //f := CurrentGinkgoTestDescription() - //processTestResult(f) - - }) - - It("podman version", func() { - session := endpointTest.Varlink("GetVersion", "", false) - result := session.OutputToStringMap() - Expect(result["version"]).To(Equal(version.Version)) - Expect(session.ExitCode()).To(Equal(0)) - }) -}) |