Skip to content

Commit

Permalink
Merge pull request #368 from LedgerHQ/add-nbgl-unit-tests
Browse files Browse the repository at this point in the history
Add Unit-Tests for some NBGL elements
  • Loading branch information
nroggeman-ledger authored Jul 5, 2023
2 parents 3ce83ba + fe973c0 commit 2108082
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 9 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
run: |
cd unit-tests/lib_standard_app/
cmake -Bbuild -H. && make -C build && make -C build test
cd ../lib_nbgl/
cmake -Bbuild -H. && make -C build && CTEST_OUTPUT_ON_FAILURE=1 make -C build test
- name: Generate code coverage
run: |
Expand All @@ -33,17 +35,24 @@ jobs:
lcov --directory . -b "$(realpath build/)" --add-tracefile coverage.base --add-tracefile coverage.capture -o coverage.info && \
lcov --directory . -b "$(realpath build/)" --remove coverage.info '*/unit-tests/*' -o coverage.info && \
genhtml coverage.info -o coverage
cd ../lib_nbgl/
lcov --directory . -b "$(realpath build/)" --capture --initial -o coverage.base && \
lcov --rc lcov_branch_coverage=1 --directory . -b "$(realpath build/)" --capture -o coverage.capture && \
lcov --directory . -b "$(realpath build/)" --add-tracefile coverage.base --add-tracefile coverage.capture -o coverage.info && \
lcov --directory . -b "$(realpath build/)" --remove coverage.info '*/unit-tests/*' -o coverage.info && \
genhtml coverage.info -o coverage
- uses: actions/upload-artifact@v3
with:
name: code-coverage
path: unit-tests/lib_standard_app/coverage
path: unit-tests/lib_*/coverage

- name: Upload to codecov.io
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./unit-tests/lib_standard_app/coverage.info
files: ./unit-tests/lib_standard_app/coverage.info,./unit-tests/lib_nbgl/coverage.info
flags: unittests
name: codecov-app-boilerplate
fail_ci_if_error: true
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ lib_bagl/src/bagl_font_open_sans_light_16px_unicode.inc
lib_bagl/src/bagl_font_open_sans_regular_11px_unicode.inc
lib_nbgl/build
TAGS
unit-tests/lib_standard_app/build/
unit-tests/lib_*/build/
12 changes: 6 additions & 6 deletions lib_nbgl/src/nbgl_fonts.c
Original file line number Diff line number Diff line change
Expand Up @@ -734,28 +734,28 @@ nbgl_unicode_ctx_t* nbgl_getUnicodeFont(nbgl_font_id_e fontId) {
*/
const nbgl_font_unicode_character_t *nbgl_getUnicodeFontCharacter(uint32_t unicode) {
#if defined(HAVE_LANGUAGE_PACK)
const nbgl_font_unicode_character_t *characters = unicodeCtx.characters;
const nbgl_font_unicode_character_t *characters = (const nbgl_font_unicode_character_t *)PIC(unicodeCtx.characters);
uint32_t n = language_pack->nb_characters;
if (characters == NULL) {
return NULL;
}
// For the moment, let just parse the full array, but at the end let use
// binary search as data are sorted by unicode value !
for (unsigned i=0; i < n-1; i++, characters++) {
if ((PIC(characters))->char_unicode == unicode) {
if (characters->char_unicode == unicode) {
// Compute & store the number of bytes used to display this character
unicodeCtx.unicode_character_byte_count = \
(PIC(characters+1))->bitmap_offset - (PIC(characters))->bitmap_offset;
return (PIC(characters));
(characters+1)->bitmap_offset - characters->bitmap_offset;
return characters;
}
}
// By default, let's use the last Unicode character, which should be the
// 0x00FFFD one, used to replace unrecognized or unrepresentable character.

// Compute & store the number of bytes used to display this character
unicodeCtx.unicode_character_byte_count = unicodeCtx.font->bitmap_len - \
(PIC(characters))->bitmap_offset;
return (PIC(characters));
characters->bitmap_offset;
return characters;
#else //defined(HAVE_LANGUAGE_PACK)
UNUSED(unicode);
// id not found
Expand Down
62 changes: 62 additions & 0 deletions unit-tests/lib_nbgl/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
cmake_minimum_required(VERSION 3.10)

if(${CMAKE_VERSION} VERSION_LESS 3.10)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()

# project information
project(unit_tests
VERSION 0.1
DESCRIPTION "Unit tests for NBGL"
LANGUAGES C)


# guard against bad build-type strings
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
endif()

include(CTest)
ENABLE_TESTING()

# specify C standard
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
add_compile_definitions(HAVE_BAGL_FONT_INTER_REGULAR_24PX)
add_compile_definitions(HAVE_BAGL_FONT_INTER_SEMIBOLD_24PX)
add_compile_definitions(HAVE_BAGL_FONT_INTER_MEDIUM_32PX)
add_compile_definitions(HAVE_BAGL_FONT_HMALPHAMONO_MEDIUM_32PX)
add_compile_definitions(HAVE_LANGUAGE_PACK)
add_compile_definitions(HAVE_UNICODE_SUPPORT)
add_compile_definitions(USB_SEGMENT_SIZE=64)

set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall ${DEFINES} -g -O0 --coverage")

set(GCC_COVERAGE_LINK_FLAGS "--coverage -lgcov")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")

# guard against in-source builds
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ")
endif()

add_compile_definitions(TEST)

include_directories(../../target/stax/include)
include_directories(../../include)
include_directories(../../lib_nbgl/include)
include_directories(../../lib_ux_stax)

add_executable(test_nbgl_fonts test_nbgl_fonts.c)
add_executable(test_nbgl_screen test_nbgl_screen.c)

add_library(nbgl_obj_pool SHARED ../../lib_nbgl/src/nbgl_obj_pool.c)
add_library(nbgl_fonts SHARED ../../lib_nbgl/src/nbgl_fonts.c)
add_library(nbgl_screen SHARED ../../lib_nbgl/src/nbgl_screen.c)

target_link_libraries(test_nbgl_fonts PUBLIC cmocka gcov nbgl_fonts)
target_link_libraries(test_nbgl_screen PUBLIC cmocka gcov nbgl_screen nbgl_obj_pool)

add_test(test_nbgl_fonts test_nbgl_fonts)
add_test(test_nbgl_screen test_nbgl_screen)
36 changes: 36 additions & 0 deletions unit-tests/lib_nbgl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Unit tests

## Prerequisite

Be sure to have installed:

- CMake >= 3.10
- CMocka >= 1.1.5

and for code coverage generation:

- lcov >= 1.14

## Overview

In `unit-tests` folder, compile with

```
cmake -Bbuild -H. && make -C build
```

and run tests with

```
CTEST_OUTPUT_ON_FAILURE=1 make -C build test
```

## Generate code coverage

Just execute in `unit-tests` folder

```
./gen_coverage.sh
```

it will output `coverage.total` and `coverage/` folder with HTML details (in `coverage/index.html`).
Binary file added unit-tests/lib_nbgl/bolos_pack_fr.bin
Binary file not shown.
167 changes: 167 additions & 0 deletions unit-tests/lib_nbgl/test_nbgl_fonts.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <string.h>

#include <cmocka.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include "nbgl_fonts.h"
#include "ux_loc.h"

const LANGUAGE_PACK *language_pack = NULL;

static void fetch_language_packs(void) {
// If we are looking for a language pack:
// - if the expected language is found then we'll use its begin/length range.
// - else we'll use the built-in package and need to reset allowed MMU range.

FILE *fptr = NULL;

fptr = fopen("../bolos_pack_fr.bin","rb");

assert_non_null(fptr);
if (fptr != NULL) {
fseek(fptr, 0, SEEK_END);

uint32_t len = ftell(fptr);

fseek(fptr, 0, SEEK_SET);

uint8_t *source = (uint8_t*)malloc(len);

assert_non_null(source);

assert_int_equal(fread((unsigned char*)source, 1, len, fptr),len);

fclose(fptr);

language_pack = (LANGUAGE_PACK *)source;

}
}

#include "nbgl_fonts.h"
#include "nbgl_font_hmalpha_mono_medium_32.inc"
#include "nbgl_font_inter_regular_24.inc"
#include "nbgl_font_inter_semibold_24.inc"
#include "nbgl_font_inter_medium_32.inc"
#include "nbgl_font_inter_regular_24_1bpp.inc"
#include "nbgl_font_inter_semibold_24_1bpp.inc"
#include "nbgl_font_inter_medium_32_1bpp.inc"

static const nbgl_font_t* const C_nbgl_fonts[] = {
#include "nbgl_font_rom_struct.inc"
};
static const unsigned int C_nbgl_fonts_count = sizeof(C_nbgl_fonts)/sizeof(C_nbgl_fonts[0]);

const nbgl_font_t* nbgl_font_getFont(unsigned int fontId) {
unsigned int i = C_nbgl_fonts_count;
while(i--) {
// font id match this entry (non indexed array)
if (C_nbgl_fonts[i]->font_id == fontId) {
return C_nbgl_fonts[i];
}
}

// id not found
return NULL;
}

void *pic(void *addr) {
return addr;
}

static void test_get_length(void **state __attribute__((unused))) {
char *str_with_unicode= "çoto";
char *str_without_unicode= "toto";
fetch_language_packs();

uint16_t width = nbgl_getTextWidth(BAGL_FONT_INTER_REGULAR_24px, str_without_unicode);
assert_int_equal(width,46);
uint16_t len = nbgl_getTextLength(str_without_unicode);

assert_int_equal(len,4);
len = nbgl_getTextLength(str_with_unicode);
assert_int_equal(len,5);
assert_int_equal(strlen(str_with_unicode),5);

width = nbgl_getTextWidth(BAGL_FONT_INTER_REGULAR_24px, str_with_unicode);
assert_int_equal(width,45);

char myChar = 0x30;
width = nbgl_getCharWidth(BAGL_FONT_INTER_REGULAR_24px, &myChar);
assert_int_equal(width,15);
width = nbgl_getCharWidth(BAGL_FONT_INTER_REGULAR_24px, "ç");
assert_int_equal(width,8);

assert_int_equal(nbgl_getTextNbLines(str_without_unicode),1);
assert_int_equal(nbgl_getTextNbLines(str_with_unicode),1);
assert_int_equal(nbgl_getTextNbLines("bonjour\nau revoir"),2);
assert_int_equal(nbgl_getTextNbLines("bonjour\nau çevoir"),2);

// '\n' is considered as end of string for nbgl_getTextLength
assert_int_equal(nbgl_getTextLength("bonjour\nau revoir"),7);
// 'ç' counts for 2 bytes
assert_int_equal(nbgl_getTextLength("bonçour\nau revoir"),8);

nbgl_getTextMaxLenAndWidth(BAGL_FONT_INTER_REGULAR_24px,"totoour\nau revoir", 50, &len, &width, false);
assert_int_equal(len,4);
assert_int_equal(width,46);

assert_int_equal(nbgl_getTextWidth(BAGL_FONT_INTER_REGULAR_24px,"au revoir"),100);
assert_int_equal(nbgl_getTextWidth(BAGL_FONT_INTER_REGULAR_24px,"totoour"),83);
assert_int_equal(strlen("totoour\nau revoir"),17);
nbgl_getTextMaxLenAndWidth(BAGL_FONT_INTER_REGULAR_24px,"totoour\nau revoir", 100, &len, &width, false);
assert_int_equal(len,7);
assert_int_equal(width,83); // width of latest line, doesn't mean anything

uint8_t nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_REGULAR_24px,"totoour\nau revoir", 100, false);
assert_int_equal(nbLines,2);
nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_REGULAR_24px,"totoour\nau revoir", 60, false);
assert_int_equal(nbLines,4);
nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_REGULAR_24px,"totoour\na", 50, false);
assert_int_equal(nbLines,3);
nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_REGULAR_24px,"totoour\nau revoi", 50, false);
assert_int_equal(nbLines,4);

nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_REGULAR_24px,"totçour\nau revoir", 100, false);
assert_int_equal(nbLines,2);
nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_REGULAR_24px,"totçour\nau revoir", 60, false);
assert_int_equal(nbLines,4);
nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_REGULAR_24px,"totçour\na", 50, false);
assert_int_equal(nbLines,3);
nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_REGULAR_24px,"totçour\nau revoi", 50, false);
assert_int_equal(nbLines,4);

nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_REGULAR_24px,"totçour au revoi", 100, true);
assert_int_equal(nbLines,2);

nbgl_getTextMaxLenAndWidth(BAGL_FONT_INTER_REGULAR_24px,"totçour\nau revoir", 50, &len, &width, false);
assert_int_equal(len,5);
assert_int_equal(width,40);

char textToWrap[32] = "toto";
nbgl_textWrapOnNbLines(BAGL_FONT_INTER_SEMIBOLD_24px, textToWrap, 156, 2);
assert_string_equal(textToWrap,"toto");

strcpy(textToWrap,"bonjour tu aimes les mois");
nbgl_textWrapOnNbLines(BAGL_FONT_INTER_SEMIBOLD_24px, textToWrap, 156, 2);
assert_string_equal(textToWrap,"bonjour tu\naimes les...");

strcpy(textToWrap,"bonjourtuaimestr les mois");
nbgl_textWrapOnNbLines(BAGL_FONT_INTER_SEMIBOLD_24px, textToWrap, 156, 2);
assert_string_equal(textToWrap,"bonjourtuaimestr les...");

nbLines = nbgl_getTextNbLinesInWidth(BAGL_FONT_INTER_MEDIUM_32px, "AB WWWWWWWW WWW W", 200, true);
assert_int_equal(nbLines,4);

int height = nbgl_getTextHeightInWidth(BAGL_FONT_INTER_MEDIUM_32px, "AB WWWWWWWW WWW W", 200, true);
assert_int_equal(height,160);
}

int main(int argc, char **argv) {
const struct CMUnitTest tests[] = {cmocka_unit_test(test_get_length)};
return cmocka_run_group_tests(tests, NULL, NULL);
}
Loading

0 comments on commit 2108082

Please sign in to comment.