From 7a60619a5c1fb41f0514236c3ff8d97dff149e71 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Tue, 14 Jan 2025 12:47:25 +0000 Subject: [PATCH 1/2] [Frontend-go] Add unit test for golang Signed-off-by: Arthur Chan --- .../frontends/frontend_go.py | 41 ++++++- .../source-code/go/test-project-1/fuzzer.go | 48 ++++++++ src/test/test_frontends_go.py | 112 ++++++++++++++++++ 3 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 src/test/data/source-code/go/test-project-1/fuzzer.go create mode 100644 src/test/test_frontends_go.py diff --git a/src/fuzz_introspector/frontends/frontend_go.py b/src/fuzz_introspector/frontends/frontend_go.py index 09e35cf17..5190a6547 100644 --- a/src/fuzz_introspector/frontends/frontend_go.py +++ b/src/fuzz_introspector/frontends/frontend_go.py @@ -290,13 +290,11 @@ def extract_calltree(self, if not func: return line_to_print - callsites = func.base_callsites() - if function in visited_functions: return line_to_print visited_functions.add(function) - for cs, line_number in callsites: + for cs, line_number in func.base_callsites(): line_to_print += self.extract_calltree( source_code.source_file, function=cs, @@ -305,6 +303,43 @@ def extract_calltree(self, line_number=line_number) return line_to_print + def get_reachable_functions( + self, + source_file: str, + source_code: Optional[SourceCodeFile] = None, + function: Optional[str] = None, + visited_functions: Optional[set[str]] = None) -> set[str]: + """Get a list of reachable functions for a provided function name.""" + if not visited_functions: + visited_functions = set() + + if not function and source_code: + function = source_code.get_entry_function_name() + + if not function: + return visited_functions + + if not source_code and function: + source_code = self.find_source_with_func_def(function) + + if not source_code: + visited_functions.add(function) + return visited_functions + print(function) + func = source_code.get_function_node(function) + if not func or function in visited_functions: + visited_functions.add(function) + return visited_functions + + visited_functions.add(function) + for cs, line_number in func.base_callsites(): + visited_functions = self.get_reachable_functions( + source_code.source_file, + function=cs, + visited_functions=visited_functions) + + return visited_functions + def find_source_with_func_def( self, target_function_name: str) -> Optional[SourceCodeFile]: """Finds the source code with a given function.""" diff --git a/src/test/data/source-code/go/test-project-1/fuzzer.go b/src/test/data/source-code/go/test-project-1/fuzzer.go new file mode 100644 index 000000000..1f094a6ac --- /dev/null +++ b/src/test/data/source-code/go/test-project-1/fuzzer.go @@ -0,0 +1,48 @@ +// Copyright 2025 Google LLC +// +// Licensed 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 basics + +import ( + "testing" + "fmt" +) + +func FuzzBasics(f *testing.F) { + f.Fuzz(func(t *testing.T, numString, str, isTrueString string) { + num, err := fmt.Sscanf(numString, "%d") + if err != nil { + return + } + + isTrue := false + if isTrueString == "true" { + isTrue = true + } + + _ = calculate(num, isTrue) + }) +} + +func calculate(a int, b bool) string { + if b { + return fmt.Sprintf("Number is: %d", a) + } + return "Boolean is false" +} + +func unusedFunction() { + fmt.Println("This function is never used") +} diff --git a/src/test/test_frontends_go.py b/src/test/test_frontends_go.py new file mode 100644 index 000000000..c40f07049 --- /dev/null +++ b/src/test/test_frontends_go.py @@ -0,0 +1,112 @@ +# Copyright 2025 Fuzz Introspector Authors +# +# Licensed 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. +"""Unit testing script for the Go frontend""" + +from fuzz_introspector.frontends import oss_fuzz # noqa: E402 + + +def test_tree_sitter_go_sample1(): + project = oss_fuzz.analyse_folder( + 'go', + 'src/test/data/source-code/go/test-project-1', + dump_output=False, + ) + + # Project check + harness = project.get_source_codes_with_harnesses() + assert len(harness) == 1 + + functions_reached = project.get_reachable_functions(harness[0].source_file, harness[0]) + + # Callsite check + assert 'calculate' in functions_reached + assert 'fmt.Sprintf' in functions_reached + assert 'unusedFunction' not in functions_reached + + +def test_tree_sitter_go_sample2(): + project = oss_fuzz.analyse_folder( + 'go', + 'src/test/data/source-code/go/test-project-2', + dump_output=False, + ) + + # Project check + harness = project.get_source_codes_with_harnesses() + + +def test_tree_sitter_go_sample3(): + project = oss_fuzz.analyse_folder( + 'go', + 'src/test/data/source-code/go/test-project-3', + dump_output=False, + ) + + # Project check + harness = project.get_source_codes_with_harnesses() + + +def test_tree_sitter_go_sample4(): + project = oss_fuzz.analyse_folder( + 'go', + 'src/test/data/source-code/go/test-project-4', + dump_output=False, + ) + + # Project check + harness = project.get_source_codes_with_harnesses() + + +def test_tree_sitter_go_sample5(): + project = oss_fuzz.analyse_folder( + 'go', + 'src/test/data/source-code/go/test-project-5', + dump_output=False, + ) + + # Project check + harness = project.get_source_codes_with_harnesses() + + +def test_tree_sitter_go_sample6(): + project = oss_fuzz.analyse_folder( + 'go', + 'src/test/data/source-code/go/test-project-6', + dump_output=False, + ) + + # Project check + harness = project.get_source_codes_with_harnesses() + + +def test_tree_sitter_go_sample7(): + project = oss_fuzz.analyse_folder( + 'go', + 'src/test/data/source-code/go/test-project-8', + dump_output=False, + ) + + # Project check + harness = project.get_source_codes_with_harnesses() + + +def test_tree_sitter_go_sample8(): + project = oss_fuzz.analyse_folder( + 'go', + 'src/test/data/source-code/go/test-project-8', + dump_output=False, + ) + + # Project check + harness = project.get_source_codes_with_harnesses() From f57870d0b39c89b681ebd28b79e01f4786dc6530 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Tue, 14 Jan 2025 12:51:38 +0000 Subject: [PATCH 2/2] Fix formatting Signed-off-by: Arthur Chan --- src/test/test_frontends_go.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/test_frontends_go.py b/src/test/test_frontends_go.py index c40f07049..136ab175c 100644 --- a/src/test/test_frontends_go.py +++ b/src/test/test_frontends_go.py @@ -44,6 +44,7 @@ def test_tree_sitter_go_sample2(): # Project check harness = project.get_source_codes_with_harnesses() + assert len(harness) == 0 def test_tree_sitter_go_sample3(): @@ -55,6 +56,7 @@ def test_tree_sitter_go_sample3(): # Project check harness = project.get_source_codes_with_harnesses() + assert len(harness) == 0 def test_tree_sitter_go_sample4(): @@ -66,6 +68,7 @@ def test_tree_sitter_go_sample4(): # Project check harness = project.get_source_codes_with_harnesses() + assert len(harness) == 0 def test_tree_sitter_go_sample5(): @@ -77,6 +80,7 @@ def test_tree_sitter_go_sample5(): # Project check harness = project.get_source_codes_with_harnesses() + assert len(harness) == 0 def test_tree_sitter_go_sample6(): @@ -88,6 +92,7 @@ def test_tree_sitter_go_sample6(): # Project check harness = project.get_source_codes_with_harnesses() + assert len(harness) == 0 def test_tree_sitter_go_sample7(): @@ -99,6 +104,7 @@ def test_tree_sitter_go_sample7(): # Project check harness = project.get_source_codes_with_harnesses() + assert len(harness) == 0 def test_tree_sitter_go_sample8(): @@ -110,3 +116,4 @@ def test_tree_sitter_go_sample8(): # Project check harness = project.get_source_codes_with_harnesses() + assert len(harness) == 0