Skip to content

Commit 1a513d6

Browse files
committed
Add font cache tests
1 parent 0d53dcd commit 1a513d6

13 files changed

Lines changed: 1287 additions & 2 deletions

CMakeLists.txt

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,39 @@ if(VEFC_BUILD_DEMO)
8989
vefc_apply_features(demo ${VEFC_ENABLE_HARFBUZZ} ${VEFC_ENABLE_FREETYPE})
9090
endif()
9191

92-
if(VEFC_BUILD_TESTS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_main.cpp")
92+
if(VEFC_BUILD_TESTS)
9393
enable_testing()
94-
add_subdirectory(tests EXCLUDE_FROM_ALL)
94+
95+
set(VEFC_TEST_SOURCES
96+
tests/test_main.cpp
97+
)
98+
99+
function(vefc_add_test_target target use_harfbuzz use_freetype)
100+
add_executable(${target} ${VEFC_TEST_SOURCES})
101+
set_target_properties(${target} PROPERTIES
102+
VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/demo"
103+
)
104+
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests)
105+
vefc_apply_common_options(${target})
106+
vefc_apply_features(${target} ${use_harfbuzz} ${use_freetype})
107+
add_test(
108+
NAME ${target}
109+
COMMAND ${target}
110+
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/demo"
111+
)
112+
endfunction()
113+
114+
vefc_add_test_target(vefc_tests_base OFF OFF)
115+
116+
if(VEFC_ENABLE_HARFBUZZ)
117+
vefc_add_test_target(vefc_tests_hb ON OFF)
118+
endif()
119+
120+
if(VEFC_ENABLE_FREETYPE)
121+
vefc_add_test_target(vefc_tests_ft OFF ON)
122+
endif()
123+
124+
if(VEFC_ENABLE_HARFBUZZ AND VEFC_ENABLE_FREETYPE)
125+
vefc_add_test_target(vefc_tests_hb_ft ON ON)
126+
endif()
95127
endif()

tests/test_backend.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "test_common.h"
2+
3+
UTEST( backend, conformance_header_passes )
4+
{
5+
vefc_test::context ctx;
6+
ve_font_id font = ctx.load_file( vefc_test::kNotoSansJP );
7+
8+
ASSERT_GE( font, 0 );
9+
ve_fontcache_backend_test_result result = ve_fontcache_backend_test_run( &ctx.cache, font );
10+
EXPECT_EQ( 0, result.failed );
11+
EXPECT_TRUE( result.passed > 0 );
12+
}

tests/test_common.h

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
#pragma once
2+
3+
#ifndef WIN32_LEAN_AND_MEAN
4+
#define WIN32_LEAN_AND_MEAN
5+
#endif
6+
7+
#ifndef NOMINMAX
8+
#define NOMINMAX
9+
#endif
10+
11+
#include <algorithm>
12+
#include <array>
13+
#include <cmath>
14+
#include <cstdint>
15+
#include <limits>
16+
#include <random>
17+
#include <string>
18+
#include <string_view>
19+
#include <vector>
20+
21+
#ifdef VE_FONTCACHE_FREETYPE_RASTERISATION
22+
#include <ft2build.h>
23+
#include FT_FREETYPE_H
24+
#include FT_GLYPH_H
25+
#endif
26+
27+
#ifdef VE_FONTCACHE_HARFBUZZ
28+
#include <hb.h>
29+
#endif
30+
31+
#define VE_STBTT_NOIMPL
32+
#include "../utf8/test/utest.h"
33+
#include "../ve_fontcache.h"
34+
#include "../ve_fontcache_backend_test.h"
35+
36+
namespace vefc_test
37+
{
38+
inline constexpr float kScaleX = 1.0f / 1920.0f;
39+
inline constexpr float kScaleY = 1.0f / 1080.0f;
40+
inline constexpr ve_fontcache_poollist_itr kInvalidPoolItr = static_cast< ve_fontcache_poollist_itr >( -1 );
41+
42+
inline constexpr const char* kRoboto = "fonts/Roboto-Regular.ttf";
43+
inline constexpr const char* kOpenSans = "fonts/OpenSans-Regular.ttf";
44+
inline constexpr const char* kNotoSansJP = "fonts/NotoSansJP-Regular.otf";
45+
inline constexpr const char* kNotoSerifSC = "fonts/NotoSerifSC-Regular.otf";
46+
inline constexpr const char* kTajawal = "fonts/Tajawal-Regular.ttf";
47+
48+
inline bool default_use_freetype()
49+
{
50+
#ifdef VE_FONTCACHE_FREETYPE_RASTERISATION
51+
return true;
52+
#else
53+
return false;
54+
#endif
55+
}
56+
57+
struct context
58+
{
59+
ve_fontcache cache {};
60+
std::vector< std::vector< uint8_t > > buffers;
61+
62+
explicit context( bool use_freetype = default_use_freetype() )
63+
{
64+
ve_fontcache_init( &cache, use_freetype );
65+
}
66+
67+
~context()
68+
{
69+
ve_fontcache_shutdown( &cache );
70+
}
71+
72+
ve_font_id load_file( const char* path, float size_px = 24.0f )
73+
{
74+
buffers.emplace_back();
75+
return ve_fontcache_loadfile( &cache, path, buffers.back(), size_px );
76+
}
77+
78+
ve_font_id load_buffer_copy( size_t buffer_idx, float size_px = 24.0f )
79+
{
80+
std::vector< uint8_t >& buffer = buffers[ buffer_idx ];
81+
return ve_fontcache_load( &cache, buffer.data(), buffer.size(), size_px );
82+
}
83+
};
84+
85+
inline void flush( context& ctx )
86+
{
87+
ve_fontcache_flush_drawlist( &ctx.cache );
88+
}
89+
90+
inline bool draw_text( context& ctx, ve_font_id font, std::u8string_view text, float posx = 0.0f, float posy = 0.0f, bool shape_cache = true )
91+
{
92+
return ve_fontcache_draw_text( &ctx.cache, font, std::u8string( text ), posx, posy, kScaleX, kScaleY, shape_cache );
93+
}
94+
95+
inline ve_fontcache_drawlist* current_drawlist( context& ctx, bool optimise = true )
96+
{
97+
if ( optimise ) {
98+
ve_fontcache_optimise_drawlist( &ctx.cache );
99+
}
100+
return ve_fontcache_get_drawlist( &ctx.cache );
101+
}
102+
103+
inline bool is_target_pass( uint32_t pass )
104+
{
105+
return pass == VE_FONTCACHE_FRAMEBUFFER_PASS_TARGET
106+
|| pass == VE_FONTCACHE_FRAMEBUFFER_PASS_TARGET_UNCACHED
107+
|| pass == VE_FONTCACHE_FRAMEBUFFER_PASS_TARGET_CPU_CACHED;
108+
}
109+
110+
inline const ve_fontcache_draw* find_first_target_draw( const ve_fontcache_drawlist& drawlist )
111+
{
112+
for ( const ve_fontcache_draw& draw : drawlist.dcalls ) {
113+
if ( is_target_pass( draw.pass ) ) {
114+
return &draw;
115+
}
116+
}
117+
118+
return nullptr;
119+
}
120+
121+
inline int count_pass( const ve_fontcache_drawlist& drawlist, uint32_t pass )
122+
{
123+
int count = 0;
124+
for ( const ve_fontcache_draw& draw : drawlist.dcalls ) {
125+
if ( draw.pass == pass && draw.end_index > draw.start_index ) {
126+
count++;
127+
}
128+
}
129+
130+
return count;
131+
}
132+
133+
inline bool has_pass( const ve_fontcache_drawlist& drawlist, uint32_t pass )
134+
{
135+
return count_pass( drawlist, pass ) > 0;
136+
}
137+
138+
inline bool all_indices_in_range( const ve_fontcache_drawlist& drawlist )
139+
{
140+
for ( uint32_t idx : drawlist.indices ) {
141+
if ( idx >= drawlist.vertices.size() ) {
142+
return false;
143+
}
144+
}
145+
146+
return true;
147+
}
148+
149+
inline bool all_vertices_finite( const ve_fontcache_drawlist& drawlist )
150+
{
151+
for ( const ve_fontcache_vertex& v : drawlist.vertices ) {
152+
if ( !std::isfinite( v.x ) || !std::isfinite( v.y ) || !std::isfinite( v.u ) || !std::isfinite( v.v ) ) {
153+
return false;
154+
}
155+
}
156+
157+
return true;
158+
}
159+
160+
inline bool target_uvs_normalised( const ve_fontcache_drawlist& drawlist )
161+
{
162+
for ( const ve_fontcache_draw& draw : drawlist.dcalls ) {
163+
if ( !is_target_pass( draw.pass ) ) {
164+
continue;
165+
}
166+
167+
for ( uint32_t idx = draw.start_index; idx < draw.end_index; idx++ ) {
168+
const ve_fontcache_vertex& v = drawlist.vertices[ drawlist.indices[ idx ] ];
169+
if ( v.u < -0.01f || v.u > 1.01f || v.v < -0.01f || v.v > 1.01f ) {
170+
return false;
171+
}
172+
}
173+
}
174+
175+
return true;
176+
}
177+
178+
inline bool colour_equals( const float actual[ 4 ], const std::array< float, 4 >& expected )
179+
{
180+
for ( size_t i = 0; i < expected.size(); i++ ) {
181+
if ( std::fabs( actual[ i ] - expected[ i ] ) > 0.0001f ) {
182+
return false;
183+
}
184+
}
185+
186+
return true;
187+
}
188+
189+
inline std::vector< uint64_t > drain_poollist( ve_fontcache_poollist& plist )
190+
{
191+
std::vector< uint64_t > values;
192+
values.reserve( plist.size );
193+
while ( plist.size > 0 ) {
194+
values.push_back( ve_fontcache_poollist_pop_back( plist ) );
195+
}
196+
return values;
197+
}
198+
199+
inline std::u8string to_u8string( const std::string& text )
200+
{
201+
std::u8string out;
202+
out.reserve( text.size() );
203+
for ( char ch : text ) {
204+
out.push_back( static_cast< char8_t >( static_cast< unsigned char >( ch ) ) );
205+
}
206+
return out;
207+
}
208+
209+
inline void append_utf8( std::u8string& out, char32_t codepoint )
210+
{
211+
if ( codepoint <= 0x7F ) {
212+
out.push_back( static_cast< char8_t >( codepoint ) );
213+
return;
214+
}
215+
216+
if ( codepoint <= 0x7FF ) {
217+
out.push_back( static_cast< char8_t >( 0xC0 | ( codepoint >> 6 ) ) );
218+
out.push_back( static_cast< char8_t >( 0x80 | ( codepoint & 0x3F ) ) );
219+
return;
220+
}
221+
222+
if ( codepoint <= 0xFFFF ) {
223+
out.push_back( static_cast< char8_t >( 0xE0 | ( codepoint >> 12 ) ) );
224+
out.push_back( static_cast< char8_t >( 0x80 | ( ( codepoint >> 6 ) & 0x3F ) ) );
225+
out.push_back( static_cast< char8_t >( 0x80 | ( codepoint & 0x3F ) ) );
226+
return;
227+
}
228+
229+
out.push_back( static_cast< char8_t >( 0xF0 | ( codepoint >> 18 ) ) );
230+
out.push_back( static_cast< char8_t >( 0x80 | ( ( codepoint >> 12 ) & 0x3F ) ) );
231+
out.push_back( static_cast< char8_t >( 0x80 | ( ( codepoint >> 6 ) & 0x3F ) ) );
232+
out.push_back( static_cast< char8_t >( 0x80 | ( codepoint & 0x3F ) ) );
233+
}
234+
235+
inline std::u8string make_ascii_string( size_t length )
236+
{
237+
std::u8string text;
238+
text.reserve( length );
239+
for ( size_t i = 0; i < length; i++ ) {
240+
text.push_back( static_cast< char8_t >( 'A' + ( i % 26 ) ) );
241+
}
242+
return text;
243+
}
244+
245+
inline std::u8string make_cjk_string( size_t length, char32_t start = 0x4E00 )
246+
{
247+
std::u8string text;
248+
for ( size_t i = 0; i < length; i++ ) {
249+
append_utf8( text, start + static_cast< char32_t >( i ) );
250+
}
251+
return text;
252+
}
253+
254+
inline bool any_non_zero( const std::vector< uint8_t >& values )
255+
{
256+
return std::any_of( values.begin(), values.end(), []( uint8_t value ) { return value != 0; } );
257+
}
258+
} // namespace vefc_test

0 commit comments

Comments
 (0)