@@ -59,6 +59,90 @@ func TestBpfProfiler_SetUp(t *testing.T) {
5959 assert .Equal (t , []string {"PID_ContainerID" }, fields .BpfProfiler .targetPIDs )
6060 },
6161 },
62+ {
63+ name : "should invoke for Raw output type" ,
64+ given : func () (fields , args ) {
65+ log .SetPrintLogs (true )
66+ // Create a dummy raw file that would be created by bcc-profiler
67+ rawContent := "main;func_a;func_b 10\n main;func_c 5"
68+ // The initial raw file created by the profiler command (before PID legend is added)
69+ // For testing, we simulate its creation and content.
70+ // The actual test will verify the final raw file (after PID legend addition and potential copy for Raw type).
71+ // This initial raw file is not directly used by the assertions but represents the state before handleFlamegraph.
72+
73+ commander := executil .NewFakeCommander ()
74+ // Mock the bcc-profiler command to simulate it writing to stdout, which then gets written to a file.
75+ // For simplicity in testing, we'll assume the file is created with expected content before handleFlamegraph.
76+ // The critical part is what handleFlamegraph does with it.
77+ commander .On ("Command" ).Return (exec .Command ("echo" , rawContent )) // Simulate command output
78+ publisher := publish .NewFakePublisher ()
79+ publisher .On ("Do" ).Return (nil ) // Assume publisher works
80+
81+ return fields {
82+ BpfProfiler : NewBpfProfiler (commander , publisher ),
83+ }, args {
84+ job : & job.ProfilingJob {
85+ Duration : 0 ,
86+ ContainerRuntime : api .FakeContainer ,
87+ ContainerID : "ContainerID" ,
88+ OutputType : api .Raw , // Test for Raw output
89+ Language : api .FakeLang ,
90+ Tool : api .Bpf ,
91+ Compressor : compressor .None ,
92+ Iteration : 1 ,
93+ },
94+ pid : "2000" , // Use a different PID for this test case
95+ }
96+ },
97+ when : func (fields fields , args args ) (error , time.Duration ) {
98+ // Before invoking, ensure the simulated raw file from bcc-profiler exists
99+ // This file would be the input to handleFlamegraph
100+ initialRawFileName := common .GetResultFile (common .TmpDir (), args .job .Tool , api .Raw , args .pid , args .job .Iteration )
101+ // The content that would be written by `file.Write(fileName, addProcessPIDLegend(out.String(), pid))`
102+ // For this test, we'll manually create this file to simulate the state right before handleFlamegraph
103+ // and ensure its content is as expected (with PID legend) after the copy operation.
104+ // The actual command output simulation is simplified here. The key is the file operations.
105+
106+ // Simulate the output of bccProfilerCommand and subsequent file write with PID legend
107+ // This is the file that handleFlamegraph will operate on (as rawFileName)
108+ // And for Raw output, this should be copied to resultFileName if their names differ,
109+ // or just be the resultFileName directly if names are the same.
110+ // From the code: resultFileName for Raw is `profiling-prefix-raw-PID-ITERATION.txt`
111+ // The file from bcc-profiler (after PID legend) is also named this way. So no copy needed, just existence.
112+ dummyRawContentWithLegend := "main;func_a;func_b 2000_10\n main;func_c 2000_5"
113+ file .Write (initialRawFileName , dummyRawContentWithLegend )
114+
115+ return fields .BpfProfiler .invoke (args .job , args .pid )
116+ },
117+ then : func (t * testing.T , fields fields , err error ) {
118+ assert .Nil (t , err )
119+ // Verify the final output file for Raw type exists and has the correct content
120+ expectedOutputFileName := common .GetResultFile (common .TmpDir (), api .Bpf , api .Raw , "2000" , 1 )
121+ assert .True (t , file .Exists (expectedOutputFileName ), "Expected raw output file to exist" )
122+
123+ // Verify content includes PID legend (simulated)
124+ // This part depends on the mock setup; if bccProfilerCommand was truly mocked to output specific content,
125+ // then we'd check for that content plus PID legend here.
126+ // Given the current simplified mock, we check for the existence and that publisher was called.
127+ // Content check:
128+ rawFileContent , _ := os .ReadFile (expectedOutputFileName )
129+ assert .Contains (t , string (rawFileContent ), "2000_" ) // Check if PID legend was added (or was part of simulated write)
130+
131+
132+ // Ensure publisher is called with the .txt file
133+ publisherMock := fields .BpfProfiler .BpfManager .(* bpfManager ).publisher .(* publish.Fake )
134+ assert .True (t , publisherMock .On ("Do" ).InvokedTimes () == 1 )
135+ // Check arguments of publisher.Do if needed, especially the filename
136+
137+ // Ensure StackSamplesToFlameGraph was NOT called
138+ // This needs a way to inspect the flamegrapher mock, which is not directly accessible here
139+ // This check is better suited for Test_bpfManager_handleFlamegraph
140+ },
141+ after : func () {
142+ // Clean up the created raw file
143+ _ = file .Remove (common .GetResultFile (common .TmpDir (), api .Bpf , api .Raw , "2000" , 1 ))
144+ },
145+ },
62146 {
63147 name : "should setup with given PID" ,
64148 given : func () (fields , args ) {
@@ -580,6 +664,77 @@ func Test_bpfManager_handleFlamegraph(t *testing.T) {
580664 _ = file .Remove (filepath .Join (common .TmpDir (), config .ProfilingPrefix + "raw.txt" ))
581665 },
582666 },
667+ {
668+ name : "should handle Raw output type by copying the file" ,
669+ given : func () (fields , args ) {
670+ rawContent := "main;func_a;func_b 1000_10\n main;func_c 1000_5"
671+ rawFileName := filepath .Join (common .TmpDir (), config .ProfilingPrefix + "input-raw.txt" )
672+ resultFileName := filepath .Join (common .TmpDir (), config .ProfilingPrefix + "output-raw.txt" )
673+
674+ _ = os .WriteFile (rawFileName , []byte (rawContent ), 0644 )
675+
676+ return fields {
677+ BpfProfiler : NewBpfProfiler (executil .NewFakeCommander (), publish .NewFakePublisher ()),
678+ }, args {
679+ job : & job.ProfilingJob {
680+ OutputType : api .Raw ,
681+ },
682+ flameGrapher : flamegraph .NewFlameGrapherFake (),
683+ fileName : rawFileName ,
684+ resultFileName : resultFileName ,
685+ }
686+ },
687+ when : func (fields fields , args args ) error {
688+ return fields .BpfProfiler .handleFlamegraph (args .job , args .flameGrapher , args .fileName , args .resultFileName )
689+ },
690+ then : func (t * testing.T , err error , flameGrapher flamegraph.FrameGrapher ) {
691+ assert .Nil (t , err )
692+ // Ensure StackSamplesToFlameGraph was NOT called
693+ assert .False (t , flameGrapher .(* flamegraph.FlameGrapherFake ).StackSamplesToFlameGraphInvoked )
694+ // Verify the result file exists and has the same content as the raw file
695+ assert .True (t , file .Exists (filepath .Join (common .TmpDir (), config .ProfilingPrefix + "output-raw.txt" )))
696+ rawContent , _ := os .ReadFile (filepath .Join (common .TmpDir (), config .ProfilingPrefix + "input-raw.txt" ))
697+ resultContent , _ := os .ReadFile (filepath .Join (common .TmpDir (), config .ProfilingPrefix + "output-raw.txt" ))
698+ assert .Equal (t , string (rawContent ), string (resultContent ))
699+ },
700+ after : func () {
701+ _ = file .Remove (filepath .Join (common .TmpDir (), config .ProfilingPrefix + "input-raw.txt" ))
702+ _ = file .Remove (filepath .Join (common .TmpDir (), config .ProfilingPrefix + "output-raw.txt" ))
703+ },
704+ },
705+ {
706+ name : "should fail handle Raw output type when copy fails" ,
707+ given : func () (fields , args ) {
708+ // Simulate a scenario where the source raw file does not exist, causing copy to fail
709+ rawFileName := filepath .Join (common .TmpDir (), config .ProfilingPrefix + "nonexistent-raw.txt" )
710+ resultFileName := filepath .Join (common .TmpDir (), config .ProfilingPrefix + "output-raw.txt" )
711+
712+ return fields {
713+ BpfProfiler : NewBpfProfiler (executil .NewFakeCommander (), publish .NewFakePublisher ()),
714+ }, args {
715+ job : & job.ProfilingJob {
716+ OutputType : api .Raw ,
717+ },
718+ flameGrapher : flamegraph .NewFlameGrapherFake (),
719+ fileName : rawFileName , // This file won't exist
720+ resultFileName : resultFileName ,
721+ }
722+ },
723+ when : func (fields fields , args args ) error {
724+ // Temporarily mock file.Copy to simulate failure if not using a real non-existent file path that causes error
725+ // For this test, relying on non-existent file is simpler.
726+ return fields .BpfProfiler .handleFlamegraph (args .job , args .flameGrapher , args .fileName , args .resultFileName )
727+ },
728+ then : func (t * testing.T , err error , flameGrapher flamegraph.FrameGrapher ) {
729+ assert .NotNil (t , err )
730+ assert .Contains (t , err .Error (), "could not copy raw file to output file" )
731+ assert .False (t , flameGrapher .(* flamegraph.FlameGrapherFake ).StackSamplesToFlameGraphInvoked )
732+ },
733+ after : func () {
734+ // Cleanup, though output-raw.txt might not be created if copy failed early.
735+ _ = file .Remove (filepath .Join (common .TmpDir (), config .ProfilingPrefix + "output-raw.txt" ))
736+ },
737+ },
583738 }
584739 for _ , tt := range tests {
585740 t .Run (tt .name , func (t * testing.T ) {
0 commit comments