FOREACH(hfile ${HEADERS})
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${hfile} DESTINATION include)
ENDFOREACH(hfile)
+
+option(ENABLE_TESTS "Run unit tests after build" ON)
+
+if(ENABLE_TESTS)
+ find_package(CMocka CONFIG REQUIRED)
+ enable_testing()
+ include(CTest)
+ include_directories(${LIBCMOCKA_INCLUDE_DIR})
+ add_subdirectory(test)
+endif(ENABLE_TESTS)
return result;
}
-
static size_t decompress_bytes(struct bs_data *data, size_t keep_offset)
{
assert(data);
return 0;
}
+#ifndef LIBTOTA_U_TEST
int MY_CDECL main(int argc, char *argv[])
{
char rs[800] = { 0 };
fputs(rs, stdout);
return res;
}
+#endif
BuildRequires: cmake
BuildRequires: pkgconfig(liblzma-tool)
BuildRequires: libbrotli-devel
+BuildRequires: pkgconfig(cmocka)
+BuildRequires: pkgconfig(libdivsufsort)
%description
Fota update agent which update firmware using delta files
--- /dev/null
+add_custom_target(ctest ALL make CTEST_OUTPUT_ON_FAILURE=1 test WORKING_DIRECTORY ./ USES_TERMINAL)
+
+file ( GLOB all_prj_srcs
+ "${CMAKE_CURRENT_SOURCE_DIR}/../ss_engine/*.c"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../bsdiff/ss_bspatch_common.c"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../bsdiff/ss_brotli_patch.c"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../bsdiff/ss_bsdiff.c"
+)
+
+include(FindPkgConfig)
+pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED liblzma-tool libdivsufsort)
+
+function(add_mocked_test name wraps)
+
+ include(FindPkgConfig)
+ pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED liblzma-tool)
+ add_compile_definitions(LIBTOTA_U_TEST)
+
+ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../ss_engine/)
+ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../bsdiff/)
+
+ add_executable(test_${name} test_${name}.c common.c ${all_prj_srcs} )
+ target_link_libraries(test_${name} cmocka ${${PROJECT_NAME}_pkgs_LDFLAGS} divsufsort brotlicommon brotlidec brotlienc pthread ${wraps})
+
+ message("--> ${CMAKE_CURRENT_BINARY_DIR}")
+ add_test(test_${name} ${CMAKE_CURRENT_BINARY_DIR}/test_${name})
+ ADD_DEPENDENCIES(ctest test_${name})
+endfunction(add_mocked_test)
+
+add_mocked_test(sha1 "")
+add_mocked_test(img_verfiy_partition "-Wl,--defsym=SS_GetAvailableFreeSpace=mock_SS_GetAvailableFreeSpace -Wl,--defsym=SS_CalculateFileSha=mock_SS_CalculateFileSha")
+add_mocked_test(brotli_compress "-Wl,--wrap=open -Wl,--wrap=mmap -Wl,--wrap=ftruncate -Wl,--wrap=lseek")
--- /dev/null
+/*MIT License
+
+Copyright (c) 2022 Samsung Electronics Co., Ltd.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <cmocka.h>
+
+#include "common.h"
+#include "test_vars.h"
+#include "sha1.h"
+#include "fota_log.h"
+
+
+struct mock_file_data mck_files[_MOCK_FILE_DATA_ARR_SIZE] = {
+ {.filename = NULL, .data = NULL, .size = 0},
+ {.filename = NULL, .data = NULL, .size = 0},
+ {.filename = NULL, .data = NULL, .size = 0}
+};
+
+void *make_test_mem_raw_data(const uint8_t * pattern, uint32_t pattern_size, uint32_t repeat_pattern) {
+ size_t data_size = pattern_size * repeat_pattern;
+ void *raw_data = NULL;
+ raw_data = malloc(data_size);
+
+ print_message("size_t size %d\n", sizeof(size_t) );
+ print_message("raw data at %x\n", raw_data );
+ if (raw_data == NULL) {
+ return NULL;
+ }
+
+ void * current_mem_ptr = raw_data;
+ while(current_mem_ptr < raw_data + data_size) {
+ memcpy(current_mem_ptr, (const void *) pattern, pattern_size);
+ current_mem_ptr += pattern_size;
+ }
+ return raw_data;
+}
+
+void compute_reference_sha1_of_raw_data(void * raw_data, uint32_t data_size, uint8_t *sha1_result) {
+
+ SHA1_CTX ctx;
+ print_message("make sha1 test \n");
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, (const unsigned char *)raw_data, data_size);
+ SHA1Final(sha1_result, &ctx);
+ return;
+}
+
+int make_files_field_data(int arr_id, const uint8_t *pattern_name, const uint8_t *pattern, uint32_t pattern_size, uint32_t repeat_pattern) {
+
+ if (arr_id >= _MOCK_FILE_DATA_ARR_SIZE) {
+ return -EBADR;
+ }
+ void *data = make_test_mem_raw_data(pattern, _PATTERN_01_SIZE, _PATTERN_01_REPEAT);
+ mck_files[arr_id].data = data;
+ mck_files[arr_id].filename = pattern_name;
+ mck_files[arr_id].size = (pattern_size * repeat_pattern);
+ return 0;
+}
+
+int make_files_field_empty_data(int arr_id, const uint8_t *filename, uint32_t size) {
+
+ if (arr_id >= _MOCK_FILE_DATA_ARR_SIZE) {
+ return -EBADR;
+ }
+ void *data = malloc(size);
+ if (data == MAP_FAILED) {
+ return -ENOMEM ;
+ }
+ memset(data, 0, size);
+ mck_files[arr_id].data = data;
+ mck_files[arr_id].filename = filename;
+ mck_files[arr_id].size = size;
+ return 0;
+}
+
+void free_files() {
+ for(int i=0; i<_MOCK_FILE_DATA_ARR_SIZE; i++) {
+ if (mck_files[i].data != NULL) { free(mck_files[i].data); }
+ }
+ }
--- /dev/null
+#pragma once
+
+#include<stdint.h>
+
+#include "ua_types.h"
+#include "sha1.h"
+#include "fota_log.h"
+#include "fota_common.h"
+#include "SS_PatchDelta.h"
+#include "SS_UPI.h"
+#include "SS_Engine_Errors.h"
+#include "test_vars.h"
+
+#define _UNCOMPRESSED_0 0
+#define _COMPRESSED_1 1
+#define _DECOMPRESSED_0 2
+
+#define _parens ()
+
+#define _expand(...) _expand4(_expand4(_expand4(_expand4(__VA_ARGS__))))
+#define _expand4(...) _expand3(_expand3(_expand3(_expand3(__VA_ARGS__))))
+#define _expand3(...) _expand2(_expand2(_expand2(_expand2(__VA_ARGS__))))
+#define _expand2(...) _expand1(_expand1(_expand1(_expand1(__VA_ARGS__))))
+#define _expand1(...) __VA_ARGS__
+
+#define _f_comp(a1, ...) \
+ __VA_OPT__(_expand(_first_function(a1, __VA_ARGS__)))
+
+#define _next_function(a1, ...) \
+ (a1 __VA_OPT__(_function_again _parens ( __VA_ARGS__)))
+
+
+#define _first_function(a1, ...) \
+ a1 __VA_OPT__(_function_again _parens ( __VA_ARGS__))
+
+#define _function_again() _next_function
+
+
+struct mock_file_data {
+ const uint8_t * filename;
+ void * data;
+ uint32_t size;
+};
+
+#define _MOCK_FILE_DATA_ARR_SIZE 3
+
+extern struct mock_file_data mck_files[_MOCK_FILE_DATA_ARR_SIZE];
+
+void *make_test_mem_raw_data(const uint8_t * pattern, uint32_t pattern_size, uint32_t repeat_pattern);
+void compute_reference_sha1_of_raw_data(void * raw_data, uint32_t data_size, uint8_t *sha1_result);
+int make_files_field_data(int arr_id, const uint8_t *pattern_name, const uint8_t *pattern, uint32_t pattern_size, uint32_t repeat_pattern);
+int make_files_field_empty_data(int arr_id, const uint8_t *filename, uint32_t size);
+void free_files();
--- /dev/null
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cmocka.h>
+#include <arpa/inet.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include <brotli/encode.h>
+#include <brotli/decode.h>
+
+#include "test_vars.h"
+#include "common.h"
+
+int brotli_compress(const char *input_file, const char *output_file, int quality);
+
+int _system_cmd_wait(const char * cmd) {
+ return system(cmd);
+}
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
+ print_message("mock_map ===> \n");
+ if (fd >= _MOCK_FILE_DATA_ARR_SIZE ) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (mck_files[fd].data == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ return mck_files[fd].data;
+}
+
+//setting errno only for testing
+int open(const char *pathname, int flags, ...) {
+ print_message("mock_open ===> pathname: %s\n", pathname);
+
+ for(int i=0; i<_MOCK_FILE_DATA_ARR_SIZE; i++) {
+ if (pathname == NULL) {
+ continue;
+ }
+ if (mck_files[i].filename == NULL) {
+ continue;
+ }
+ if (strcmp(pathname, (const char*)mck_files[i].filename) == 0) {
+
+ if (mck_files[i].data == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+ if (mck_files[i].size == 0 ) {
+ errno = ENOSPC;
+ return -1;
+ }
+ return i;
+ }
+ }
+ errno = ENOENT;
+ return -1;
+}
+
+#define _want_realloc 1
+int ftruncate(int fd, off_t length) {
+ print_message("mock ftruncate ===> fd: %d, to size: %d \n", fd, length);
+ #ifdef _want_reaalloc
+ mck_files[fd].data = realloc(mck_files[fd].data, (size_t) length);
+ if ((mck_files[fd].data) == NULL) {
+ return -1;
+ }
+ #endif
+ mck_files[fd].size = (uint32_t) length;
+ return 0;
+}
+
+off_t lseek(int fd, off_t offset, int whence) {
+ print_message("mock lseek ===> \n");
+ if (whence == SEEK_END) return (off_t) mck_files[fd].size;
+ return 0;
+}
+
+static void test_brotli_compress(void **state) {
+ (void) state;
+
+ int res = make_files_field_data(0, (const uint8_t *) _PATTERN_01_NAME, _PATTERN_01, _PATTERN_01_SIZE, _PATTERN_01_REPEAT); assert_true(res == 0);
+ if(res != 0) {
+ print_message("make_files_field_data failed");
+ free_files();
+ assert_int_equal(res,0);
+ }
+
+ res = make_files_field_empty_data(1, "compressed", (uint32_t) BrotliEncoderMaxCompressedSize( _PATTERN_01_SIZE * _PATTERN_01_REPEAT));
+ if(res != 0) {
+ print_message("make_files_field_data failed");
+ free_files();
+ assert_int_equal(res,0);
+ }
+
+ res = make_files_field_empty_data(2, "uncompressed", _PATTERN_01_SIZE * _PATTERN_01_REPEAT);
+ if(res != 0) {
+ print_message("make_files_field_empty_data failed");
+ free_files();
+ assert_int_equal(res,0);
+ }
+
+ int brotli_compress_result = brotli_compress(_PATTERN_01_NAME, "compressed", BROTLI_DEFAULT_QUALITY);
+
+ if(brotli_compress_result != 0) {
+
+ print_message("brotli compress failed");
+ free_files();
+ assert_int_equal(brotli_compress_result,0);
+ }
+
+ BrotliDecoderErrorCode brotli_decompress_result = BrotliDecoderDecompress((size_t)mck_files[_COMPRESSED_1].size, (const uint8_t *)mck_files[_COMPRESSED_1].data,
+ (size_t)mck_files[_DECOMPRESSED_0].size, (const uint8_t *)mck_files[_DECOMPRESSED_0].data);
+
+ if(mck_files[_UNCOMPRESSED_0].size != mck_files[_DECOMPRESSED_0].size) {
+ print_message("compared data size not equal");
+ free_files();
+ assert_int_equal(mck_files[_UNCOMPRESSED_0].size, mck_files[_DECOMPRESSED_0].size);
+ }
+
+ if(brotli_decompress_result != BROTLI_DECODER_SUCCESS) {
+ print_message("brotli decompres failed");
+ free_files();
+ assert_true(brotli_decompress_result == BROTLI_DECODER_SUCCESS);
+ }
+
+ int compare_result = memcmp(mck_files[_UNCOMPRESSED_0].data, mck_files[_DECOMPRESSED_0].data, mck_files[_DECOMPRESSED_0].size);
+ if(compare_result != 0) {
+ print_message("compared data are not equal");
+ free_files();
+ assert_int_equal(compare_result, 0);
+}
+ free_files();
+}
+
+int main(void) {
+
+ __log_out_file__ = stdout;
+
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_brotli_compress),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
--- /dev/null
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cmocka.h>
+#include <arpa/inet.h>
+
+#include "sha1.h"
+
+#include "common.h"
+#include "test_vars.h"
+
+int _system_cmd_wait(const char * cmd) {
+ return system(cmd);
+}
+
+void mock_ui_progress(void *data, unsigned long value) {
+ return;
+}
+
+long
+mock_SS_GetAvailableFreeSpace(const char *partition_name,
+ SS_UINT32 *available_flash_size)
+{
+ print_message("mock SS_GetAvailableFreeSpace fof %s\n", partition_name);
+ if (partition_name == NULL) {
+ return -EBADFD;
+ }
+ *available_flash_size = _PATTERN_02_SIZE * _PATTERN_02_REPEAT * 4 ;
+ return 0;
+
+}
+
+int mock_SS_CalculateFileSha(char *filename, long int filesize, unsigned char calculated_sha1[SHA_DIGEST_SIZE]) {
+ for(int i = 0; i <_MOCK_FILE_DATA_ARR_SIZE; i++ ) {
+ if (strcmp(filename, mck_files[i].filename) == 0) {
+ if (mck_files[i].data == NULL) {
+ return E_SS_FAILURE;
+ }
+ if (mck_files[i].size == 0) {
+ return E_SS_FAILURE;
+ }
+ compute_reference_sha1_of_raw_data(mck_files[i].data, mck_files[i].size, calculated_sha1);
+ return 0;
+ }
+ }
+ return E_SS_FAILURE;
+}
+
+static void test_verfiy_partition(void **state) {
+ (void) state;
+
+ const uint8_t * pattern_01_data = _PATTERN_01_A;
+ const uint8_t * pattern_01_file_name = _PATTERN_01_NAME;
+ void *test_mem_raw_data_01 = NULL;
+ test_mem_raw_data_01 = make_test_mem_raw_data(pattern_01_data, _PATTERN_01_SIZE, _PATTERN_01_REPEAT);
+ const char *area_01_sha1_txt = _AREA_01_SHA1;
+ assert_ptr_not_equal(test_mem_raw_data_01, NULL);
+
+ mck_files[0].filename = pattern_01_file_name;
+ mck_files[0].data = test_mem_raw_data_01;
+ mck_files[0].size = _PATTERN_02_SIZE *_PATTERN_02_REPEAT;
+
+ const uint8_t * pattern_02_data = _PATTERN_02_A;
+ const uint8_t * pattern_02_file_name = _PATTERN_02_NAME;
+ void *test_mem_raw_data_02 = NULL;
+ test_mem_raw_data_02 = make_test_mem_raw_data(pattern_02_data, _PATTERN_02_SIZE, _PATTERN_02_REPEAT);
+ assert_ptr_not_equal(test_mem_raw_data_02, NULL);
+ const char *area_02_sha1_txt = _AREA_02_SHA1;
+
+ mck_files[1].filename = pattern_02_file_name;
+ mck_files[1].data = test_mem_raw_data_02;
+ mck_files[1].size = _PATTERN_02_SIZE *_PATTERN_02_REPEAT;
+
+ ua_part_info_t part_info;
+ ua_update_cfg_t update_cfg;
+
+ update_cfg.soure_img_size = _PATTERN_01_SIZE * _PATTERN_01_REPEAT;
+ update_cfg.soure_sha1 = area_01_sha1_txt;
+
+ update_cfg.target_img_size = _PATTERN_02_SIZE * _PATTERN_02_REPEAT;
+ update_cfg.target_sha1 = area_02_sha1_txt;
+
+ ua_dataSS_t ua_dataSS;
+
+ ua_dataSS.update_cfg = &update_cfg;
+ ua_dataSS.parti_info = &part_info;
+
+ ua_dataSS.ui_progress = mock_ui_progress;
+ const char * src_blk_name = _PATTERN_01_NAME;
+
+ int img_part_veryfi_result = SS_IMGVerfiyPartition(&ua_dataSS, src_blk_name, true);
+
+ if(test_mem_raw_data_01 != NULL) { free(test_mem_raw_data_01); }
+ if(test_mem_raw_data_02 != NULL) { free(test_mem_raw_data_02); }
+
+ assert_int_equal(img_part_veryfi_result, 0);
+}
+
+int main(void) {
+
+ __log_out_file__ = stdout;
+
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_verfiy_partition),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
--- /dev/null
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cmocka.h>
+#include <arpa/inet.h>
+
+#include "sha1.h"
+
+#include "test_vars.h"
+
+static const uint32_t expected_hash[] = {
+ 0xce27cb14,
+ 0x1098feb0,
+ 0x0714e758,
+ 0x646be3e9,
+ 0x9c185b71
+};
+
+int _system_cmd_wait(const char * cmd) {
+ return system(cmd);
+}
+
+static void test_sha1(void **state) {
+ (void) state;
+ const unsigned char * test_data = _PATTERN_01;
+
+ uint32_t hash_value[5];
+
+ SHA1_CTX ctx;
+ print_message("make sha1 test \n");
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, test_data, _PATTERN_01_SIZE);
+ SHA1Final((unsigned char *)&hash_value, &ctx);
+
+ print_message("make sha1 test: compare result \n");
+ for(int i = 0; i < 5; i++) {
+ print_message("make sha1 test: %x -- %x \n", hash_value[i], htonl(expected_hash[i]));
+ assert_true(hash_value[i] == htonl(expected_hash[i]));
+ }
+}
+int main(void) {
+
+ __log_out_file__ = stdout;
+
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_sha1),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
--- /dev/null
+#pragma once
+
+FILE * __log_out_file__ ;
+unsigned int __log_level__;
+
+#define _PATTERN_01_NAME "pattern_01"
+#define _PATTERN_01 "0123456789ABCDEF"
+#define _PATTERN_01_A "0123456789ABCDEFA"
+#define _PATTERN_01_SIZE 16
+#define _PATTERN_01_REPEAT 1024*1024
+#define _AREA_01_SHA1 "aa030f8404810af8722d0d9c1f4aebe4bbe2e01b"
+
+#define _PATTERN_02_NAME "pattern_02"
+#define _PATTERN_02 "FEDCBA9876543210"
+#define _PATTERN_02_A "FEDCBA9876543210A"
+#define _PATTERN_02_SIZE 16
+#define _PATTERN_02_REPEAT 1024*1024
+#define _PATTERN_02_k_REPEAT 1024
+#define _AREA_02_SHA1 "185b380d420207e13bb8483f331170af87d22d3f"