github.com/ubuntu/ubuntu-report@v1.7.4-0.20240410144652-96f37d845fac/pkg/sysmetrics/C/libsysmetrics_test.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "bytes" 6 "context" 7 "io/ioutil" 8 "net/http" 9 "net/http/httptest" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/ubuntu/ubuntu-report/internal/helper" 18 ) 19 20 // The actual test functions are in non-_test.go files 21 // so that they can use cgo (import "C"). 22 // These wrappers are here for gotest to find. 23 // Similar technic than in https://golang.org/misc/cgo/test/cgo_test.go 24 func TestCollect(t *testing.T) { testCollect(t) } 25 func TestSendReport(t *testing.T) { testSendReport(t) } 26 func TestSendDecline(t *testing.T) { testSendDecline(t) } 27 func TestNonInteractiveCollectAndSend(t *testing.T) { testNonInteractiveCollectAndSend(t) } 28 func TestInteractiveCollectAndSend(t *testing.T) { testInteractiveCollectAndSend(t) } 29 30 func TestCollectExample(t *testing.T) { 31 helper.SkipIfShort(t) 32 t.Parallel() 33 ensureGCC(t) 34 35 out, tearDown := helper.TempDir(t) 36 defer tearDown() 37 lib := buildLib(t, out) 38 p := extractExampleFromDoc(t, out, "Collect system info", "", "") 39 binary := buildExample(t, out, p, lib) 40 41 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 42 defer cancel() 43 cmd := exec.CommandContext(ctx, binary) 44 cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+out) 45 data, err := cmd.CombinedOutput() 46 47 if err != nil { 48 t.Fatal("we didn't expect an error and got one", err) 49 } 50 51 if !strings.Contains(string(data), expectedReportItem) { 52 t.Errorf("we expected at least %s in output, got: '%s", expectedReportItem, string(data)) 53 } 54 } 55 56 func TestSendReportExample(t *testing.T) { 57 helper.SkipIfShort(t) 58 t.Parallel() 59 ensureGCC(t) 60 61 a := helper.Asserter{T: t} 62 63 serverHit := false 64 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 65 serverHit = true 66 })) 67 defer ts.Close() 68 69 out, tearDown := helper.TempDir(t) 70 defer tearDown() 71 72 lib := buildLib(t, out) 73 p := extractExampleFromDoc(t, out, "Send provided metrics data to server", `""`, `"`+ts.URL+`"`) 74 binary := buildExample(t, out, p, lib) 75 76 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 77 defer cancel() 78 cmd := exec.CommandContext(ctx, binary) 79 cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+out, "XDG_CACHE_HOME="+out) 80 err := cmd.Run() 81 82 if err != nil { 83 t.Fatal("we didn't expect an error and got one", err) 84 } 85 86 // There isn't a data race as only the external binary can hit test server, 87 // but Go can't know it. To prevent that, shutdown the test server explicitly 88 ts.Close() 89 90 a.Equal(serverHit, true) 91 xdgP := filepath.Join(out, "ubuntu-report") 92 p = filepath.Join(xdgP, helper.FindInDirectory(t, "", xdgP)) 93 data, err := ioutil.ReadFile(p) 94 if err != nil { 95 t.Fatalf("couldn't open report file %s", p) 96 } 97 d := string(data) 98 99 if !strings.Contains(d, expectedReportItem) { 100 t.Errorf("we expected to find %s in report file, got: %s", expectedReportItem, d) 101 } 102 } 103 104 func TestSendDeclineExample(t *testing.T) { 105 helper.SkipIfShort(t) 106 t.Parallel() 107 ensureGCC(t) 108 109 a := helper.Asserter{T: t} 110 111 serverHit := false 112 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 113 serverHit = true 114 })) 115 defer ts.Close() 116 117 out, tearDown := helper.TempDir(t) 118 defer tearDown() 119 120 lib := buildLib(t, out) 121 p := extractExampleFromDoc(t, out, "Send denial message to server", `""`, `"`+ts.URL+`"`) 122 binary := buildExample(t, out, p, lib) 123 124 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 125 defer cancel() 126 cmd := exec.CommandContext(ctx, binary) 127 cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+out, "XDG_CACHE_HOME="+out) 128 err := cmd.Run() 129 130 if err != nil { 131 t.Fatal("we didn't expect an error and got one", err) 132 } 133 134 // There isn't a data race as only the external binary can hit test server, 135 // but Go can't know it. To prevent that, shutdown the test server explicitly 136 ts.Close() 137 138 a.Equal(serverHit, true) 139 xdgP := filepath.Join(out, "ubuntu-report") 140 p = filepath.Join(xdgP, helper.FindInDirectory(t, "", xdgP)) 141 data, err := ioutil.ReadFile(p) 142 if err != nil { 143 t.Fatalf("couldn't open report file %s", p) 144 } 145 d := string(data) 146 147 if !strings.Contains(d, optOutJSON) { 148 t.Errorf("we expected to find %s in report file, got: %s", optOutJSON, d) 149 } 150 } 151 152 func TestCollectAndSendExample(t *testing.T) { 153 helper.SkipIfShort(t) 154 t.Parallel() 155 ensureGCC(t) 156 157 a := helper.Asserter{T: t} 158 159 serverHit := false 160 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 161 serverHit = true 162 })) 163 defer ts.Close() 164 165 out, tearDown := helper.TempDir(t) 166 defer tearDown() 167 168 lib := buildLib(t, out) 169 p := extractExampleFromDoc(t, out, "Collect and send system info to server", `""`, `"`+ts.URL+`"`) 170 binary := buildExample(t, out, p, lib) 171 172 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 173 defer cancel() 174 cmd := exec.CommandContext(ctx, binary) 175 cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+out, "XDG_CACHE_HOME="+out) 176 err := cmd.Run() 177 178 if err != nil { 179 t.Fatal("we didn't expect an error and got one", err) 180 } 181 182 // There isn't a data race as only the external binary can hit test server, 183 // but Go can't know it. To prevent that, shutdown the test server explicitly 184 ts.Close() 185 186 a.Equal(serverHit, true) 187 xdgP := filepath.Join(out, "ubuntu-report") 188 p = filepath.Join(xdgP, helper.FindInDirectory(t, "", xdgP)) 189 data, err := ioutil.ReadFile(p) 190 if err != nil { 191 t.Fatalf("couldn't open report file %s", p) 192 } 193 d := string(data) 194 195 if !strings.Contains(d, expectedReportItem) { 196 t.Errorf("we expected to find %s in report file, got: %s", expectedReportItem, d) 197 } 198 } 199 200 func ensureGCC(t *testing.T) { 201 if _, err := exec.LookPath("gcc"); err != nil { 202 t.Skip("skipping test: no gcc found:", err) 203 } 204 } 205 206 func buildExample(t *testing.T, dest, example, lib string) string { 207 t.Helper() 208 209 d := filepath.Join(dest, "example") 210 cmd := exec.Command("gcc", "-I", dest, "-o", d, example, lib) 211 var out bytes.Buffer 212 cmd.Stderr = &out 213 if err := cmd.Run(); err != nil { 214 t.Fatal("couldn't build example binary:", err, "\n", out.String()) 215 } 216 return d 217 } 218 219 func buildLib(t *testing.T, p string) string { 220 t.Helper() 221 libName := "libsysmetrics.so.1" 222 d := filepath.Join(p, libName) 223 cmd := exec.Command("go", "build", "-o", d, "-buildmode=c-shared", "-ldflags", "-extldflags -Wl,-soname,"+libName, "libsysmetrics.go") 224 if err := cmd.Run(); err != nil { 225 t.Fatal("couldn't build library:", err) 226 } 227 if err := os.Rename(filepath.Join(p, "libsysmetrics.so.h"), filepath.Join(p, "libsysmetrics.h")); err != nil { 228 t.Fatal("couldn't rename header file", err) 229 } 230 return d 231 } 232 233 func extractExampleFromDoc(t *testing.T, dir, title, pattern, replace string) string { 234 t.Helper() 235 236 f, err := os.Open("doc.go") 237 if err != nil { 238 t.Fatal("couldn't open documentation file:", err) 239 } 240 defer f.Close() 241 242 p := filepath.Join(dir, strings.Replace(strings.ToLower(title), " ", "_", -1)+".c") 243 w, err := os.Create(p) 244 if err != nil { 245 t.Fatal("couldn't create example file:", err) 246 } 247 defer w.Close() 248 249 scanner := bufio.NewScanner(f) 250 correctSection := false 251 inExample := false 252 for scanner.Scan() { 253 txt := strings.TrimPrefix(scanner.Text(), "//") 254 if strings.HasPrefix(txt, " "+title) { 255 correctSection = true 256 continue 257 } 258 if !correctSection { 259 continue 260 } 261 if strings.HasPrefix(txt, " Example") { 262 inExample = true 263 continue 264 } 265 if !inExample { 266 continue 267 } 268 // end of example: no space separated content, nor empty line 269 if !(strings.HasPrefix(txt, " ") || txt == "") { 270 break 271 } 272 txt = strings.Replace(strings.TrimPrefix(txt, " "), pattern, replace, -1) 273 if _, err := w.WriteString(txt + "\n"); err != nil { 274 t.Fatalf("couldn't write '%s' to destination example file: %v", txt, err) 275 } 276 } 277 278 return p 279 }