| // Licensed to the Apache Software Foundation (ASF) under one or more | |
| // contributor license agreements. See the NOTICE file distributed with | |
| // this work for additional information regarding copyright ownership. | |
| // The ASF licenses this file to You under the Apache License, Version 2.0 | |
| // (the "License"); you may not use this file except in compliance with | |
| // the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package main | |
| import ( | |
| "context" | |
| "os" | |
| "path/filepath" | |
| "testing" | |
| "github.com/apache/beam/sdks/v2/go/container/tools" | |
| "google.golang.org/protobuf/types/known/structpb" | |
| ) | |
| func TestActivePidsRegistry(t *testing.T) { | |
| // Reset active PIDs | |
| activePids = nil | |
| activePids = append(activePids, 101) | |
| activePids = append(activePids, 102) | |
| pids := getActivePids() | |
| if len(pids) != 2 || pids[0] != 101 || pids[1] != 102 { | |
| t.Errorf("Expected active pids [101, 102], got %v", pids) | |
| } | |
| unregisterPid(101) | |
| pids = getActivePids() | |
| if len(pids) != 1 || pids[0] != 102 { | |
| t.Errorf("Expected active pids [102], got %v", pids) | |
| } | |
| unregisterPid(102) | |
| pids = getActivePids() | |
| if len(pids) != 0 { | |
| t.Errorf("Expected active pids empty, got %v", pids) | |
| } | |
| } | |
| func TestSetupProfilerConfig(t *testing.T) { | |
| st, err := structpb.NewStruct(map[string]interface{}{ | |
| "options": map[string]interface{}{ | |
| "profiler_agent": "coredump", | |
| "jobId": "test-job", | |
| }, | |
| }) | |
| if err != nil { | |
| t.Fatalf("Failed to create structpb: %v", err) | |
| } | |
| po := tools.ParseOptionsFromProto(st, "") | |
| ctx := setupProfilerConfig(context.Background(), &tools.Logger{}, po) | |
| pcfg := getProfilerConfig(ctx) | |
| if pcfg == nil { | |
| t.Fatal("ProfilerConfig was nil") | |
| } | |
| if pcfg.Agent != "coredump" { | |
| t.Errorf("Expected agent coredump, got %s", pcfg.Agent) | |
| } | |
| } | |
| func TestIsProfilerDisengaged(t *testing.T) { | |
| tempDir, err := os.MkdirTemp("", "disengage_test") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer os.RemoveAll(tempDir) | |
| sentinelPath := filepath.Join(tempDir, "stop_sentinel") | |
| pcfg := &ProfilerConfig{ | |
| StopSentinelPath: sentinelPath, | |
| } | |
| if isProfilerDisengaged(pcfg) { | |
| t.Error("Expected profiler NOT to be disengaged before sentinel creation") | |
| } | |
| // Create sentinel file | |
| if err := os.WriteFile(sentinelPath, []byte{}, 0644); err != nil { | |
| t.Fatal(err) | |
| } | |
| if !isProfilerDisengaged(pcfg) { | |
| t.Error("Expected profiler to be disengaged after sentinel creation") | |
| } | |
| } | |
| func TestCreatePystackSummary(t *testing.T) { | |
| t.Run("ExtractsGILThreadTrace", func(t *testing.T) { | |
| output := "Thread 1 (waiting):\n" + | |
| " File \"worker.py\", line 10, in run\n" + | |
| "\n" + | |
| "Thread 2 (active, Has the GIL):\n" + | |
| " File \"main.py\", line 42, in execute\n" + | |
| " File \"db.py\", line 5, in query\n" + | |
| "\n" + | |
| "Thread 3 (idle):\n" + | |
| " File \"server.py\", line 99, in listen\n" | |
| expected := "Thread 2 (active, Has the GIL):\n" + | |
| " File \"main.py\", line 42, in execute\n" + | |
| " File \"db.py\", line 5, in query\n" | |
| result := createPystackSummary(output) | |
| if result != expected { | |
| t.Errorf("Expected:\n%s\nGot:\n%s", expected, result) | |
| } | |
| }) | |
| t.Run("FallbackSmallOutput", func(t *testing.T) { | |
| output := "Thread 1 (waiting):\n" + | |
| " File \"worker.py\", line 10, in run" | |
| result := createPystackSummary(output) | |
| if result != output { | |
| t.Errorf("Expected identical output, got:\n%s", result) | |
| } | |
| }) | |
| } |