Add ctest based tests to run bpf_conformance tests. (#268)

* Add ctest based tests

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* Fix cmake

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* Fix typo in yml

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* Fix yml

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* Fix yml

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* Restrict coverage to project code

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

---------

Signed-off-by: Alan Jowett <alanjo@microsoft.com>
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 10774e1..6a12371 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -1,3 +1,5 @@
+# Copyright (c) 2022-present, IO Visor Project
+# SPDX-License-Identifier: Apache-2.0
 #
 # Copyright (c) 2022-present, IO Visor Project
 # All rights reserved.
@@ -104,7 +106,7 @@
       arch: arm64
       platform: ubuntu-20.04
       build_type: RelWithDebInfo
-  
+
   linux_release_scan_build:
     uses: ./.github/workflows/posix.yml
     with:
@@ -121,7 +123,7 @@
       build_type: RelWithDebInfo
       enable_coverage: true
 
-  linux_arm64_release_coverage:
+  linux_release_arm64_coverage:
     uses: ./.github/workflows/posix.yml
     with:
       arch: arm64
@@ -200,10 +202,13 @@
       - macos_debug_coverage
       - linux_release_coverage
       - linux_debug_coverage
+      - linux_release_arm64_coverage
+      - linux_debug_arm64_coverage
+
     runs-on: ubuntu-20.04
     steps:
     - name: Coveralls Finished
       uses: coverallsapp/github-action@v2.1.2
       with:
         github-token: ${{ secrets.github_token }}
-        parallel-finished: true
\ No newline at end of file
+        parallel-finished: true
diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml
index 5034eba..aeb38eb 100644
--- a/.github/workflows/posix.yml
+++ b/.github/workflows/posix.yml
@@ -118,7 +118,7 @@
           lcov \
           boost
 
-    # Build the bpf_conformance suite seperately as it doesn't build in arm64.
+    # Build the bpf_conformance suite separately as it doesn't build in arm64.
     - name: Configure bpf_conformance
       run: |
         export CCACHE_DIR="$(pwd)/ccache"
@@ -161,6 +161,7 @@
           -DUBPF_ENABLE_TESTS=true \
           -DUBPF_ENABLE_INSTALL=true \
           -DUBPF_SKIP_EXTERNAL=true \
+          -DBPF_CONFORMANCE_RUNNER="$(pwd)/build_bpf_conformance/bin/bpf_conformance_runner" \
           ${arch_flags}
 
     - name: Build uBPF
@@ -183,44 +184,7 @@
         path: ${{github.workspace}}/scan_build_report
         retention-days: 5
 
-    - name: Run the local bpf_conformance tests
-      run: |
-        export BPF_CONFORMANCE_RUNNER="build_bpf_conformance/bin/bpf_conformance_runner"
-        export BPF_CONFORMANCE_TEST_DIR="--test_file_directory tests"
-
-        # For arm64, we need to run the tests in qemu, so use the scripts
-        if [[ "${{ inputs.arch }}" == "arm64" ]] ; then
-          export BPF_CONFORMANCE_PLUGIN_JIT="--plugin_path aarch64_test/run-jit.sh"
-          export BPF_CONFORMANCE_PLUGIN_INTERPRET="--plugin_path aarch64_test/run-interpret.sh"
-        else
-          export BPF_CONFORMANCE_PLUGIN_JIT="--plugin_path build/bin/ubpf_plugin --plugin_options --jit"
-          export BPF_CONFORMANCE_PLUGIN_INTERPRET="--plugin_path build/bin/ubpf_plugin --plugin_options --interpret"
-        fi
-
-        ${BPF_CONFORMANCE_RUNNER} ${BPF_CONFORMANCE_TEST_DIR} ${BPF_CONFORMANCE_PLUGIN_JIT}
-        ${BPF_CONFORMANCE_RUNNER} ${BPF_CONFORMANCE_TEST_DIR} ${BPF_CONFORMANCE_PLUGIN_INTERPRET}
-
-    - name: Run the upstream bpf_conformance tests
-      run: |
-        export BPF_CONFORMANCE_RUNNER="build_bpf_conformance/bin/bpf_conformance_runner"
-        export BPF_CONFORMANCE_TEST_DIR="--test_file_directory external/bpf_conformance/tests"
-        # Exclude tests that check atomic operations, as they are not supported by uBPF yet.
-        export BPF_CONFORMANCE_TEST_FILTER="--exclude_regex (lock|call_local)"
-
-        # For arm64, we need to run the tests in qemu, so use the scripts
-        if [[ "${{ inputs.arch }}" == "arm64" ]] ; then
-          export BPF_CONFORMANCE_PLUGIN_JIT="--plugin_path aarch64_test/run-jit.sh"
-          export BPF_CONFORMANCE_PLUGIN_INTERPRET="--plugin_path aarch64_test/run-interpret.sh"
-        else
-          export BPF_CONFORMANCE_PLUGIN_JIT="--plugin_path build/bin/ubpf_plugin --plugin_options --jit"
-          export BPF_CONFORMANCE_PLUGIN_INTERPRET="--plugin_path build/bin/ubpf_plugin --plugin_options --interpret"
-        fi
-
-        ${BPF_CONFORMANCE_RUNNER} ${BPF_CONFORMANCE_TEST_DIR} ${BPF_CONFORMANCE_TEST_FILTER} ${BPF_CONFORMANCE_PLUGIN_JIT}
-        ${BPF_CONFORMANCE_RUNNER} ${BPF_CONFORMANCE_TEST_DIR} ${BPF_CONFORMANCE_TEST_FILTER} ${BPF_CONFORMANCE_PLUGIN_INTERPRET}
-
     - name: Run the CTest suite
-      if: inputs.arch != 'arm64'
       run: |
         export CCACHE_DIR="$(pwd)/ccache"
 
@@ -236,7 +200,7 @@
       if: inputs.enable_coverage == true
       run: |
         mkdir -p coverage
-        lcov --capture --directory build --include '*' --output-file coverage/lcov.info
+        lcov --capture --directory build --include '${{env.GITHUB_WORKSPACE}}/*' --output-file coverage/lcov.info
 
     - name: Coveralls Parallel
       if: inputs.enable_coverage == true
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9fb0b3d..98a3bc3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,7 @@
     add_subdirectory("external")
   endif()
   add_subdirectory("bpf")
+  add_subdirectory("aarch64_test")
 endif()
 
 if(UBPF_ENABLE_PACKAGE)
diff --git a/aarch64_test/CMakeLists.txt b/aarch64_test/CMakeLists.txt
new file mode 100644
index 0000000..65f38b9
--- /dev/null
+++ b/aarch64_test/CMakeLists.txt
@@ -0,0 +1,5 @@
+# Copyright (c) Microsoft Corporation
+# SPDX-License-Identifier: Apache-2.0
+
+file(COPY run-interpret.sh DESTINATION ${CMAKE_BINARY_DIR}/bin)
+file(COPY run-jit.sh DESTINATION ${CMAKE_BINARY_DIR}/bin)
diff --git a/aarch64_test/run-interpret.sh b/aarch64_test/run-interpret.sh
index b608cdd..0f8a54c 100755
--- a/aarch64_test/run-interpret.sh
+++ b/aarch64_test/run-interpret.sh
@@ -3,4 +3,4 @@
 # SPDX-License-Identifier: Apache-2.0
 
 # Work around for argument passing.
-qemu-aarch64 -L /usr/aarch64-linux-gnu build/bin/ubpf_plugin "$*" --interpret
+qemu-aarch64 -L /usr/aarch64-linux-gnu ../bin/ubpf_plugin "$*" --interpret
diff --git a/aarch64_test/run-jit.sh b/aarch64_test/run-jit.sh
index 8988153..9bbdf6d 100755
--- a/aarch64_test/run-jit.sh
+++ b/aarch64_test/run-jit.sh
@@ -3,4 +3,4 @@
 # SPDX-License-Identifier: Apache-2.0
 
 # Work around for argument passing.
-qemu-aarch64 -L /usr/aarch64-linux-gnu build/bin/ubpf_plugin "$*" --jit
+qemu-aarch64 -L /usr/aarch64-linux-gnu ../bin/ubpf_plugin "$*" --jit
diff --git a/bpf/CMakeLists.txt b/bpf/CMakeLists.txt
index 36d527d..f8fde06 100644
--- a/bpf/CMakeLists.txt
+++ b/bpf/CMakeLists.txt
@@ -10,6 +10,11 @@
   RESULT_VARIABLE CLANG_RETURN_CODE
   OUTPUT_STRIP_TRAILING_WHITESPACE
 )
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
+    set(PREFIX qemu-aarch64 -L /usr/aarch64-linux-gnu)
+else()
+    set(PREFIX)
+endif()
 
 if (CLANG_RETURN_CODE EQUAL 0)
     message(STATUS "Clang supports BPF target")
@@ -40,9 +45,9 @@
 
     add_custom_target(${file_name}_ELF ALL DEPENDS ${bpf_obj_file_path} SOURCES ${bpf_file_path})
 
-    add_test(NAME ${file_name}_TEST_INTERPRET COMMAND "${CMAKE_BINARY_DIR}/bin/ubpf_test" "${bpf_obj_file_path}")
+    add_test(NAME ${file_name}_TEST_INTERPRET COMMAND ${PREFIX} "${CMAKE_BINARY_DIR}/bin/ubpf_test" "${bpf_obj_file_path}")
     set_tests_properties(${file_name}_TEST_INTERPRET PROPERTIES PASS_REGULAR_EXPRESSION "0x0")
-    add_test(NAME ${file_name}_TEST_JIT COMMAND "${CMAKE_BINARY_DIR}/bin/ubpf_test" "${bpf_obj_file_path}")
+    add_test(NAME ${file_name}_TEST_JIT COMMAND ${PREFIX} "${CMAKE_BINARY_DIR}/bin/ubpf_test" "${bpf_obj_file_path}")
     set_tests_properties(${file_name}_TEST_JIT PROPERTIES PASS_REGULAR_EXPRESSION "0x0")
 endfunction()
 
diff --git a/cmake/options.cmake b/cmake/options.cmake
index fa18612..36f14e8 100644
--- a/cmake/options.cmake
+++ b/cmake/options.cmake
@@ -16,6 +16,7 @@
 option(UBPF_ENABLE_PACKAGE "Set to true to enable packaging")
 option(UBPF_SKIP_EXTERNAL "Set to true to skip external projects")
 option(UBPF_INSTALL_GIT_HOOKS "Set to true to install git hooks" ON)
+option(BPF_CONFORMANCE_RUNNER "Set to use a custom bpf_conformance runner")
 
 # Note that the compile_commands.json file is only exporter when
 # using the Ninja or Makefile generator
diff --git a/ubpf_plugin/CMakeLists.txt b/ubpf_plugin/CMakeLists.txt
index adc7e18..f7a854d 100644
--- a/ubpf_plugin/CMakeLists.txt
+++ b/ubpf_plugin/CMakeLists.txt
@@ -6,19 +6,61 @@
 file(COPY ${CMAKE_SOURCE_DIR}/tests DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
 
 add_executable(
-  ubpf_plugin
-  ubpf_plugin.cc
+    ubpf_plugin
+    ubpf_plugin.cc
 )
 
 target_include_directories("ubpf_plugin" PRIVATE
-  "${CMAKE_SOURCE_DIR}/vm"
-  "${CMAKE_BINARY_DIR}/vm"
-  "${CMAKE_SOURCE_DIR}/vm/inc"
-  "${CMAKE_BINARY_DIR}/vm/inc"
+    "${CMAKE_SOURCE_DIR}/vm"
+    "${CMAKE_BINARY_DIR}/vm"
+    "${CMAKE_SOURCE_DIR}/vm/inc"
+    "${CMAKE_BINARY_DIR}/vm/inc"
 )
 
 target_link_libraries(
-  ubpf_plugin
-  ubpf
-  ubpf_settings
+    ubpf_plugin
+    ubpf
+    ubpf_settings
 )
+
+file(GLOB external_files ${CMAKE_SOURCE_DIR}/external/bpf_conformance/tests/*.data)
+file(GLOB local_files ${CMAKE_SOURCE_DIR}/tests/*.data)
+
+set(files ${external_files} ${local_files})
+
+if(NOT BPF_CONFORMANCE_RUNNER)
+    set(BPF_CONFORMANCE_RUNNER ${CMAKE_BINARY_DIR}/external/bpf_conformance/bin/bpf_conformance_runner)
+else()
+    message(STATUS "Using custom bpf_conformance_runner: ${BPF_CONFORMANCE_RUNNER}")
+endif()
+
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
+    set(PLUGIN_JIT --plugin_path ${CMAKE_BINARY_DIR}/bin/run-jit.sh)
+    set(PLUGIN_INTERPRET --plugin_path ${CMAKE_BINARY_DIR}/bin/run-interpret.sh)
+else()
+    set(PLUGIN_JIT --plugin_path ${CMAKE_BINARY_DIR}/bin/ubpf_plugin --plugin_options --jit)
+    set(PLUGIN_INTERPRET --plugin_path ${CMAKE_BINARY_DIR}/bin/ubpf_plugin --plugin_options --interpret)
+endif()
+
+foreach(file ${files})
+    # TODO: remove this once we have a proper implementation of interlocked operations
+    # and support for calling local functions.
+    string(REGEX MATCH "(lock|call_local)" EXPECT_FAILURE "${file}")
+    add_test(
+        NAME ${file}-JIT
+        COMMAND ${BPF_CONFORMANCE_RUNNER} --test_file_path ${file} ${PLUGIN_JIT}
+    )
+
+    if(EXPECT_FAILURE)
+        set_tests_properties(${file}-JIT PROPERTIES WILL_FAIL TRUE)
+    endif()
+
+    add_test(
+        NAME ${file}-Interpreter
+        COMMAND ${BPF_CONFORMANCE_RUNNER} --test_file_path ${file} ${PLUGIN_INTERPRET}
+    )
+
+    if(EXPECT_FAILURE)
+        set_tests_properties(${file}-Interpreter PROPERTIES WILL_FAIL TRUE)
+    endif()
+endforeach()