Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / modules / canvaskit / go / gold_test_env / gold_test_env.go
1 // Copyright 2022 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package main
6
7 import (
8         "encoding/base64"
9         "encoding/json"
10         "errors"
11         "fmt"
12         "io"
13         "io/ioutil"
14         "net"
15         "net/http"
16         "os"
17         "os/signal"
18         "path"
19         "path/filepath"
20         "strconv"
21         "syscall"
22 )
23
24 const (
25         envPortFileBaseName = "port"
26 )
27
28 func main() {
29         envDir, envReadyFile := mustGetEnvironmentVariables()
30
31         port, listener := mustGetUnusedNetworkPort()
32
33         beginTestManagementLogic(listener)
34
35         mustPrepareTestEnvironment(envDir, port)
36
37         setupTerminationLogic()
38
39         mustSignalTestsCanBegin(envReadyFile)
40
41         select {} // Block until the termination handler calls os.Exit
42 }
43
44 // mustGetEnvironmentVariables returns two file paths: a directory that can be used to communicate
45 // between this binary and the test binaries, and the file that needs to be created when this
46 // binary has finished setting things up. It panics if it cannot read the values from the
47 // set environment variables.
48 func mustGetEnvironmentVariables() (string, string) {
49         // Read in build paths to the ready and port files.
50         envDir := os.Getenv("ENV_DIR")
51         if envDir == "" {
52                 panic("required environment variable ENV_DIR is unset")
53         }
54         envReadyFile := os.Getenv("ENV_READY_FILE")
55         if envReadyFile == "" {
56                 panic("required environment variable ENV_READY_FILE is unset")
57         }
58         return envDir, envReadyFile
59 }
60
61 // mustGetUnusedNetworkPort returns a network port chosen by the OS (and assumed to be previously
62 // unused) and a listener for that port. We choose a non-deterministic port instead of a fixed port
63 // because multiple tests may be running in parallel.
64 func mustGetUnusedNetworkPort() (int, net.Listener) {
65         // Listen on an unused port chosen by the OS.
66         listener, err := net.Listen("tcp", ":0")
67         if err != nil {
68                 panic(err)
69         }
70         port := listener.Addr().(*net.TCPAddr).Port
71         fmt.Printf("Environment is ready to go!\nListening on port %d.\n", port)
72         return port, listener
73 }
74
75 // beginTestManagementLogic sets up the server endpoints which allow the JS gm() tests to exfiltrate
76 // their PNG images by means of a POST request.
77 func beginTestManagementLogic(listener net.Listener) {
78         // The contents of this path go to //bazel-testlogs/path/to/test/test.outputs/ and are combined
79         // into outputs.zip.
80         // e.g. ls bazel-testlogs/modules/canvaskit/hello_world_test_with_env/test.outputs/
81         //   test_001
82         //   test_002
83         //   outputs.zip   # contains test_001 and test_002
84         // This environment var is documented in https://bazel.build/reference/test-encyclopedia
85         outPath := os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR")
86         if outPath == "" {
87                 panic("output directory was not configured")
88         }
89
90         http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
91                 w.WriteHeader(http.StatusOK)
92         })
93
94         http.HandleFunc("/report", func(w http.ResponseWriter, r *http.Request) {
95                 payload, err := readPayload(r)
96                 if err != nil {
97                         http.Error(w, err.Error(), http.StatusBadRequest)
98                 }
99                 if payload.TestName == "" {
100                         http.Error(w, "Must specify test name", http.StatusBadRequest)
101                         return
102                 }
103                 // Write the data in the POST to the special Bazel output directory
104                 fileContents, err := base64.StdEncoding.DecodeString(payload.Base64Data)
105                 if err != nil {
106                     fmt.Printf("Invalid base64 data: %s\n", err.Error())
107                         http.Error(w, "Invalid base64 data "+err.Error(), http.StatusBadRequest)
108                         return
109                 }
110                 fp := filepath.Join(outPath, payload.TestName)
111                 // Two newlines here makes the log stick out more.
112                 fmt.Printf("Writing test data to %s\n\n", fp)
113                 out, err := os.Create(fp)
114                 if err != nil {
115                         http.Error(w, err.Error(), http.StatusInternalServerError)
116                         panic(err)
117                 }
118                 if _, err := out.Write(fileContents); err != nil {
119                         http.Error(w, err.Error(), http.StatusInternalServerError)
120                         panic(err)
121                 }
122
123                 // Signal to the test that we have written the data to disk. Tests should be sure to wait
124                 // for this response before signaling they are done to avoid a race condition.
125                 w.WriteHeader(http.StatusCreated)
126                 // We are not worried about an XSS reflection attack here on a local server only up
127                 // when running tests.
128                 if _, err := fmt.Fprintln(w, "Accepted for test "+payload.TestName); err != nil {
129                         panic(err)
130                 }
131         })
132         go func() {
133                 serveForever(listener)
134         }()
135 }
136
137 type testPayload struct {
138         TestName   string `json:"name"`
139         Base64Data string `json:"b64_data"`
140 }
141
142 // readPayload reads the body of the given request as JSON and parses it into a testPayload struct.
143 func readPayload(r *http.Request) (testPayload, error) {
144         var payload testPayload
145         if r.Body == nil {
146                 return payload, errors.New("no body received")
147         }
148         b, err := io.ReadAll(r.Body)
149         if err != nil {
150                 return payload, err
151         }
152         _ = r.Body.Close()
153         if err := json.Unmarshal(b, &payload); err != nil {
154                 return payload, errors.New("invalid JSON")
155         }
156         return payload, nil
157 }
158
159 // serveForever serves the given listener and blocks. If it could not start serving, it will panic.
160 func serveForever(listener net.Listener) {
161         // If http.Serve returns, it is an error.
162         if err := http.Serve(listener, nil); err != nil {
163                 panic(fmt.Sprintf("Finished serving due to error: %s\n", err))
164         }
165 }
166
167 // mustPrepareTestEnvironment writes any files to the temporary test directory. This is just a file
168 // that indicates which port the gold tests should make POST requests to. It panics if there are
169 // any errors.
170 func mustPrepareTestEnvironment(dirTestsCanRead string, port int) {
171         envPortFile := path.Join(dirTestsCanRead, envPortFileBaseName)
172         if err := ioutil.WriteFile(envPortFile, []byte(strconv.Itoa(port)), 0644); err != nil {
173                 panic(err)
174         }
175 }
176
177 // setupTerminationLogic creates a handler for SIGTERM which is what test_on_env will send the
178 // environment when the tests complete. There is currently nothing to do other than exit.
179 func setupTerminationLogic() {
180         c := make(chan os.Signal, 1)
181         go func() {
182                 <-c
183                 os.Exit(0)
184         }()
185         signal.Notify(c, syscall.SIGTERM)
186 }
187
188 // mustSignalTestsCanBegin creates the agreed upon ENV_READY_FILE which signals the test binary can
189 // be executed by Bazel. See test_on_env.bzl for more. It panics if the file cannot be created.
190 func mustSignalTestsCanBegin(envReadyFile string) {
191         if err := ioutil.WriteFile(envReadyFile, []byte{}, 0644); err != nil {
192                 panic(err)
193         }
194 }