Skip to content

create unit test for swc

Mustafa Bahaa edited this page May 11, 2024 · 1 revision

Table of Contents

Prerequisites

Introduction

This wiki page outlines how to create a unit test for dio_swc.

Folder structure

This is a proposal for the unit test folder structure. Try to follow this structure to unify the general concept across the entire project.

software/bsw/mcal/dio_swc
│ 
├── CMakeLists.txt
├── core
│   ├── Dio.c
│   ├── Dio.h
│   └── Dio_MemMap.h
├── tests
│   └── unit
│       ├── stubs
│       ├── test_dio_swc.c
│       └── test_runners
│           ├── test_main.c
│           └── test_runner.c
└── tools
    └── cmake
        ├── dio_swc_compile.cmake
        └── dio_swc_tests.cmake

Setup test runner folder

You will find template for different files in files test_main.c

#include "unity_fixture.h"

static void RunAllTests(void)
{
  RUN_TEST_GROUP(dio_swc);
}

int main(int argc, const char * argv[])
{
  return UnityMain(argc, argv, RunAllTests);
}

test_runner.c

#include "unity.h"
#include "unity_fixture.h"

TEST_GROUP_RUNNER(dio_swc)
{
  RUN_TEST_CASE(dio_swc, TC1);
  RUN_TEST_CASE(dio_swc, TC2);
}

test_dio_swc.c

#include "unity.h"
#include "unity_fixture.h"


TEST_GROUP(dio_swc);

TEST_SETUP(dio_swc)
{
}

TEST_TEAR_DOWN(dio_swc)
{
}

TEST(dio_swc, TC1)
{

}
TEST(dio_swc, TC2)
{

}

Analyze the test Function

We need to analyze the function to perform unit tests on it and determine its dependencies.

void Dio_GetVersionInfo(Std_VersionInfoType *VersionInfo)
{
  // Check if DET is enabled and if VersionInfo pointer is NULL
#ifdef DIO_DEV_ERROR_DETECT
  if (VersionInfo == NULL)
  {
    // Report error and return without any action
    Det_ReportError(DIO_MODULE_ID, DIO_INSTANCE_ID, DIO_GET_VERSION_INFO_SID, DIO_E_PARAM_POINTER);
    return;
  }
#endif

  // Fill the VersionInfo structure with module version information
  VersionInfo->vendorID = 0;
  VersionInfo->moduleID = DIO_MODULE_ID;
  VersionInfo->instanceID = DIO_INSTANCE_ID;
  VersionInfo->sw_major_version = DIO_CFG_SW_MAJOR_VERSION;
  VersionInfo->sw_minor_version = DIO_CFG_SW_MINOR_VERSION;
  VersionInfo->sw_patch_version = DIO_CFG_SW_PATCH_VERSION;
  VersionInfo->ar_major_version = DIO_CFG_AR_RELEASE_MAJOR_VERSION;
  VersionInfo->ar_minor_version = DIO_CFG_AR_RELEASE_MINOR_VERSION;
  VersionInfo->ar_patch_version = DIO_CFG_AR_RELEASE_PATCH_VERSION;
}

In this function, the Det_ReportError only needs to be mocked.

write CMake files

  • For software/bsw/mcal/dio_swc/tools/cmake/dio_swc_tests.cmake
set(
    dio_swc_tests_sources_list

    ${dio_swc}/tests/unit/test_dio_swc.c  # Main test file that contain the test cases 

    # Any mocked file will be under ${MOCK_FILE_PATH}/mocks/mock_headerFileName.c
    ${MOCK_FILE_PATH}/mocks/mock_Det.c   # Include the mock_Det 

    # Include Test Runners 
    ${dio_swc}/tests/unit/test_runners/test_runner.c
    ${dio_swc}/tests/unit/test_runners/test_main.c
)

set(
    dio_swc_ut_sources_list
    
    ${dio_swc}/core/Dio.c # Main test file 
    ${gendata}/Dio_Lcfg.c 

)

set (
    dio_swc_mock_header_files
    
    ${stubs}/Det.h  # Det.h will be mocked

)

set(
    dio_swc_tests_includes 

    ${dio_swc}/core
    ${port_swc}/core
    ${gendata}
    ${common_includes}
    ${platform}
    ${stubs}
    ${dio_swc}/tests/unit/stubs # include stub folder 
    ${MOCK_FILE_PATH}/mocks/ # MOCK_FILE_PATH will be assigend at `software/bsw/mcal/dio_swc/CMakeLists.txt`
)

set(
    dio_swc_tests_compile_options

)

set(
    dio_swc_tests_defines
)

For complete details on the usage of this variable, take a look at section Integrate CMock/Unity with project files in * Unit Test Frameworks Integration.

  • for software/bsw/mcal/dio_swc/CMakeLists.txt
# CMake Version Check
cmake_minimum_required(VERSION 3.14)

# Project
project(dio_swc 
        LANGUAGES C CXX
        VERSION "1.0.0"
        DESCRIPTION "dio_swc")

##############################################################################
# Extract Module Path in build/CMake/Test/                                   #
##############################################################################
extract_module_path(dio_swc MOCK_FILE_PATH) #Assign the path of mock files @ MOCK_FILE_PATH variable 

##############################################################################
# Include Common Compilation Settings                                       #
##############################################################################
include(tools/cmake/dio_swc_compile.cmake)  #Include Variables 

##############################################################################
# Build Configuration                                                        #
##############################################################################

# Compilation Mode: Only build the library

if(MODE STREQUAL "COMPILING")
  add_library(dio_swc ${dio_swc_sources_list})
  target_compile_options(dio_swc PRIVATE ${dio_swc_compile_options})
  target_include_directories(dio_swc PRIVATE ${dio_swc_includes})
  target_compile_definitions(dio_swc PRIVATE ${dio_swc_defines})
elseif (MODE STREQUAL "TESTING")
  include(tools/cmake/dio_swc_tests.cmake)
  add_library(dio_swc ${dio_swc_ut_sources_list})
  target_compile_options(dio_swc PRIVATE ${dio_swc_tests_compile_options})
  target_include_directories(dio_swc PRIVATE ${dio_swc_tests_includes})
  target_compile_definitions(dio_swc PRIVATE ${dio_swc_tests_defines})
endif()

if(MODE STREQUAL "TESTING")  
  # Execute CMock script to generate mock_Det.h
  execute_process(
    COMMAND ruby ${CMOCK_SCRIPT} -o${CMOCK_CONFIG_FILE} ${dio_swc_mock_header_files}
    OUTPUT_VARIABLE CMOCK_OUTPUT
    WORKING_DIRECTORY ${MOCK_FILE_PATH}  #  /workspace/tools/build/CMake/Test/software/bsw/mcal/dio_swc
  )
  message("CMock output: ${CMOCK_OUTPUT}")

  # Testing Mode: Build the library and test executable
  add_executable(dio_swc_test ${dio_swc_tests_sources_list} ${unity_common_sources})
  target_compile_definitions(dio_swc_test PRIVATE ${dio_swc_tests_defines})
  target_compile_options(dio_swc_test PRIVATE ${dio_swc_tests_compile_options})
  target_include_directories(dio_swc_test PRIVATE ${dio_swc_tests_includes} ${unity_common_includes})
endif()

Excute unit_test rule and solve errors

make unit_test rebuild=1

output

We encountered a linking error, as shown in the screenshot belowError The compiler was unable to locate the object files for dio.c and port.c. We will address this issue by adding dio.c as an external library and mocking the port library.

  • For software/bsw/mcal/dio_swc/tools/cmake/dio_swc_tests.cmake
set (
    dio_swc_mock_header_files
    
    ${stubs}/Det.h
    ${port_swc}/core/Port.h

)

set(
    dio_swc_tests_libs
    "dio_swc"
)
make unit_test rebuild=1

output

linking error, as shown in the screenshot belowError

and here a wise man once said "طيب ما تجرب يا أخي انت خسران حاجه" or

comic

I search for this variable using the search feature in VSCode search

I found that the variable is externally declared in port.h and dio.h, with its definition in port.c.

we can define it in our test file or i will add them in file called stub_structure.h

  • software/bsw/mcal/dio_swc/tests/unit/stubs/stub_structure.h
#include "stdint.h"
#include "Port.h"
typedef struct
{ /*port Structure*/
 int32_t RESERVED0[255];
 uint32_t DATA;  /*port Data*/
 uint32_t DIR;   /*port Direction*/
 uint32_t IS;    /*port Interrupt Sense*/
 uint32_t IBE;   /*port Interrupt Both Edges*/
 uint32_t IEV;   /*port Interrupt Event*/
 uint32_t IM;    /*port Interrupt Mask*/
 uint32_t RIS;   /*port Raw Interrupt Status*/
 uint32_t MIS;   /*port Masked Interrupt Status*/
 int32_t ICR;    /*port Interrupt Clear*/
 uint32_t AFSEL; /*port Alternate Function Select*/
 int32_t RESERVED1[55];
 uint32_t DR2R;   /*port 2-mA Drive Select*/
 uint32_t DR4R;   /*port 4-mA Drive Select*/
 uint32_t DR8R;   /*port 8-mA Drive Select*/
 uint32_t ODR;    /*port Open Drain Select*/
 uint32_t PUR;    /*port Pull-Up Select*/
 uint32_t PDR;    /*port Pull-Down Select*/
 uint32_t SLR;    /*port Slew Rate Control Select*/
 uint32_t DEN;    /*port Digital Enable*/
 uint32_t LOCK;   /*port Lock*/
 int32_t CR;      /*port Commit*/
 uint32_t AMSEL;  /*port Analog Mode Select*/
 uint32_t PCTL;   /*port Port Control*/
 uint32_t ADCCTL; /*port ADC Control*/
 uint32_t DMACTL; /*port DMA Control*/
} Port_Type;

uint32_t PORTA_VAR = 0 ;
uint32_t PORTB_VAR = 0 ;
uint32_t PORTC_VAR = 0 ;
uint32_t PORTD_VAR = 0 ;
uint32_t PORTE_VAR = 0 ;
uint32_t PORTF_VAR = 0 ;

#define PORTA &PORTA_VAR  // Change HW address by variable address to avoid segmentation fault
#define PORTB &PORTB_VAR
#define PORTC &PORTC_VAR
#define PORTD &PORTD_VAR
#define PORTE &PORTE_VAR
#define PORTF &PORTF_VAR


/* Define the base addresses of the ports */
Port_Type *portBaseAddresses[] = {
    PORTA,
    PORTB,
    PORTC,
    PORTD,
    PORTE,
    PORTF};

Finalllly it works <3

Write Unit tests

TEST(dio_swc, test_Dio_GetVersionInfo_ValidVersionInfo)
{
    // Define a Std_VersionInfoType structure to hold version information
    Std_VersionInfoType versionInfo;

    // Call the function with a valid pointer to versionInfo
    Dio_GetVersionInfo(&versionInfo);

    // Check if the versionInfo structure is correctly filled
    TEST_ASSERT_EQUAL(0, versionInfo.vendorID);
    TEST_ASSERT_EQUAL(DIO_MODULE_ID, versionInfo.moduleID);
    TEST_ASSERT_EQUAL(DIO_INSTANCE_ID, versionInfo.instanceID);
    TEST_ASSERT_EQUAL(DIO_CFG_SW_MAJOR_VERSION, versionInfo.sw_major_version);
    TEST_ASSERT_EQUAL(DIO_CFG_SW_MINOR_VERSION, versionInfo.sw_minor_version);
    TEST_ASSERT_EQUAL(DIO_CFG_SW_PATCH_VERSION, versionInfo.sw_patch_version);
    TEST_ASSERT_EQUAL(DIO_CFG_AR_RELEASE_MAJOR_VERSION, versionInfo.ar_major_version);
    TEST_ASSERT_EQUAL(DIO_CFG_AR_RELEASE_MINOR_VERSION, versionInfo.ar_minor_version);
    TEST_ASSERT_EQUAL(DIO_CFG_AR_RELEASE_PATCH_VERSION, versionInfo.ar_patch_version);

}
TEST(dio_swc, test_Dio_GetVersionInfo_VersionInfo_Null)
{
    // Expect a call to Det_ReportError with specific parameters and return E_OK
    Det_ReportError_ExpectAndReturn(DIO_MODULE_ID, DIO_INSTANCE_ID, DIO_GET_VERSION_INFO_SID, DIO_E_PARAM_POINTER,E_OK);

    // Call the function with a null pointer
    Dio_GetVersionInfo(NULL);


}

brooo make sure you add your test function test_Dio_GetVersionInfo_ValidVersionInfo_Null , test_Dio_GetVersionInfo_VersionInfo_Null and to the test runner file.

TEST_GROUP_RUNNER(dio_swc)
{
  RUN_TEST_CASE(dio_swc, test_Dio_GetVersionInfo_ValidVersionInfo)
  RUN_TEST_CASE(dio_swc, test_Dio_GetVersionInfo_VersionInfo_Null)
}

Output

make unit_test rebuild=1

Output

the html report will be @ tools/build/CMake/output/tests/unit/html

Html

Html