Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added sample unit and integration tests for template #6

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Steps to use this template:
- Click on *Create repository*
- Update the placeholders (`git grep -i CHANGEME`, `git grep -i TODO`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Update the placeholders (`git grep -i CHANGEME`, `git grep -i TODO`)
- Update the placeholders (`git grep -i CHANGEME`, `git grep -i TODO`, `git grep -i mytracer`)

Either update the grep commands, or rename to MyTracer to ChangeMeTracer in the go files.

- Write your eBPF program (follow [Hello world gadget](https://www.inspektor-gadget.io/docs/latest/gadget-devel/hello-world-gadget))
- Write [Unit](https://www.inspektor-gadget.io/blog/2024/12/inspektor-gadget-unittesting-framework) and [Integration](https://www.inspektor-gadget.io/blog/2024/06/introducing-the-new-testing-framework-for-image-based-gadgets) Tests for Gadget.
- Delete this section from README.md

---
Expand Down
89 changes: 89 additions & 0 deletions test/integration/changeme_mytracer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// This is a sample integration test file to serve as a guide for implementation.
// Refer to this blog: https://www.inspektor-gadget.io/blog/2024/06/introducing-the-new-testing-framework-for-image-based-gadgets.

package tests

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"

gadgettesting "github.com/inspektor-gadget/inspektor-gadget/gadgets/testing"
ebpftypes "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/ebpf/types"
igtesting "github.com/inspektor-gadget/inspektor-gadget/pkg/testing"
"github.com/inspektor-gadget/inspektor-gadget/pkg/testing/containers"
igrunner "github.com/inspektor-gadget/inspektor-gadget/pkg/testing/ig"
"github.com/inspektor-gadget/inspektor-gadget/pkg/testing/match"
"github.com/inspektor-gadget/inspektor-gadget/pkg/testing/utils"
)

// ExpectedMyTracerEvent represents the structure of the expected output
// from the tracer. It helps in validating the actual output.
type ExpectedMyTracerEvent struct {
Proc ebpftypes.Process `json:"proc"`
Fd uint32 `json:"fd"`
FName string `json:"fname"`
// Add additional expected fields as necessary
}

func TestMyTracerGadget(t *testing.T) {
// Ensure required environment variables are set; skip test otherwise.
gadgettesting.RequireEnvironmentVariables(t)
// Initialize utilities for the test.
utils.InitTest(t)

// Create a container factory to manage container lifecycle during the test.
containerFactory, err := containers.NewContainerFactory(utils.Runtime)
require.NoError(t, err, "Failed to create a new container factory")

// Define the test container's name and image.
containerName := "test-mytracer"
containerImage := "base-container-image" // Replace with the actual container image to be used.

// Configure the test container with the desired command and options.
testContainer := containerFactory.NewContainer(
containerName,
"desired command for execution", // Replace with the actual command to execute in the container.
containers.WithContainerImage(containerImage),
)

// Start the test container and defer its cleanup.
testContainer.Start(t)
t.Cleanup(func() {
testContainer.Stop(t)
})

// Configure options for running the tracer gadget.
runnerOpts := []igrunner.Option{
// Specify runtime and set a timeout for the tracer execution.
igrunner.WithFlags(fmt.Sprintf("-r=%s", utils.Runtime), "--timeout=5"),

// Provide a validation function to check the tracer's output against expectations.
igrunner.WithValidateOutput(func(t *testing.T, output string) {
// Define the expected event structure.
expectedEntry := &ExpectedMyTracerEvent{
Proc: utils.BuildProc("cat", 1000, 1111),
FName: "/dev/null",
Fd: 3,
// Add additional expected fields as necessary.
}

// Define a normalization function if any field requires preprocessing before comparison.
normalize := func(e *ExpectedMyTracerEvent) {
// Example: Normalize fields such as timestamps or dynamic values.
}

// Match actual output with the expected entry.
match.MatchEntries(t, match.JSONMultiObjectMode, output, normalize, expectedEntry)
}),
}

// Define the test steps to run the tracer gadget.
testSteps := []igtesting.TestStep{
igrunner.New("mytracer", runnerOpts...), // Replace "mytracer" with the actual gadget name.
}

// Execute the test steps.
igtesting.RunTestSteps(testSteps, t, nil)
}
90 changes: 90 additions & 0 deletions test/unit/changeme_mytracer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// This is a sample unit test file to serve as a guide for implementation.
// Refer to this blog: https://www.inspektor-gadget.io/blog/2024/12/inspektor-gadget-unittesting-framework

package tests

import (
"testing"

"github.com/cilium/ebpf"

gadgettesting "github.com/inspektor-gadget/inspektor-gadget/gadgets/testing"
utilstest "github.com/inspektor-gadget/inspektor-gadget/internal/test"
ebpftypes "github.com/inspektor-gadget/inspektor-gadget/pkg/operators/ebpf/types"
"github.com/inspektor-gadget/inspektor-gadget/pkg/testing/gadgetrunner"
)

// ExpectedMyTracerEvent represents the structure of the expected output
// from the tracer. It helps in validating the actual output.
type ExpectedMyTracerEvent struct {
Proc ebpftypes.Process `json:"proc"`
Fd uint32 `json:"fd"`
FName string `json:"fname"`
// Add additional expected fields as necessary
}

type testDef struct {
// Configuration for initializing the runner
runnerConfig *utilstest.RunnerConfig

// Function to create a mount namespace filter map
mntnsFilterMap func(info *utilstest.RunnerInfo) *ebpf.Map

// Function to generate events in the gadget
generateEvent func() (int, error)

// Validation logic for the captured events
validateEvent func(t *testing.T, info *utilstest.RunnerInfo, fd int, events []ExpectedMyTracerEvent)
}

func TestMyTracerGadget(t *testing.T) {
// Initialize the unit test
gadgettesting.InitUnitTest(t)

// Define test cases
testCases := map[string]testDef{
"ExampleTest": {
runnerConfig: &utilstest.RunnerConfig{
// Add necessary runner configurations here
},
mntnsFilterMap: func(info *utilstest.RunnerInfo) *ebpf.Map {
// Example of creating an eBPF map (replace with actual implementation)
return nil
},
generateEvent: func() (int, error) {
// Simulate the generation of events (replace with actual logic)
return 0, nil
},
validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, fd int, events []ExpectedMyTracerEvent) {
// Add logic to validate captured events against expected events
if len(events) == 0 {
t.Errorf("No events captured")
}
},
},
// Add more test cases as needed
}

// Iterate over test cases
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()

// Initialize the runner
runner := utilstest.NewRunnerWithTest(t, testCase.runnerConfig)
defer runner.Close()

// Configure gadget runner options
opts := gadgetrunner.GadgetRunnerOpts[ExpectedMyTracerEvent]{
// Set up options such as gadget name, arguments, etc.
}

// Initialize and run the gadget
gadgetRunner := gadgetrunner.NewGadgetRunner(t, opts)
gadgetRunner.RunGadget()

// Validate captured events
testCase.validateEvent(t, runner.Info, 0, gadgetRunner.CapturedEvents)
})
}
}