Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
fmt
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
fmt
Commits
3f4984fb
Commit
3f4984fb
authored
Sep 19, 2018
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Clean core-test and fix linkage errors on older gcc
parent
d4366505
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
635 additions
and
606 deletions
+635
-606
include/fmt/core.h
include/fmt/core.h
+48
-0
include/fmt/format.h
include/fmt/format.h
+0
-48
src/format.cc
src/format.cc
+17
-10
test/CMakeLists.txt
test/CMakeLists.txt
+2
-2
test/core-test.cc
test/core-test.cc
+94
-527
test/format-test.cc
test/format-test.cc
+455
-0
test/mock-allocator.h
test/mock-allocator.h
+11
-10
test/test-assert.h
test/test-assert.h
+8
-9
No files found.
include/fmt/core.h
View file @
3f4984fb
...
...
@@ -755,6 +755,54 @@ class basic_format_arg {
bool
is_arithmetic
()
const
{
return
internal
::
is_arithmetic
(
type_
);
}
};
struct
monostate
{};
/**
\rst
Visits an argument dispatching to the appropriate visit method based on
the argument type. For example, if the argument type is ``double`` then
``vis(value)`` will be called with the value of type ``double``.
\endrst
*/
template
<
typename
Visitor
,
typename
Context
>
FMT_CONSTEXPR
typename
internal
::
result_of
<
Visitor
(
int
)
>::
type
visit
(
Visitor
&&
vis
,
const
basic_format_arg
<
Context
>
&
arg
)
{
typedef
typename
Context
::
char_type
char_type
;
switch
(
arg
.
type_
)
{
case
internal
:
:
none_type
:
break
;
case
internal
:
:
named_arg_type
:
FMT_ASSERT
(
false
,
"invalid argument type"
);
break
;
case
internal
:
:
int_type
:
return
vis
(
arg
.
value_
.
int_value
);
case
internal
:
:
uint_type
:
return
vis
(
arg
.
value_
.
uint_value
);
case
internal
:
:
long_long_type
:
return
vis
(
arg
.
value_
.
long_long_value
);
case
internal
:
:
ulong_long_type
:
return
vis
(
arg
.
value_
.
ulong_long_value
);
case
internal
:
:
bool_type
:
return
vis
(
arg
.
value_
.
int_value
!=
0
);
case
internal
:
:
char_type
:
return
vis
(
static_cast
<
char_type
>
(
arg
.
value_
.
int_value
));
case
internal
:
:
double_type
:
return
vis
(
arg
.
value_
.
double_value
);
case
internal
:
:
long_double_type
:
return
vis
(
arg
.
value_
.
long_double_value
);
case
internal
:
:
cstring_type
:
return
vis
(
arg
.
value_
.
string
.
value
);
case
internal
:
:
string_type
:
return
vis
(
basic_string_view
<
char_type
>
(
arg
.
value_
.
string
.
value
,
arg
.
value_
.
string
.
size
));
case
internal
:
:
pointer_type
:
return
vis
(
arg
.
value_
.
pointer
);
case
internal
:
:
custom_type
:
return
vis
(
typename
basic_format_arg
<
Context
>::
handle
(
arg
.
value_
.
custom
));
}
return
vis
(
monostate
());
}
// Parsing context consisting of a format string range being parsed and an
// argument counter for automatic indexing.
template
<
typename
Char
,
typename
ErrorHandler
=
internal
::
error_handler
>
...
...
include/fmt/format.h
View file @
3f4984fb
...
...
@@ -1154,54 +1154,6 @@ template <typename T = void>
struct
null
{};
}
// namespace internal
struct
monostate
{};
/**
\rst
Visits an argument dispatching to the appropriate visit method based on
the argument type. For example, if the argument type is ``double`` then
``vis(value)`` will be called with the value of type ``double``.
\endrst
*/
template
<
typename
Visitor
,
typename
Context
>
FMT_CONSTEXPR
typename
internal
::
result_of
<
Visitor
(
int
)
>::
type
visit
(
Visitor
&&
vis
,
const
basic_format_arg
<
Context
>
&
arg
)
{
typedef
typename
Context
::
char_type
char_type
;
switch
(
arg
.
type_
)
{
case
internal
:
:
none_type
:
break
;
case
internal
:
:
named_arg_type
:
FMT_ASSERT
(
false
,
"invalid argument type"
);
break
;
case
internal
:
:
int_type
:
return
vis
(
arg
.
value_
.
int_value
);
case
internal
:
:
uint_type
:
return
vis
(
arg
.
value_
.
uint_value
);
case
internal
:
:
long_long_type
:
return
vis
(
arg
.
value_
.
long_long_value
);
case
internal
:
:
ulong_long_type
:
return
vis
(
arg
.
value_
.
ulong_long_value
);
case
internal
:
:
bool_type
:
return
vis
(
arg
.
value_
.
int_value
!=
0
);
case
internal
:
:
char_type
:
return
vis
(
static_cast
<
char_type
>
(
arg
.
value_
.
int_value
));
case
internal
:
:
double_type
:
return
vis
(
arg
.
value_
.
double_value
);
case
internal
:
:
long_double_type
:
return
vis
(
arg
.
value_
.
long_double_value
);
case
internal
:
:
cstring_type
:
return
vis
(
arg
.
value_
.
string
.
value
);
case
internal
:
:
string_type
:
return
vis
(
basic_string_view
<
char_type
>
(
arg
.
value_
.
string
.
value
,
arg
.
value_
.
string
.
size
));
case
internal
:
:
pointer_type
:
return
vis
(
arg
.
value_
.
pointer
);
case
internal
:
:
custom_type
:
return
vis
(
typename
basic_format_arg
<
Context
>::
handle
(
arg
.
value_
.
custom
));
}
return
vis
(
monostate
());
}
enum
alignment
{
ALIGN_DEFAULT
,
ALIGN_LEFT
,
ALIGN_RIGHT
,
ALIGN_CENTER
,
ALIGN_NUMERIC
};
...
...
src/format.cc
View file @
3f4984fb
...
...
@@ -14,33 +14,40 @@ template struct internal::basic_data<void>;
template
FMT_API
char
internal
::
thousands_sep
(
locale_provider
*
lp
);
template
void
internal
::
basic_buffer
<
char
>
::
append
(
const
char
*
,
const
char
*
);
template
void
basic_fixed_buffer
<
char
>
::
grow
(
std
::
size_t
);
template
void
internal
::
arg_map
<
format_context
>
::
init
(
const
basic_format_args
<
format_context
>
&
args
);
template
FMT_API
int
internal
::
char_traits
<
char
>
::
format_float
(
char
*
buffer
,
std
::
size_t
size
,
const
char
*
format
,
int
precision
,
double
value
);
char
*
,
std
::
size_t
,
const
char
*
,
int
,
double
);
template
FMT_API
int
internal
::
char_traits
<
char
>
::
format_float
(
char
*
buffer
,
std
::
size_t
size
,
const
char
*
format
,
int
precision
,
long
double
value
);
char
*
,
std
::
size_t
,
const
char
*
,
int
,
long
double
);
template
FMT_API
std
::
string
internal
::
vformat
<
char
>(
string_view
,
basic_format_args
<
format_context
>
);
// Explicit instantiations for wchar_t.
template
FMT_API
wchar_t
internal
::
thousands_sep
(
locale_provider
*
lp
);
template
FMT_API
wchar_t
internal
::
thousands_sep
(
locale_provider
*
);
template
void
internal
::
basic_buffer
<
wchar_t
>
::
append
(
const
wchar_t
*
,
const
wchar_t
*
);
template
void
basic_fixed_buffer
<
wchar_t
>
::
grow
(
std
::
size_t
);
template
void
internal
::
arg_map
<
wformat_context
>
::
init
(
const
basic_format_args
<
wformat_context
>
&
args
);
const
basic_format_args
<
wformat_context
>
&
);
template
FMT_API
int
internal
::
char_traits
<
wchar_t
>
::
format_float
(
wchar_t
*
buffer
,
std
::
size_t
size
,
const
wchar_t
*
format
,
int
precision
,
double
value
);
wchar_t
*
,
std
::
size_t
,
const
wchar_t
*
,
int
,
double
);
template
FMT_API
int
internal
::
char_traits
<
wchar_t
>
::
format_float
(
wchar_t
*
buffer
,
std
::
size_t
size
,
const
wchar_t
*
format
,
int
precision
,
long
double
value
);
wchar_t
*
,
std
::
size_t
,
const
wchar_t
*
,
int
,
long
double
);
template
FMT_API
std
::
wstring
internal
::
vformat
<
wchar_t
>(
wstring_view
,
basic_format_args
<
wformat_context
>
);
FMT_END_NAMESPACE
test/CMakeLists.txt
View file @
3f4984fb
...
...
@@ -85,9 +85,9 @@ function(add_fmt_test name)
endfunction
()
add_fmt_test
(
assert-test
)
add_fmt_test
(
core-test
mock-allocator.h
)
add_fmt_test
(
core-test
)
add_fmt_test
(
gtest-extra-test
)
add_fmt_test
(
format-test
)
add_fmt_test
(
format-test
mock-allocator.h
)
add_fmt_test
(
format-impl-test
)
add_fmt_test
(
ostream-test
)
add_fmt_test
(
printf-test
)
...
...
test/core-test.cc
View file @
3f4984fb
// Formatting library for C++ -
utility
tests
// Formatting library for C++ -
core
tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "test-assert.h"
#include <cfloat>
#include <algorithm>
#include <climits>
#include <cstring>
#include <functional>
#include <iterator>
#include <limits>
#include <string>
#include <type_traits>
#if FMT_USE_TYPE_TRAITS
# include <type_traits>
#endif
#include "test-assert.h"
#include "gmock.h"
#include "gtest-extra.h"
#include "mock-allocator.h"
#include "util.h"
// Check if f
ormat.h compiles with windows.h included
.
// Check if f
mt/core.h compiles with windows.h included before it
.
#ifdef _WIN32
# include <windows.h>
#endif
...
...
@@ -34,17 +30,15 @@
using
fmt
::
basic_format_arg
;
using
fmt
::
internal
::
basic_buffer
;
using
fmt
::
basic_memory_buffer
;
using
fmt
::
string_view
;
using
fmt
::
internal
::
value
;
using
fmt
::
string_view
;
using
testing
::
_
;
using
testing
::
Return
;
using
testing
::
StrictMock
;
namespace
{
struct
Tes
t
{};
struct
test_struc
t
{};
template
<
typename
Context
,
typename
T
>
basic_format_arg
<
Context
>
make_arg
(
const
T
&
value
)
{
...
...
@@ -54,7 +48,7 @@ basic_format_arg<Context> make_arg(const T &value) {
FMT_BEGIN_NAMESPACE
template
<
typename
Char
>
struct
formatter
<
Tes
t
,
Char
>
{
struct
formatter
<
test_struc
t
,
Char
>
{
template
<
typename
ParseContext
>
auto
parse
(
ParseContext
&
ctx
)
->
decltype
(
ctx
.
begin
())
{
return
ctx
.
begin
();
...
...
@@ -62,7 +56,7 @@ struct formatter<Test, Char> {
typedef
std
::
back_insert_iterator
<
basic_buffer
<
Char
>>
iterator
;
auto
format
(
Tes
t
,
basic_format_context
<
iterator
,
char
>
&
ctx
)
auto
format
(
test_struc
t
,
basic_format_context
<
iterator
,
char
>
&
ctx
)
->
decltype
(
ctx
.
out
())
{
const
Char
*
test
=
"test"
;
return
std
::
copy_n
(
test
,
std
::
strlen
(
test
),
ctx
.
out
());
...
...
@@ -70,52 +64,30 @@ struct formatter<Test, Char> {
};
FMT_END_NAMESPACE
static
void
CheckForwarding
(
MockAllocator
<
int
>
&
alloc
,
AllocatorRef
<
MockAllocator
<
int
>>
&
ref
)
{
int
mem
;
// Check if value_type is properly defined.
AllocatorRef
<
MockAllocator
<
int
>
>::
value_type
*
ptr
=
&
mem
;
// Check forwarding.
EXPECT_CALL
(
alloc
,
allocate
(
42
)).
WillOnce
(
Return
(
ptr
));
ref
.
allocate
(
42
);
EXPECT_CALL
(
alloc
,
deallocate
(
ptr
,
42
));
ref
.
deallocate
(
ptr
,
42
);
}
TEST
(
AllocatorTest
,
AllocatorRef
)
{
StrictMock
<
MockAllocator
<
int
>
>
alloc
;
typedef
AllocatorRef
<
MockAllocator
<
int
>
>
TestAllocatorRef
;
TestAllocatorRef
ref
(
&
alloc
);
// Check if AllocatorRef forwards to the underlying allocator.
CheckForwarding
(
alloc
,
ref
);
TestAllocatorRef
ref2
(
ref
);
CheckForwarding
(
alloc
,
ref2
);
TestAllocatorRef
ref3
;
EXPECT_EQ
(
nullptr
,
ref3
.
get
());
ref3
=
ref
;
CheckForwarding
(
alloc
,
ref3
);
}
#if FMT_USE_TYPE_TRAITS
TEST
(
BufferTest
,
Noncopyable
)
{
EXPECT_FALSE
(
std
::
is_copy_constructible
<
basic_buffer
<
char
>
>::
value
);
#if !FMT_MSC_VER
// std::is_copy_assignable is broken in MSVC2013.
EXPECT_FALSE
(
std
::
is_copy_assignable
<
basic_buffer
<
char
>
>::
value
);
#endif
}
TEST
(
BufferTest
,
Nonmoveable
)
{
EXPECT_FALSE
(
std
::
is_move_constructible
<
basic_buffer
<
char
>
>::
value
);
#if !FMT_MSC_VER
// std::is_move_assignable is broken in MSVC2013.
EXPECT_FALSE
(
std
::
is_move_assignable
<
basic_buffer
<
char
>
>::
value
);
}
#endif
}
// A test buffer with a dummy grow method.
template
<
typename
T
>
struct
TestB
uffer
:
basic_buffer
<
T
>
{
struct
test_b
uffer
:
basic_buffer
<
T
>
{
void
grow
(
std
::
size_t
capacity
)
{
this
->
set
(
nullptr
,
capacity
);
}
};
template
<
typename
T
>
struct
MockB
uffer
:
basic_buffer
<
T
>
{
struct
mock_b
uffer
:
basic_buffer
<
T
>
{
MOCK_METHOD1
(
do_grow
,
void
(
std
::
size_t
capacity
));
void
grow
(
std
::
size_t
capacity
)
{
...
...
@@ -123,21 +95,21 @@ struct MockBuffer : basic_buffer<T> {
do_grow
(
capacity
);
}
MockB
uffer
()
{}
MockB
uffer
(
T
*
data
)
{
this
->
set
(
data
,
0
);
}
MockB
uffer
(
T
*
data
,
std
::
size_t
capacity
)
{
this
->
set
(
data
,
capacity
);
}
mock_b
uffer
()
{}
mock_b
uffer
(
T
*
data
)
{
this
->
set
(
data
,
0
);
}
mock_b
uffer
(
T
*
data
,
std
::
size_t
capacity
)
{
this
->
set
(
data
,
capacity
);
}
};
TEST
(
BufferTest
,
Ctor
)
{
{
MockB
uffer
<
int
>
buffer
;
mock_b
uffer
<
int
>
buffer
;
EXPECT_EQ
(
nullptr
,
&
buffer
[
0
]);
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
capacity
());
}
{
int
dummy
;
MockB
uffer
<
int
>
buffer
(
&
dummy
);
mock_b
uffer
<
int
>
buffer
(
&
dummy
);
EXPECT_EQ
(
&
dummy
,
&
buffer
[
0
]);
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
capacity
());
...
...
@@ -145,21 +117,21 @@ TEST(BufferTest, Ctor) {
{
int
dummy
;
std
::
size_t
capacity
=
std
::
numeric_limits
<
std
::
size_t
>::
max
();
MockB
uffer
<
int
>
buffer
(
&
dummy
,
capacity
);
mock_b
uffer
<
int
>
buffer
(
&
dummy
,
capacity
);
EXPECT_EQ
(
&
dummy
,
&
buffer
[
0
]);
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
EXPECT_EQ
(
capacity
,
buffer
.
capacity
());
}
}
struct
DyingBuffer
:
TestB
uffer
<
int
>
{
struct
dying_buffer
:
test_b
uffer
<
int
>
{
MOCK_METHOD0
(
die
,
void
());
~
DyingB
uffer
()
{
die
();
}
~
dying_b
uffer
()
{
die
();
}
};
TEST
(
BufferTest
,
VirtualDtor
)
{
typedef
StrictMock
<
DyingBuffer
>
StictMockB
uffer
;
StictMockBuffer
*
mock_buffer
=
new
StictMockB
uffer
();
typedef
StrictMock
<
dying_buffer
>
stict_mock_b
uffer
;
stict_mock_buffer
*
mock_buffer
=
new
stict_mock_b
uffer
();
EXPECT_CALL
(
*
mock_buffer
,
die
());
basic_buffer
<
int
>
*
buffer
=
mock_buffer
;
delete
buffer
;
...
...
@@ -167,7 +139,7 @@ TEST(BufferTest, VirtualDtor) {
TEST
(
BufferTest
,
Access
)
{
char
data
[
10
];
MockB
uffer
<
char
>
buffer
(
data
,
sizeof
(
data
));
mock_b
uffer
<
char
>
buffer
(
data
,
sizeof
(
data
));
buffer
[
0
]
=
11
;
EXPECT_EQ
(
11
,
buffer
[
0
]);
buffer
[
3
]
=
42
;
...
...
@@ -178,7 +150,7 @@ TEST(BufferTest, Access) {
TEST
(
BufferTest
,
Resize
)
{
char
data
[
123
];
MockB
uffer
<
char
>
buffer
(
data
,
sizeof
(
data
));
mock_b
uffer
<
char
>
buffer
(
data
,
sizeof
(
data
));
buffer
[
10
]
=
42
;
EXPECT_EQ
(
42
,
buffer
[
10
]);
buffer
.
resize
(
20
);
...
...
@@ -197,7 +169,7 @@ TEST(BufferTest, Resize) {
}
TEST
(
BufferTest
,
Clear
)
{
TestB
uffer
<
char
>
buffer
;
test_b
uffer
<
char
>
buffer
;
buffer
.
resize
(
20
);
buffer
.
resize
(
0
);
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
...
...
@@ -206,7 +178,7 @@ TEST(BufferTest, Clear) {
TEST
(
BufferTest
,
Append
)
{
char
data
[
15
];
MockB
uffer
<
char
>
buffer
(
data
,
10
);
mock_b
uffer
<
char
>
buffer
(
data
,
10
);
const
char
*
test
=
"test"
;
buffer
.
append
(
test
,
test
+
5
);
EXPECT_STREQ
(
test
,
&
buffer
[
0
]);
...
...
@@ -221,202 +193,14 @@ TEST(BufferTest, Append) {
TEST
(
BufferTest
,
AppendAllocatesEnoughStorage
)
{
char
data
[
19
];
MockB
uffer
<
char
>
buffer
(
data
,
10
);
mock_b
uffer
<
char
>
buffer
(
data
,
10
);
const
char
*
test
=
"abcdefgh"
;
buffer
.
resize
(
10
);
EXPECT_CALL
(
buffer
,
do_grow
(
19
));
buffer
.
append
(
test
,
test
+
9
);
}
TEST
(
MemoryBufferTest
,
Ctor
)
{
basic_memory_buffer
<
char
,
123
>
buffer
;
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
EXPECT_EQ
(
123u
,
buffer
.
capacity
());
}
typedef
AllocatorRef
<
std
::
allocator
<
char
>
>
TestAllocator
;
static
void
check_move_buffer
(
const
char
*
str
,
basic_memory_buffer
<
char
,
5
,
TestAllocator
>
&
buffer
)
{
std
::
allocator
<
char
>
*
alloc
=
buffer
.
get_allocator
().
get
();
basic_memory_buffer
<
char
,
5
,
TestAllocator
>
buffer2
(
std
::
move
(
buffer
));
// Move shouldn't destroy the inline content of the first buffer.
EXPECT_EQ
(
str
,
std
::
string
(
&
buffer
[
0
],
buffer
.
size
()));
EXPECT_EQ
(
str
,
std
::
string
(
&
buffer2
[
0
],
buffer2
.
size
()));
EXPECT_EQ
(
5u
,
buffer2
.
capacity
());
// Move should transfer allocator.
EXPECT_EQ
(
nullptr
,
buffer
.
get_allocator
().
get
());
EXPECT_EQ
(
alloc
,
buffer2
.
get_allocator
().
get
());
}
TEST
(
MemoryBufferTest
,
MoveCtor
)
{
std
::
allocator
<
char
>
alloc
;
basic_memory_buffer
<
char
,
5
,
TestAllocator
>
buffer
((
TestAllocator
(
&
alloc
)));
const
char
test
[]
=
"test"
;
buffer
.
append
(
test
,
test
+
4
);
check_move_buffer
(
"test"
,
buffer
);
// Adding one more character fills the inline buffer, but doesn't cause
// dynamic allocation.
buffer
.
push_back
(
'a'
);
check_move_buffer
(
"testa"
,
buffer
);
const
char
*
inline_buffer_ptr
=
&
buffer
[
0
];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
buffer
.
push_back
(
'b'
);
basic_memory_buffer
<
char
,
5
,
TestAllocator
>
buffer2
(
std
::
move
(
buffer
));
// Move should rip the guts of the first buffer.
EXPECT_EQ
(
inline_buffer_ptr
,
&
buffer
[
0
]);
EXPECT_EQ
(
"testab"
,
std
::
string
(
&
buffer2
[
0
],
buffer2
.
size
()));
EXPECT_GT
(
buffer2
.
capacity
(),
5u
);
}
static
void
check_move_assign_buffer
(
const
char
*
str
,
basic_memory_buffer
<
char
,
5
>
&
buffer
)
{
basic_memory_buffer
<
char
,
5
>
buffer2
;
buffer2
=
std
::
move
(
buffer
);
// Move shouldn't destroy the inline content of the first buffer.
EXPECT_EQ
(
str
,
std
::
string
(
&
buffer
[
0
],
buffer
.
size
()));
EXPECT_EQ
(
str
,
std
::
string
(
&
buffer2
[
0
],
buffer2
.
size
()));
EXPECT_EQ
(
5u
,
buffer2
.
capacity
());
}
TEST
(
MemoryBufferTest
,
MoveAssignment
)
{
basic_memory_buffer
<
char
,
5
>
buffer
;
const
char
test
[]
=
"test"
;
buffer
.
append
(
test
,
test
+
4
);
check_move_assign_buffer
(
"test"
,
buffer
);
// Adding one more character fills the inline buffer, but doesn't cause
// dynamic allocation.
buffer
.
push_back
(
'a'
);
check_move_assign_buffer
(
"testa"
,
buffer
);
const
char
*
inline_buffer_ptr
=
&
buffer
[
0
];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
buffer
.
push_back
(
'b'
);
basic_memory_buffer
<
char
,
5
>
buffer2
;
buffer2
=
std
::
move
(
buffer
);
// Move should rip the guts of the first buffer.
EXPECT_EQ
(
inline_buffer_ptr
,
&
buffer
[
0
]);
EXPECT_EQ
(
"testab"
,
std
::
string
(
&
buffer2
[
0
],
buffer2
.
size
()));
EXPECT_GT
(
buffer2
.
capacity
(),
5u
);
}
TEST
(
MemoryBufferTest
,
Grow
)
{
typedef
AllocatorRef
<
MockAllocator
<
int
>
>
Allocator
;
typedef
basic_memory_buffer
<
int
,
10
,
Allocator
>
Base
;
MockAllocator
<
int
>
alloc
;
struct
TestMemoryBuffer
:
Base
{
TestMemoryBuffer
(
Allocator
alloc
)
:
Base
(
alloc
)
{}
void
grow
(
std
::
size_t
size
)
{
Base
::
grow
(
size
);
}
}
buffer
((
Allocator
(
&
alloc
)));
buffer
.
resize
(
7
);
using
fmt
::
internal
::
to_unsigned
;
for
(
int
i
=
0
;
i
<
7
;
++
i
)
buffer
[
to_unsigned
(
i
)]
=
i
*
i
;
EXPECT_EQ
(
10u
,
buffer
.
capacity
());
int
mem
[
20
];
mem
[
7
]
=
0xdead
;
EXPECT_CALL
(
alloc
,
allocate
(
20
)).
WillOnce
(
Return
(
mem
));
buffer
.
grow
(
20
);
EXPECT_EQ
(
20u
,
buffer
.
capacity
());
// Check if size elements have been copied
for
(
int
i
=
0
;
i
<
7
;
++
i
)
EXPECT_EQ
(
i
*
i
,
buffer
[
to_unsigned
(
i
)]);
// and no more than that.
EXPECT_EQ
(
0xdead
,
buffer
[
7
]);
EXPECT_CALL
(
alloc
,
deallocate
(
mem
,
20
));
}
TEST
(
MemoryBufferTest
,
Allocator
)
{
typedef
AllocatorRef
<
MockAllocator
<
char
>
>
TestAllocator
;
basic_memory_buffer
<
char
,
10
,
TestAllocator
>
buffer
;
EXPECT_EQ
(
nullptr
,
buffer
.
get_allocator
().
get
());
StrictMock
<
MockAllocator
<
char
>
>
alloc
;
char
mem
;
{
basic_memory_buffer
<
char
,
10
,
TestAllocator
>
buffer2
((
TestAllocator
(
&
alloc
)));
EXPECT_EQ
(
&
alloc
,
buffer2
.
get_allocator
().
get
());
std
::
size_t
size
=
2
*
fmt
::
inline_buffer_size
;
EXPECT_CALL
(
alloc
,
allocate
(
size
)).
WillOnce
(
Return
(
&
mem
));
buffer2
.
reserve
(
size
);
EXPECT_CALL
(
alloc
,
deallocate
(
&
mem
,
size
));
}
}
TEST
(
MemoryBufferTest
,
ExceptionInDeallocate
)
{
typedef
AllocatorRef
<
MockAllocator
<
char
>
>
TestAllocator
;
StrictMock
<
MockAllocator
<
char
>
>
alloc
;
basic_memory_buffer
<
char
,
10
,
TestAllocator
>
buffer
((
TestAllocator
(
&
alloc
)));
std
::
size_t
size
=
2
*
fmt
::
inline_buffer_size
;
std
::
vector
<
char
>
mem
(
size
);
{
EXPECT_CALL
(
alloc
,
allocate
(
size
)).
WillOnce
(
Return
(
&
mem
[
0
]));
buffer
.
resize
(
size
);
std
::
fill
(
&
buffer
[
0
],
&
buffer
[
0
]
+
size
,
'x'
);
}
std
::
vector
<
char
>
mem2
(
2
*
size
);
{
EXPECT_CALL
(
alloc
,
allocate
(
2
*
size
)).
WillOnce
(
Return
(
&
mem2
[
0
]));
std
::
exception
e
;
EXPECT_CALL
(
alloc
,
deallocate
(
&
mem
[
0
],
size
)).
WillOnce
(
testing
::
Throw
(
e
));
EXPECT_THROW
(
buffer
.
reserve
(
2
*
size
),
std
::
exception
);
EXPECT_EQ
(
&
mem2
[
0
],
&
buffer
[
0
]);
// Check that the data has been copied.
for
(
std
::
size_t
i
=
0
;
i
<
size
;
++
i
)
EXPECT_EQ
(
'x'
,
buffer
[
i
]);
}
EXPECT_CALL
(
alloc
,
deallocate
(
&
mem2
[
0
],
2
*
size
));
}
TEST
(
FixedBufferTest
,
Ctor
)
{
char
array
[
10
]
=
"garbage"
;
fmt
::
basic_fixed_buffer
<
char
>
buffer
(
array
,
sizeof
(
array
));
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
EXPECT_EQ
(
10u
,
buffer
.
capacity
());
EXPECT_EQ
(
array
,
buffer
.
data
());
}
TEST
(
FixedBufferTest
,
CompileTimeSizeCtor
)
{
char
array
[
10
]
=
"garbage"
;
fmt
::
basic_fixed_buffer
<
char
>
buffer
(
array
);
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
EXPECT_EQ
(
10u
,
buffer
.
capacity
());
EXPECT_EQ
(
array
,
buffer
.
data
());
}
TEST
(
FixedBufferTest
,
BufferOverflow
)
{
char
array
[
10
];
fmt
::
basic_fixed_buffer
<
char
>
buffer
(
array
);
buffer
.
resize
(
10
);
EXPECT_THROW_MSG
(
buffer
.
resize
(
11
),
std
::
runtime_error
,
"buffer overflow"
);
}
struct
uint32_pair
{
uint32_t
u
[
2
];
};
TEST
(
UtilTest
,
BitCast
)
{
auto
s
=
fmt
::
internal
::
bit_cast
<
uint32_pair
>
(
uint64_t
{
42
});
EXPECT_EQ
(
fmt
::
internal
::
bit_cast
<
uint64_t
>
(
s
),
42ull
);
s
=
fmt
::
internal
::
bit_cast
<
uint32_pair
>
(
uint64_t
(
~
0ull
));
EXPECT_EQ
(
fmt
::
internal
::
bit_cast
<
uint64_t
>
(
s
),
~
0ull
);
}
TEST
(
UtilTest
,
Increment
)
{
char
s
[
10
]
=
"123"
;
increment
(
s
);
EXPECT_STREQ
(
"124"
,
s
);
s
[
2
]
=
'8'
;
increment
(
s
);
EXPECT_STREQ
(
"129"
,
s
);
increment
(
s
);
EXPECT_STREQ
(
"130"
,
s
);
s
[
1
]
=
s
[
2
]
=
'9'
;
increment
(
s
);
EXPECT_STREQ
(
"200"
,
s
);
}
TEST
(
UtilTest
,
FormatArgs
)
{
TEST
(
ArgTest
,
FormatArgs
)
{
fmt
::
format_args
args
;
EXPECT_FALSE
(
args
.
get
(
1
));
}
...
...
@@ -445,8 +229,8 @@ struct custom_context {
void
advance_to
(
const
char
*
)
{}
};
TEST
(
UtilTest
,
MakeValueWithCustomFormatter
)
{
::
Tes
t
t
;
TEST
(
ArgTest
,
MakeValueWithCustomContext
)
{
test_struc
t
t
;
fmt
::
internal
::
value
<
custom_context
>
arg
=
fmt
::
internal
::
make_value
<
custom_context
>
(
t
);
custom_context
ctx
=
{
false
};
...
...
@@ -456,7 +240,6 @@ TEST(UtilTest, MakeValueWithCustomFormatter) {
FMT_BEGIN_NAMESPACE
namespace
internal
{
template
<
typename
Char
>
bool
operator
==
(
custom_value
<
Char
>
lhs
,
custom_value
<
Char
>
rhs
)
{
return
lhs
.
value
==
rhs
.
value
;
...
...
@@ -466,32 +249,35 @@ FMT_END_NAMESPACE
// Use a unique result type to make sure that there are no undesirable
// conversions.
struct
R
esult
{};
struct
test_r
esult
{};
template
<
typename
T
>
struct
MockVisitor
:
fmt
::
internal
::
function
<
Result
>
{
MockVisitor
()
{
ON_CALL
(
*
this
,
visit
(
_
)).
WillByDefault
(
Return
(
Result
()));
struct
mock_visitor
{
template
<
typename
U
>
struct
result
{
typedef
test_result
type
;
};
mock_visitor
()
{
ON_CALL
(
*
this
,
visit
(
_
)).
WillByDefault
(
testing
::
Return
(
test_result
()));
}
MOCK_METHOD1_T
(
visit
,
R
esult
(
T
value
));
MOCK_METHOD1_T
(
visit
,
test_r
esult
(
T
value
));
MOCK_METHOD0_T
(
unexpected
,
void
());
R
esult
operator
()(
T
value
)
{
return
visit
(
value
);
}
test_r
esult
operator
()(
T
value
)
{
return
visit
(
value
);
}
template
<
typename
U
>
R
esult
operator
()(
U
)
{
test_r
esult
operator
()(
U
)
{
unexpected
();
return
R
esult
();
return
test_r
esult
();
}
};
template
<
typename
T
>
struct
VisitT
ype
{
typedef
T
Type
;
};
struct
visit_t
ype
{
typedef
T
Type
;
};
#define VISIT_TYPE(Type_,
VisitT
ype_) \
#define VISIT_TYPE(Type_,
visit_t
ype_) \
template <> \
struct
VisitType<Type_> { typedef VisitT
ype_ Type; }
struct
visit_type<Type_> { typedef visit_t
ype_ Type; }
VISIT_TYPE
(
signed
char
,
int
);
VISIT_TYPE
(
unsigned
char
,
unsigned
);
...
...
@@ -509,7 +295,7 @@ VISIT_TYPE(unsigned long, unsigned long long);
VISIT_TYPE
(
float
,
double
);
#define CHECK_ARG_(Char, expected, value) { \
testing::StrictMock<
MockV
isitor<decltype(expected)>> visitor; \
testing::StrictMock<
mock_v
isitor<decltype(expected)>> visitor; \
EXPECT_CALL(visitor, visit(expected)); \
typedef std::back_insert_iterator<basic_buffer<Char>> iterator; \
fmt::visit(visitor, \
...
...
@@ -518,7 +304,7 @@ VISIT_TYPE(float, double);
#define CHECK_ARG(value, typename_) { \
typedef decltype(value) value_type; \
typename_
VisitT
ype<value_type>::Type expected = value; \
typename_
visit_t
ype<value_type>::Type expected = value; \
CHECK_ARG_(char, expected, value) \
CHECK_ARG_(wchar_t, expected, value) \
}
...
...
@@ -549,13 +335,13 @@ TYPED_TEST(NumericArgTest, MakeAndVisit) {
CHECK_ARG
(
std
::
numeric_limits
<
TypeParam
>::
max
(),
typename
);
}
TEST
(
Util
Test
,
CharArg
)
{
TEST
(
Arg
Test
,
CharArg
)
{
CHECK_ARG_
(
char
,
'a'
,
'a'
);
CHECK_ARG_
(
wchar_t
,
L'a'
,
'a'
);
CHECK_ARG_
(
wchar_t
,
L'a'
,
L'a'
);
}
TEST
(
Util
Test
,
StringArg
)
{
TEST
(
Arg
Test
,
StringArg
)
{
char
str_data
[]
=
"test"
;
char
*
str
=
str_data
;
const
char
*
cstr
=
str
;
...
...
@@ -565,7 +351,7 @@ TEST(UtilTest, StringArg) {
CHECK_ARG_
(
char
,
sref
,
std
::
string
(
str
));
}
TEST
(
Util
Test
,
WStringArg
)
{
TEST
(
Arg
Test
,
WStringArg
)
{
wchar_t
str_data
[]
=
L"test"
;
wchar_t
*
str
=
str_data
;
const
wchar_t
*
cstr
=
str
;
...
...
@@ -577,7 +363,7 @@ TEST(UtilTest, WStringArg) {
CHECK_ARG_
(
wchar_t
,
sref
,
fmt
::
wstring_view
(
str
));
}
TEST
(
Util
Test
,
PointerArg
)
{
TEST
(
Arg
Test
,
PointerArg
)
{
void
*
p
=
nullptr
;
const
void
*
cp
=
nullptr
;
CHECK_ARG_
(
char
,
cp
,
p
);
...
...
@@ -586,56 +372,48 @@ TEST(UtilTest, PointerArg) {
}
struct
check_custom
{
Result
operator
()(
fmt
::
basic_format_arg
<
fmt
::
format_context
>::
handle
h
)
const
{
fmt
::
memory_buffer
buffer
;
test_result
operator
()(
fmt
::
basic_format_arg
<
fmt
::
format_context
>::
handle
h
)
const
{
struct
test_buffer
:
fmt
::
internal
::
basic_buffer
<
char
>
{
char
data
[
10
];
test_buffer
()
:
basic_buffer
(
data
,
0
,
10
)
{}
void
grow
(
std
::
size_t
)
{}
}
buffer
;
fmt
::
internal
::
basic_buffer
<
char
>
&
base
=
buffer
;
fmt
::
format_context
ctx
(
std
::
back_inserter
(
base
),
""
,
fmt
::
format_args
());
h
.
format
(
ctx
);
EXPECT_EQ
(
"test"
,
std
::
string
(
buffer
.
data
()
,
buffer
.
size
()));
return
R
esult
();
EXPECT_EQ
(
"test"
,
std
::
string
(
buffer
.
data
,
buffer
.
size
()));
return
test_r
esult
();
}
};
TEST
(
Util
Test
,
CustomArg
)
{
::
Tes
t
test
;
typedef
MockV
isitor
<
fmt
::
basic_format_arg
<
fmt
::
format_context
>::
handle
>
TEST
(
Arg
Test
,
CustomArg
)
{
test_struc
t
test
;
typedef
mock_v
isitor
<
fmt
::
basic_format_arg
<
fmt
::
format_context
>::
handle
>
visitor
;
testing
::
StrictMock
<
visitor
>
v
;
EXPECT_CALL
(
v
,
visit
(
_
)).
WillOnce
(
testing
::
Invoke
(
check_custom
()));
fmt
::
visit
(
v
,
make_arg
<
fmt
::
format_context
>
(
test
));
}
TEST
(
Arg
Visitor
Test
,
VisitInvalidArg
)
{
typedef
MockV
isitor
<
fmt
::
monostate
>
Visitor
;
TEST
(
ArgTest
,
VisitInvalidArg
)
{
typedef
mock_v
isitor
<
fmt
::
monostate
>
Visitor
;
testing
::
StrictMock
<
Visitor
>
visitor
;
EXPECT_CALL
(
visitor
,
visit
(
_
));
fmt
::
basic_format_arg
<
fmt
::
format_context
>
arg
;
visit
(
visitor
,
arg
);
}
// Tests fmt::internal::count_digits for integer type Int.
template
<
typename
Int
>
void
test_count_digits
()
{
for
(
Int
i
=
0
;
i
<
10
;
++
i
)
EXPECT_EQ
(
1u
,
fmt
::
internal
::
count_digits
(
i
));
for
(
Int
i
=
1
,
n
=
1
,
end
=
std
::
numeric_limits
<
Int
>::
max
()
/
10
;
n
<=
end
;
++
i
)
{
n
*=
10
;
EXPECT_EQ
(
i
,
fmt
::
internal
::
count_digits
(
n
-
1
));
EXPECT_EQ
(
i
+
1
,
fmt
::
internal
::
count_digits
(
n
));
}
}
TEST
(
UtilTest
,
StringRef
)
{
TEST
(
StringViewTest
,
Length
)
{
// Test that StringRef::size() returns string length, not buffer size.
char
str
[
100
]
=
"some string"
;
EXPECT_EQ
(
std
::
strlen
(
str
),
string_view
(
str
).
size
());
EXPECT_LT
(
std
::
strlen
(
str
),
sizeof
(
str
));
}
// Check
StringRef
's comparison operator.
// Check
string_view
's comparison operator.
template
<
template
<
typename
>
class
Op
>
void
CheckO
p
()
{
void
check_o
p
()
{
const
char
*
inputs
[]
=
{
"foo"
,
"fop"
,
"fo"
};
std
::
size_t
num_inputs
=
sizeof
(
inputs
)
/
sizeof
(
*
inputs
);
for
(
std
::
size_t
i
=
0
;
i
<
num_inputs
;
++
i
)
{
...
...
@@ -646,249 +424,38 @@ void CheckOp() {
}
}
TEST
(
UtilTest
,
StringRef
Compare
)
{
EXPECT_EQ
(
0
,
string_view
(
"foo"
).
compare
(
string_view
(
"foo"
))
);
TEST
(
StringViewTest
,
Compare
)
{
EXPECT_EQ
(
string_view
(
"foo"
).
compare
(
string_view
(
"foo"
)),
0
);
EXPECT_GT
(
string_view
(
"fop"
).
compare
(
string_view
(
"foo"
)),
0
);
EXPECT_LT
(
string_view
(
"foo"
).
compare
(
string_view
(
"fop"
)),
0
);
EXPECT_GT
(
string_view
(
"foo"
).
compare
(
string_view
(
"fo"
)),
0
);
EXPECT_LT
(
string_view
(
"fo"
).
compare
(
string_view
(
"foo"
)),
0
);
CheckOp
<
std
::
equal_to
>
();
CheckOp
<
std
::
not_equal_to
>
();
CheckOp
<
std
::
less
>
();
CheckOp
<
std
::
less_equal
>
();
CheckOp
<
std
::
greater
>
();
CheckOp
<
std
::
greater_equal
>
();
}
TEST
(
UtilTest
,
CountDigits
)
{
test_count_digits
<
uint32_t
>
();
test_count_digits
<
uint64_t
>
();
check_op
<
std
::
equal_to
>
();
check_op
<
std
::
not_equal_to
>
();
check_op
<
std
::
less
>
();
check_op
<
std
::
less_equal
>
();
check_op
<
std
::
greater
>
();
check_op
<
std
::
greater_equal
>
();
}
#ifdef _WIN32
TEST
(
UtilTest
,
UTF16ToUTF8
)
{
std
::
string
s
=
"ёжик"
;
fmt
::
internal
::
utf16_to_utf8
u
(
L"
\x0451\x0436\x0438\x043A
"
);
EXPECT_EQ
(
s
,
u
.
str
());
EXPECT_EQ
(
s
.
size
(),
u
.
size
());
}
TEST
(
UtilTest
,
UTF16ToUTF8EmptyString
)
{
std
::
string
s
=
""
;
fmt
::
internal
::
utf16_to_utf8
u
(
L""
);
EXPECT_EQ
(
s
,
u
.
str
());
EXPECT_EQ
(
s
.
size
(),
u
.
size
());
}
enum
basic_enum
{};
TEST
(
UtilTest
,
UTF8ToUTF16
)
{
std
::
string
s
=
"лошадка"
;
fmt
::
internal
::
utf8_to_utf16
u
(
s
.
c_str
());
EXPECT_EQ
(
L"
\x043B\x043E\x0448\x0430\x0434\x043A\x0430
"
,
u
.
str
());
EXPECT_EQ
(
7
,
u
.
size
());
}
TEST
(
UtilTest
,
UTF8ToUTF16EmptyString
)
{
std
::
string
s
=
""
;
fmt
::
internal
::
utf8_to_utf16
u
(
s
.
c_str
());
EXPECT_EQ
(
L""
,
u
.
str
());
EXPECT_EQ
(
s
.
size
(),
u
.
size
());
}
template
<
typename
Converter
,
typename
Char
>
void
check_utf_conversion_error
(
const
char
*
message
,
fmt
::
basic_string_view
<
Char
>
str
=
fmt
::
basic_string_view
<
Char
>
(
0
,
1
))
{
fmt
::
memory_buffer
out
;
fmt
::
internal
::
format_windows_error
(
out
,
ERROR_INVALID_PARAMETER
,
message
);
fmt
::
system_error
error
(
0
,
""
);
try
{
(
Converter
)(
str
);
}
catch
(
const
fmt
::
system_error
&
e
)
{
error
=
e
;
}
EXPECT_EQ
(
ERROR_INVALID_PARAMETER
,
error
.
error_code
());
EXPECT_EQ
(
fmt
::
to_string
(
out
),
error
.
what
());
}
TEST
(
UtilTest
,
UTF16ToUTF8Error
)
{
check_utf_conversion_error
<
fmt
::
internal
::
utf16_to_utf8
,
wchar_t
>
(
"cannot convert string from UTF-16 to UTF-8"
);
}
TEST
(
UtilTest
,
UTF8ToUTF16Error
)
{
const
char
*
message
=
"cannot convert string from UTF-8 to UTF-16"
;
check_utf_conversion_error
<
fmt
::
internal
::
utf8_to_utf16
,
char
>
(
message
);
check_utf_conversion_error
<
fmt
::
internal
::
utf8_to_utf16
,
char
>
(
message
,
fmt
::
string_view
(
"foo"
,
INT_MAX
+
1u
));
}
TEST
(
UtilTest
,
UTF16ToUTF8Convert
)
{
fmt
::
internal
::
utf16_to_utf8
u
;
EXPECT_EQ
(
ERROR_INVALID_PARAMETER
,
u
.
convert
(
fmt
::
wstring_view
(
0
,
1
)));
EXPECT_EQ
(
ERROR_INVALID_PARAMETER
,
u
.
convert
(
fmt
::
wstring_view
(
L"foo"
,
INT_MAX
+
1u
)));
}
#endif // _WIN32
typedef
void
(
*
FormatErrorMessage
)(
fmt
::
internal
::
buffer
&
out
,
int
error_code
,
string_view
message
);
template
<
typename
Error
>
void
check_throw_error
(
int
error_code
,
FormatErrorMessage
format
)
{
fmt
::
system_error
error
(
0
,
""
);
try
{
throw
Error
(
error_code
,
"test {}"
,
"error"
);
}
catch
(
const
fmt
::
system_error
&
e
)
{
error
=
e
;
}
fmt
::
memory_buffer
message
;
format
(
message
,
error_code
,
"test error"
);
EXPECT_EQ
(
to_string
(
message
),
error
.
what
());
EXPECT_EQ
(
error_code
,
error
.
error_code
());
}
TEST
(
UtilTest
,
FormatSystemError
)
{
fmt
::
memory_buffer
message
;
fmt
::
format_system_error
(
message
,
EDOM
,
"test"
);
EXPECT_EQ
(
fmt
::
format
(
"test: {}"
,
get_system_error
(
EDOM
)),
to_string
(
message
));
message
=
fmt
::
memory_buffer
();
// Check if std::allocator throws on allocating max size_t / 2 chars.
size_t
max_size
=
std
::
numeric_limits
<
size_t
>::
max
()
/
2
;
bool
throws_on_alloc
=
false
;
try
{
std
::
allocator
<
char
>
alloc
;
alloc
.
deallocate
(
alloc
.
allocate
(
max_size
),
max_size
);
}
catch
(
const
std
::
bad_alloc
&
)
{
throws_on_alloc
=
true
;
}
if
(
!
throws_on_alloc
)
{
fmt
::
print
(
"warning: std::allocator allocates {} chars"
,
max_size
);
return
;
}
fmt
::
format_system_error
(
message
,
EDOM
,
fmt
::
string_view
(
nullptr
,
max_size
));
EXPECT_EQ
(
fmt
::
format
(
"error {}"
,
EDOM
),
to_string
(
message
));
}
TEST
(
UtilTest
,
SystemError
)
{
fmt
::
system_error
e
(
EDOM
,
"test"
);
EXPECT_EQ
(
fmt
::
format
(
"test: {}"
,
get_system_error
(
EDOM
)),
e
.
what
());
EXPECT_EQ
(
EDOM
,
e
.
error_code
());
check_throw_error
<
fmt
::
system_error
>
(
EDOM
,
fmt
::
format_system_error
);
}
TEST
(
UtilTest
,
ReportSystemError
)
{
fmt
::
memory_buffer
out
;
fmt
::
format_system_error
(
out
,
EDOM
,
"test error"
);
out
.
push_back
(
'\n'
);
EXPECT_WRITE
(
stderr
,
fmt
::
report_system_error
(
EDOM
,
"test error"
),
to_string
(
out
));
}
#ifdef _WIN32
TEST
(
UtilTest
,
FormatWindowsError
)
{
LPWSTR
message
=
0
;
FormatMessageW
(
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_SYSTEM
|
FORMAT_MESSAGE_IGNORE_INSERTS
,
0
,
ERROR_FILE_EXISTS
,
MAKELANGID
(
LANG_NEUTRAL
,
SUBLANG_DEFAULT
),
reinterpret_cast
<
LPWSTR
>
(
&
message
),
0
,
0
);
fmt
::
internal
::
utf16_to_utf8
utf8_message
(
message
);
LocalFree
(
message
);
fmt
::
memory_buffer
actual_message
;
fmt
::
internal
::
format_windows_error
(
actual_message
,
ERROR_FILE_EXISTS
,
"test"
);
EXPECT_EQ
(
fmt
::
format
(
"test: {}"
,
utf8_message
.
str
()),
fmt
::
to_string
(
actual_message
));
actual_message
.
resize
(
0
);
fmt
::
internal
::
format_windows_error
(
actual_message
,
ERROR_FILE_EXISTS
,
fmt
::
string_view
(
0
,
std
::
numeric_limits
<
size_t
>::
max
()));
EXPECT_EQ
(
fmt
::
format
(
"error {}"
,
ERROR_FILE_EXISTS
),
fmt
::
to_string
(
actual_message
));
}
TEST
(
UtilTest
,
FormatLongWindowsError
)
{
LPWSTR
message
=
0
;
// this error code is not available on all Windows platforms and
// Windows SDKs, so do not fail the test if the error string cannot
// be retrieved.
const
int
provisioning_not_allowed
=
0x80284013L
/*TBS_E_PROVISIONING_NOT_ALLOWED*/
;
if
(
FormatMessageW
(
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_SYSTEM
|
FORMAT_MESSAGE_IGNORE_INSERTS
,
0
,
static_cast
<
DWORD
>
(
provisioning_not_allowed
),
MAKELANGID
(
LANG_NEUTRAL
,
SUBLANG_DEFAULT
),
reinterpret_cast
<
LPWSTR
>
(
&
message
),
0
,
0
)
==
0
)
{
return
;
}
fmt
::
internal
::
utf16_to_utf8
utf8_message
(
message
);
LocalFree
(
message
);
fmt
::
memory_buffer
actual_message
;
fmt
::
internal
::
format_windows_error
(
actual_message
,
provisioning_not_allowed
,
"test"
);
EXPECT_EQ
(
fmt
::
format
(
"test: {}"
,
utf8_message
.
str
()),
fmt
::
to_string
(
actual_message
));
}
TEST
(
UtilTest
,
WindowsError
)
{
check_throw_error
<
fmt
::
windows_error
>
(
ERROR_FILE_EXISTS
,
fmt
::
internal
::
format_windows_error
);
}
TEST
(
UtilTest
,
ReportWindowsError
)
{
fmt
::
memory_buffer
out
;
fmt
::
internal
::
format_windows_error
(
out
,
ERROR_FILE_EXISTS
,
"test error"
);
out
.
push_back
(
'\n'
);
EXPECT_WRITE
(
stderr
,
fmt
::
report_windows_error
(
ERROR_FILE_EXISTS
,
"test error"
),
fmt
::
to_string
(
out
));
}
#endif // _WIN32
enum
TestEnum2
{};
TEST
(
UtilTest
,
ConvertToInt
)
{
TEST
(
CoreTest
,
ConvertToInt
)
{
EXPECT_FALSE
((
fmt
::
convert_to_int
<
char
,
char
>::
value
));
EXPECT_FALSE
((
fmt
::
convert_to_int
<
const
char
*
,
char
>::
value
));
EXPECT_TRUE
((
fmt
::
convert_to_int
<
TestEnum2
,
char
>::
value
));
EXPECT_TRUE
((
fmt
::
convert_to_int
<
basic_enum
,
char
>::
value
));
}
#if FMT_USE_ENUM_BASE
enum
TestEnum
:
char
{
TestValue
};
TEST
(
UtilTest
,
IsEnumConvertibleToInt
)
{
EXPECT_TRUE
((
fmt
::
convert_to_int
<
TestEnum
,
char
>::
value
));
}
#endif
TEST
(
UtilTest
,
ParseNonnegativeInt
)
{
if
(
std
::
numeric_limits
<
int
>::
max
()
!=
static_cast
<
int
>
(
static_cast
<
unsigned
>
(
1
)
<<
31
))
{
fmt
::
print
(
"Skipping parse_nonnegative_int test
\n
"
);
return
;
}
const
char
*
s
=
"10000000000"
;
EXPECT_THROW_MSG
(
parse_nonnegative_int
(
s
,
fmt
::
internal
::
error_handler
()),
fmt
::
format_error
,
"number is too big"
);
s
=
"2147483649"
;
EXPECT_THROW_MSG
(
parse_nonnegative_int
(
s
,
fmt
::
internal
::
error_handler
()),
fmt
::
format_error
,
"number is too big"
);
}
enum
enum_with_underlying_type
:
char
{
TestValue
};
TEST
(
IteratorTest
,
CountingIterator
)
{
fmt
::
internal
::
counting_iterator
<
char
>
it
;
auto
prev
=
it
++
;
EXPECT_EQ
(
prev
.
count
(),
0
);
EXPECT_EQ
(
it
.
count
(),
1
);
TEST
(
CoreTest
,
IsEnumConvertibleToInt
)
{
EXPECT_TRUE
((
fmt
::
convert_to_int
<
enum_with_underlying_type
,
char
>::
value
));
}
TEST
(
IteratorTest
,
TruncatingIterator
)
{
char
*
p
=
FMT_NULL
;
fmt
::
internal
::
truncating_iterator
<
char
*>
it
(
p
,
3
);
auto
prev
=
it
++
;
EXPECT_EQ
(
prev
.
base
(),
p
);
EXPECT_EQ
(
it
.
base
(),
p
+
1
);
TEST
(
CoreTest
,
Format
)
{
// This should work without including fmt/format.h.
#ifdef FMT_FORMAT_H_
# error fmt/format.h must not be included in the core test
#endif
EXPECT_EQ
(
fmt
::
format
(
"{}"
,
42
),
"42"
);
}
test/format-test.cc
View file @
3f4984fb
...
...
@@ -14,17 +14,24 @@
#include <memory>
#include <stdint.h>
// Check if fmt/format.h compiles with windows.h included before it.
#ifdef _WIN32
# include <windows.h>
#endif
#include "fmt/format.h"
#include "gmock.h"
#include "gtest-extra.h"
#include "mock-allocator.h"
#include "util.h"
#undef ERROR
#undef min
#undef max
using
std
::
size_t
;
using
fmt
::
basic_memory_buffer
;
using
fmt
::
basic_writer
;
using
fmt
::
format
;
using
fmt
::
format_error
;
...
...
@@ -32,6 +39,9 @@ using fmt::string_view;
using
fmt
::
memory_buffer
;
using
fmt
::
wmemory_buffer
;
using
testing
::
Return
;
using
testing
::
StrictMock
;
namespace
{
// Format value using the standard library.
...
...
@@ -101,6 +111,451 @@ struct WriteChecker {
EXPECT_PRED_FORMAT1(WriteChecker<wchar_t>(), value)
}
// namespace
// Tests fmt::internal::count_digits for integer type Int.
template
<
typename
Int
>
void
test_count_digits
()
{
for
(
Int
i
=
0
;
i
<
10
;
++
i
)
EXPECT_EQ
(
1u
,
fmt
::
internal
::
count_digits
(
i
));
for
(
Int
i
=
1
,
n
=
1
,
end
=
std
::
numeric_limits
<
Int
>::
max
()
/
10
;
n
<=
end
;
++
i
)
{
n
*=
10
;
EXPECT_EQ
(
i
,
fmt
::
internal
::
count_digits
(
n
-
1
));
EXPECT_EQ
(
i
+
1
,
fmt
::
internal
::
count_digits
(
n
));
}
}
TEST
(
UtilTest
,
CountDigits
)
{
test_count_digits
<
uint32_t
>
();
test_count_digits
<
uint64_t
>
();
}
struct
uint32_pair
{
uint32_t
u
[
2
];
};
TEST
(
UtilTest
,
BitCast
)
{
auto
s
=
fmt
::
internal
::
bit_cast
<
uint32_pair
>
(
uint64_t
{
42
});
EXPECT_EQ
(
fmt
::
internal
::
bit_cast
<
uint64_t
>
(
s
),
42ull
);
s
=
fmt
::
internal
::
bit_cast
<
uint32_pair
>
(
uint64_t
(
~
0ull
));
EXPECT_EQ
(
fmt
::
internal
::
bit_cast
<
uint64_t
>
(
s
),
~
0ull
);
}
TEST
(
UtilTest
,
Increment
)
{
char
s
[
10
]
=
"123"
;
increment
(
s
);
EXPECT_STREQ
(
"124"
,
s
);
s
[
2
]
=
'8'
;
increment
(
s
);
EXPECT_STREQ
(
"129"
,
s
);
increment
(
s
);
EXPECT_STREQ
(
"130"
,
s
);
s
[
1
]
=
s
[
2
]
=
'9'
;
increment
(
s
);
EXPECT_STREQ
(
"200"
,
s
);
}
TEST
(
UtilTest
,
ParseNonnegativeInt
)
{
if
(
std
::
numeric_limits
<
int
>::
max
()
!=
static_cast
<
int
>
(
static_cast
<
unsigned
>
(
1
)
<<
31
))
{
fmt
::
print
(
"Skipping parse_nonnegative_int test
\n
"
);
return
;
}
const
char
*
s
=
"10000000000"
;
EXPECT_THROW_MSG
(
parse_nonnegative_int
(
s
,
fmt
::
internal
::
error_handler
()),
fmt
::
format_error
,
"number is too big"
);
s
=
"2147483649"
;
EXPECT_THROW_MSG
(
parse_nonnegative_int
(
s
,
fmt
::
internal
::
error_handler
()),
fmt
::
format_error
,
"number is too big"
);
}
TEST
(
IteratorTest
,
CountingIterator
)
{
fmt
::
internal
::
counting_iterator
<
char
>
it
;
auto
prev
=
it
++
;
EXPECT_EQ
(
prev
.
count
(),
0
);
EXPECT_EQ
(
it
.
count
(),
1
);
}
TEST
(
IteratorTest
,
TruncatingIterator
)
{
char
*
p
=
FMT_NULL
;
fmt
::
internal
::
truncating_iterator
<
char
*>
it
(
p
,
3
);
auto
prev
=
it
++
;
EXPECT_EQ
(
prev
.
base
(),
p
);
EXPECT_EQ
(
it
.
base
(),
p
+
1
);
}
TEST
(
MemoryBufferTest
,
Ctor
)
{
basic_memory_buffer
<
char
,
123
>
buffer
;
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
EXPECT_EQ
(
123u
,
buffer
.
capacity
());
}
static
void
check_forwarding
(
mock_allocator
<
int
>
&
alloc
,
allocator_ref
<
mock_allocator
<
int
>>
&
ref
)
{
int
mem
;
// Check if value_type is properly defined.
allocator_ref
<
mock_allocator
<
int
>
>::
value_type
*
ptr
=
&
mem
;
// Check forwarding.
EXPECT_CALL
(
alloc
,
allocate
(
42
)).
WillOnce
(
testing
::
Return
(
ptr
));
ref
.
allocate
(
42
);
EXPECT_CALL
(
alloc
,
deallocate
(
ptr
,
42
));
ref
.
deallocate
(
ptr
,
42
);
}
TEST
(
AllocatorTest
,
allocator_ref
)
{
StrictMock
<
mock_allocator
<
int
>
>
alloc
;
typedef
allocator_ref
<
mock_allocator
<
int
>
>
test_allocator_ref
;
test_allocator_ref
ref
(
&
alloc
);
// Check if allocator_ref forwards to the underlying allocator.
check_forwarding
(
alloc
,
ref
);
test_allocator_ref
ref2
(
ref
);
check_forwarding
(
alloc
,
ref2
);
test_allocator_ref
ref3
;
EXPECT_EQ
(
nullptr
,
ref3
.
get
());
ref3
=
ref
;
check_forwarding
(
alloc
,
ref3
);
}
typedef
allocator_ref
<
std
::
allocator
<
char
>
>
TestAllocator
;
static
void
check_move_buffer
(
const
char
*
str
,
basic_memory_buffer
<
char
,
5
,
TestAllocator
>
&
buffer
)
{
std
::
allocator
<
char
>
*
alloc
=
buffer
.
get_allocator
().
get
();
basic_memory_buffer
<
char
,
5
,
TestAllocator
>
buffer2
(
std
::
move
(
buffer
));
// Move shouldn't destroy the inline content of the first buffer.
EXPECT_EQ
(
str
,
std
::
string
(
&
buffer
[
0
],
buffer
.
size
()));
EXPECT_EQ
(
str
,
std
::
string
(
&
buffer2
[
0
],
buffer2
.
size
()));
EXPECT_EQ
(
5u
,
buffer2
.
capacity
());
// Move should transfer allocator.
EXPECT_EQ
(
nullptr
,
buffer
.
get_allocator
().
get
());
EXPECT_EQ
(
alloc
,
buffer2
.
get_allocator
().
get
());
}
TEST
(
MemoryBufferTest
,
MoveCtor
)
{
std
::
allocator
<
char
>
alloc
;
basic_memory_buffer
<
char
,
5
,
TestAllocator
>
buffer
((
TestAllocator
(
&
alloc
)));
const
char
test
[]
=
"test"
;
buffer
.
append
(
test
,
test
+
4
);
check_move_buffer
(
"test"
,
buffer
);
// Adding one more character fills the inline buffer, but doesn't cause
// dynamic allocation.
buffer
.
push_back
(
'a'
);
check_move_buffer
(
"testa"
,
buffer
);
const
char
*
inline_buffer_ptr
=
&
buffer
[
0
];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
buffer
.
push_back
(
'b'
);
basic_memory_buffer
<
char
,
5
,
TestAllocator
>
buffer2
(
std
::
move
(
buffer
));
// Move should rip the guts of the first buffer.
EXPECT_EQ
(
inline_buffer_ptr
,
&
buffer
[
0
]);
EXPECT_EQ
(
"testab"
,
std
::
string
(
&
buffer2
[
0
],
buffer2
.
size
()));
EXPECT_GT
(
buffer2
.
capacity
(),
5u
);
}
static
void
check_move_assign_buffer
(
const
char
*
str
,
basic_memory_buffer
<
char
,
5
>
&
buffer
)
{
basic_memory_buffer
<
char
,
5
>
buffer2
;
buffer2
=
std
::
move
(
buffer
);
// Move shouldn't destroy the inline content of the first buffer.
EXPECT_EQ
(
str
,
std
::
string
(
&
buffer
[
0
],
buffer
.
size
()));
EXPECT_EQ
(
str
,
std
::
string
(
&
buffer2
[
0
],
buffer2
.
size
()));
EXPECT_EQ
(
5u
,
buffer2
.
capacity
());
}
TEST
(
MemoryBufferTest
,
MoveAssignment
)
{
basic_memory_buffer
<
char
,
5
>
buffer
;
const
char
test
[]
=
"test"
;
buffer
.
append
(
test
,
test
+
4
);
check_move_assign_buffer
(
"test"
,
buffer
);
// Adding one more character fills the inline buffer, but doesn't cause
// dynamic allocation.
buffer
.
push_back
(
'a'
);
check_move_assign_buffer
(
"testa"
,
buffer
);
const
char
*
inline_buffer_ptr
=
&
buffer
[
0
];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
buffer
.
push_back
(
'b'
);
basic_memory_buffer
<
char
,
5
>
buffer2
;
buffer2
=
std
::
move
(
buffer
);
// Move should rip the guts of the first buffer.
EXPECT_EQ
(
inline_buffer_ptr
,
&
buffer
[
0
]);
EXPECT_EQ
(
"testab"
,
std
::
string
(
&
buffer2
[
0
],
buffer2
.
size
()));
EXPECT_GT
(
buffer2
.
capacity
(),
5u
);
}
TEST
(
MemoryBufferTest
,
Grow
)
{
typedef
allocator_ref
<
mock_allocator
<
int
>
>
Allocator
;
typedef
basic_memory_buffer
<
int
,
10
,
Allocator
>
Base
;
mock_allocator
<
int
>
alloc
;
struct
TestMemoryBuffer
:
Base
{
TestMemoryBuffer
(
Allocator
alloc
)
:
Base
(
alloc
)
{}
void
grow
(
std
::
size_t
size
)
{
Base
::
grow
(
size
);
}
}
buffer
((
Allocator
(
&
alloc
)));
buffer
.
resize
(
7
);
using
fmt
::
internal
::
to_unsigned
;
for
(
int
i
=
0
;
i
<
7
;
++
i
)
buffer
[
to_unsigned
(
i
)]
=
i
*
i
;
EXPECT_EQ
(
10u
,
buffer
.
capacity
());
int
mem
[
20
];
mem
[
7
]
=
0xdead
;
EXPECT_CALL
(
alloc
,
allocate
(
20
)).
WillOnce
(
Return
(
mem
));
buffer
.
grow
(
20
);
EXPECT_EQ
(
20u
,
buffer
.
capacity
());
// Check if size elements have been copied
for
(
int
i
=
0
;
i
<
7
;
++
i
)
EXPECT_EQ
(
i
*
i
,
buffer
[
to_unsigned
(
i
)]);
// and no more than that.
EXPECT_EQ
(
0xdead
,
buffer
[
7
]);
EXPECT_CALL
(
alloc
,
deallocate
(
mem
,
20
));
}
TEST
(
MemoryBufferTest
,
Allocator
)
{
typedef
allocator_ref
<
mock_allocator
<
char
>
>
TestAllocator
;
basic_memory_buffer
<
char
,
10
,
TestAllocator
>
buffer
;
EXPECT_EQ
(
nullptr
,
buffer
.
get_allocator
().
get
());
StrictMock
<
mock_allocator
<
char
>
>
alloc
;
char
mem
;
{
basic_memory_buffer
<
char
,
10
,
TestAllocator
>
buffer2
((
TestAllocator
(
&
alloc
)));
EXPECT_EQ
(
&
alloc
,
buffer2
.
get_allocator
().
get
());
std
::
size_t
size
=
2
*
fmt
::
inline_buffer_size
;
EXPECT_CALL
(
alloc
,
allocate
(
size
)).
WillOnce
(
Return
(
&
mem
));
buffer2
.
reserve
(
size
);
EXPECT_CALL
(
alloc
,
deallocate
(
&
mem
,
size
));
}
}
TEST
(
MemoryBufferTest
,
ExceptionInDeallocate
)
{
typedef
allocator_ref
<
mock_allocator
<
char
>
>
TestAllocator
;
StrictMock
<
mock_allocator
<
char
>
>
alloc
;
basic_memory_buffer
<
char
,
10
,
TestAllocator
>
buffer
((
TestAllocator
(
&
alloc
)));
std
::
size_t
size
=
2
*
fmt
::
inline_buffer_size
;
std
::
vector
<
char
>
mem
(
size
);
{
EXPECT_CALL
(
alloc
,
allocate
(
size
)).
WillOnce
(
Return
(
&
mem
[
0
]));
buffer
.
resize
(
size
);
std
::
fill
(
&
buffer
[
0
],
&
buffer
[
0
]
+
size
,
'x'
);
}
std
::
vector
<
char
>
mem2
(
2
*
size
);
{
EXPECT_CALL
(
alloc
,
allocate
(
2
*
size
)).
WillOnce
(
Return
(
&
mem2
[
0
]));
std
::
exception
e
;
EXPECT_CALL
(
alloc
,
deallocate
(
&
mem
[
0
],
size
)).
WillOnce
(
testing
::
Throw
(
e
));
EXPECT_THROW
(
buffer
.
reserve
(
2
*
size
),
std
::
exception
);
EXPECT_EQ
(
&
mem2
[
0
],
&
buffer
[
0
]);
// Check that the data has been copied.
for
(
std
::
size_t
i
=
0
;
i
<
size
;
++
i
)
EXPECT_EQ
(
'x'
,
buffer
[
i
]);
}
EXPECT_CALL
(
alloc
,
deallocate
(
&
mem2
[
0
],
2
*
size
));
}
TEST
(
FixedBufferTest
,
Ctor
)
{
char
array
[
10
]
=
"garbage"
;
fmt
::
basic_fixed_buffer
<
char
>
buffer
(
array
,
sizeof
(
array
));
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
EXPECT_EQ
(
10u
,
buffer
.
capacity
());
EXPECT_EQ
(
array
,
buffer
.
data
());
}
TEST
(
FixedBufferTest
,
CompileTimeSizeCtor
)
{
char
array
[
10
]
=
"garbage"
;
fmt
::
basic_fixed_buffer
<
char
>
buffer
(
array
);
EXPECT_EQ
(
static_cast
<
size_t
>
(
0
),
buffer
.
size
());
EXPECT_EQ
(
10u
,
buffer
.
capacity
());
EXPECT_EQ
(
array
,
buffer
.
data
());
}
TEST
(
FixedBufferTest
,
BufferOverflow
)
{
char
array
[
10
];
fmt
::
basic_fixed_buffer
<
char
>
buffer
(
array
);
buffer
.
resize
(
10
);
EXPECT_THROW_MSG
(
buffer
.
resize
(
11
),
std
::
runtime_error
,
"buffer overflow"
);
}
#ifdef _WIN32
TEST
(
UtilTest
,
UTF16ToUTF8
)
{
std
::
string
s
=
"ёжик"
;
fmt
::
internal
::
utf16_to_utf8
u
(
L"
\x0451\x0436\x0438\x043A
"
);
EXPECT_EQ
(
s
,
u
.
str
());
EXPECT_EQ
(
s
.
size
(),
u
.
size
());
}
TEST
(
UtilTest
,
UTF16ToUTF8EmptyString
)
{
std
::
string
s
=
""
;
fmt
::
internal
::
utf16_to_utf8
u
(
L""
);
EXPECT_EQ
(
s
,
u
.
str
());
EXPECT_EQ
(
s
.
size
(),
u
.
size
());
}
TEST
(
UtilTest
,
UTF8ToUTF16
)
{
std
::
string
s
=
"лошадка"
;
fmt
::
internal
::
utf8_to_utf16
u
(
s
.
c_str
());
EXPECT_EQ
(
L"
\x043B\x043E\x0448\x0430\x0434\x043A\x0430
"
,
u
.
str
());
EXPECT_EQ
(
7
,
u
.
size
());
}
TEST
(
UtilTest
,
UTF8ToUTF16EmptyString
)
{
std
::
string
s
=
""
;
fmt
::
internal
::
utf8_to_utf16
u
(
s
.
c_str
());
EXPECT_EQ
(
L""
,
u
.
str
());
EXPECT_EQ
(
s
.
size
(),
u
.
size
());
}
template
<
typename
Converter
,
typename
Char
>
void
check_utf_conversion_error
(
const
char
*
message
,
fmt
::
basic_string_view
<
Char
>
str
=
fmt
::
basic_string_view
<
Char
>
(
0
,
1
))
{
fmt
::
memory_buffer
out
;
fmt
::
internal
::
format_windows_error
(
out
,
ERROR_INVALID_PARAMETER
,
message
);
fmt
::
system_error
error
(
0
,
""
);
try
{
(
Converter
)(
str
);
}
catch
(
const
fmt
::
system_error
&
e
)
{
error
=
e
;
}
EXPECT_EQ
(
ERROR_INVALID_PARAMETER
,
error
.
error_code
());
EXPECT_EQ
(
fmt
::
to_string
(
out
),
error
.
what
());
}
TEST
(
UtilTest
,
UTF16ToUTF8Error
)
{
check_utf_conversion_error
<
fmt
::
internal
::
utf16_to_utf8
,
wchar_t
>
(
"cannot convert string from UTF-16 to UTF-8"
);
}
TEST
(
UtilTest
,
UTF8ToUTF16Error
)
{
const
char
*
message
=
"cannot convert string from UTF-8 to UTF-16"
;
check_utf_conversion_error
<
fmt
::
internal
::
utf8_to_utf16
,
char
>
(
message
);
check_utf_conversion_error
<
fmt
::
internal
::
utf8_to_utf16
,
char
>
(
message
,
fmt
::
string_view
(
"foo"
,
INT_MAX
+
1u
));
}
TEST
(
UtilTest
,
UTF16ToUTF8Convert
)
{
fmt
::
internal
::
utf16_to_utf8
u
;
EXPECT_EQ
(
ERROR_INVALID_PARAMETER
,
u
.
convert
(
fmt
::
wstring_view
(
0
,
1
)));
EXPECT_EQ
(
ERROR_INVALID_PARAMETER
,
u
.
convert
(
fmt
::
wstring_view
(
L"foo"
,
INT_MAX
+
1u
)));
}
#endif // _WIN32
typedef
void
(
*
FormatErrorMessage
)(
fmt
::
internal
::
buffer
&
out
,
int
error_code
,
string_view
message
);
template
<
typename
Error
>
void
check_throw_error
(
int
error_code
,
FormatErrorMessage
format
)
{
fmt
::
system_error
error
(
0
,
""
);
try
{
throw
Error
(
error_code
,
"test {}"
,
"error"
);
}
catch
(
const
fmt
::
system_error
&
e
)
{
error
=
e
;
}
fmt
::
memory_buffer
message
;
format
(
message
,
error_code
,
"test error"
);
EXPECT_EQ
(
to_string
(
message
),
error
.
what
());
EXPECT_EQ
(
error_code
,
error
.
error_code
());
}
TEST
(
UtilTest
,
FormatSystemError
)
{
fmt
::
memory_buffer
message
;
fmt
::
format_system_error
(
message
,
EDOM
,
"test"
);
EXPECT_EQ
(
fmt
::
format
(
"test: {}"
,
get_system_error
(
EDOM
)),
to_string
(
message
));
message
=
fmt
::
memory_buffer
();
// Check if std::allocator throws on allocating max size_t / 2 chars.
size_t
max_size
=
std
::
numeric_limits
<
size_t
>::
max
()
/
2
;
bool
throws_on_alloc
=
false
;
try
{
std
::
allocator
<
char
>
alloc
;
alloc
.
deallocate
(
alloc
.
allocate
(
max_size
),
max_size
);
}
catch
(
const
std
::
bad_alloc
&
)
{
throws_on_alloc
=
true
;
}
if
(
!
throws_on_alloc
)
{
fmt
::
print
(
"warning: std::allocator allocates {} chars"
,
max_size
);
return
;
}
fmt
::
format_system_error
(
message
,
EDOM
,
fmt
::
string_view
(
nullptr
,
max_size
));
EXPECT_EQ
(
fmt
::
format
(
"error {}"
,
EDOM
),
to_string
(
message
));
}
TEST
(
UtilTest
,
SystemError
)
{
fmt
::
system_error
e
(
EDOM
,
"test"
);
EXPECT_EQ
(
fmt
::
format
(
"test: {}"
,
get_system_error
(
EDOM
)),
e
.
what
());
EXPECT_EQ
(
EDOM
,
e
.
error_code
());
check_throw_error
<
fmt
::
system_error
>
(
EDOM
,
fmt
::
format_system_error
);
}
TEST
(
UtilTest
,
ReportSystemError
)
{
fmt
::
memory_buffer
out
;
fmt
::
format_system_error
(
out
,
EDOM
,
"test error"
);
out
.
push_back
(
'\n'
);
EXPECT_WRITE
(
stderr
,
fmt
::
report_system_error
(
EDOM
,
"test error"
),
to_string
(
out
));
}
#ifdef _WIN32
TEST
(
UtilTest
,
FormatWindowsError
)
{
LPWSTR
message
=
0
;
FormatMessageW
(
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_SYSTEM
|
FORMAT_MESSAGE_IGNORE_INSERTS
,
0
,
ERROR_FILE_EXISTS
,
MAKELANGID
(
LANG_NEUTRAL
,
SUBLANG_DEFAULT
),
reinterpret_cast
<
LPWSTR
>
(
&
message
),
0
,
0
);
fmt
::
internal
::
utf16_to_utf8
utf8_message
(
message
);
LocalFree
(
message
);
fmt
::
memory_buffer
actual_message
;
fmt
::
internal
::
format_windows_error
(
actual_message
,
ERROR_FILE_EXISTS
,
"test"
);
EXPECT_EQ
(
fmt
::
format
(
"test: {}"
,
utf8_message
.
str
()),
fmt
::
to_string
(
actual_message
));
actual_message
.
resize
(
0
);
fmt
::
internal
::
format_windows_error
(
actual_message
,
ERROR_FILE_EXISTS
,
fmt
::
string_view
(
0
,
std
::
numeric_limits
<
size_t
>::
max
()));
EXPECT_EQ
(
fmt
::
format
(
"error {}"
,
ERROR_FILE_EXISTS
),
fmt
::
to_string
(
actual_message
));
}
TEST
(
UtilTest
,
FormatLongWindowsError
)
{
LPWSTR
message
=
0
;
// this error code is not available on all Windows platforms and
// Windows SDKs, so do not fail the test if the error string cannot
// be retrieved.
const
int
provisioning_not_allowed
=
0x80284013L
/*TBS_E_PROVISIONING_NOT_ALLOWED*/
;
if
(
FormatMessageW
(
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_SYSTEM
|
FORMAT_MESSAGE_IGNORE_INSERTS
,
0
,
static_cast
<
DWORD
>
(
provisioning_not_allowed
),
MAKELANGID
(
LANG_NEUTRAL
,
SUBLANG_DEFAULT
),
reinterpret_cast
<
LPWSTR
>
(
&
message
),
0
,
0
)
==
0
)
{
return
;
}
fmt
::
internal
::
utf16_to_utf8
utf8_message
(
message
);
LocalFree
(
message
);
fmt
::
memory_buffer
actual_message
;
fmt
::
internal
::
format_windows_error
(
actual_message
,
provisioning_not_allowed
,
"test"
);
EXPECT_EQ
(
fmt
::
format
(
"test: {}"
,
utf8_message
.
str
()),
fmt
::
to_string
(
actual_message
));
}
TEST
(
UtilTest
,
WindowsError
)
{
check_throw_error
<
fmt
::
windows_error
>
(
ERROR_FILE_EXISTS
,
fmt
::
internal
::
format_windows_error
);
}
TEST
(
UtilTest
,
ReportWindowsError
)
{
fmt
::
memory_buffer
out
;
fmt
::
internal
::
format_windows_error
(
out
,
ERROR_FILE_EXISTS
,
"test error"
);
out
.
push_back
(
'\n'
);
EXPECT_WRITE
(
stderr
,
fmt
::
report_windows_error
(
ERROR_FILE_EXISTS
,
"test error"
),
fmt
::
to_string
(
out
));
}
#endif // _WIN32
TEST
(
StringViewTest
,
Ctor
)
{
EXPECT_STREQ
(
"abc"
,
string_view
(
"abc"
).
data
());
EXPECT_EQ
(
3u
,
string_view
(
"abc"
).
size
());
...
...
test/mock-allocator.h
View file @
3f4984fb
...
...
@@ -9,23 +9,24 @@
#define FMT_MOCK_ALLOCATOR_H_
#include "gmock.h"
#include "fmt/format.h"
template
<
typename
T
>
class
MockA
llocator
{
class
mock_a
llocator
{
public:
MockA
llocator
()
{}
MockAllocator
(
const
MockA
llocator
&
)
{}
mock_a
llocator
()
{}
mock_allocator
(
const
mock_a
llocator
&
)
{}
typedef
T
value_type
;
MOCK_METHOD1_T
(
allocate
,
T
*
(
std
::
size_t
n
));
MOCK_METHOD2_T
(
deallocate
,
void
(
T
*
p
,
std
::
size_t
n
));
};
template
<
typename
Allocator
>
class
AllocatorR
ef
{
class
allocator_r
ef
{
private:
Allocator
*
alloc_
;
void
move
(
AllocatorR
ef
&
other
)
{
void
move
(
allocator_r
ef
&
other
)
{
alloc_
=
other
.
alloc_
;
other
.
alloc_
=
nullptr
;
}
...
...
@@ -33,18 +34,18 @@ class AllocatorRef {
public:
typedef
typename
Allocator
::
value_type
value_type
;
explicit
AllocatorR
ef
(
Allocator
*
alloc
=
nullptr
)
:
alloc_
(
alloc
)
{}
explicit
allocator_r
ef
(
Allocator
*
alloc
=
nullptr
)
:
alloc_
(
alloc
)
{}
AllocatorRef
(
const
AllocatorR
ef
&
other
)
:
alloc_
(
other
.
alloc_
)
{}
AllocatorRef
(
AllocatorR
ef
&&
other
)
{
move
(
other
);
}
allocator_ref
(
const
allocator_r
ef
&
other
)
:
alloc_
(
other
.
alloc_
)
{}
allocator_ref
(
allocator_r
ef
&&
other
)
{
move
(
other
);
}
AllocatorRef
&
operator
=
(
AllocatorR
ef
&&
other
)
{
allocator_ref
&
operator
=
(
allocator_r
ef
&&
other
)
{
assert
(
this
!=
&
other
);
move
(
other
);
return
*
this
;
}
AllocatorRef
&
operator
=
(
const
AllocatorR
ef
&
other
)
{
allocator_ref
&
operator
=
(
const
allocator_r
ef
&
other
)
{
alloc_
=
other
.
alloc_
;
return
*
this
;
}
...
...
test/test-assert.h
View file @
3f4984fb
...
...
@@ -5,23 +5,22 @@
//
// For the license information refer to format.h.
#ifndef FMT_TEST_ASSERT_H
#define FMT_TEST_ASSERT_H
#ifndef FMT_TEST_ASSERT_H
_
#define FMT_TEST_ASSERT_H
_
#include <stdexcept>
#include "gtest.h"
class
AssertionF
ailure
:
public
std
::
logic_error
{
class
assertion_f
ailure
:
public
std
::
logic_error
{
public:
explicit
AssertionF
ailure
(
const
char
*
message
)
:
std
::
logic_error
(
message
)
{}
explicit
assertion_f
ailure
(
const
char
*
message
)
:
std
::
logic_error
(
message
)
{}
};
#define FMT_ASSERT(condition, message) \
if (!(condition)) throw AssertionFailure(message);
#include "gtest-extra.h"
if (!(condition)) throw assertion_failure(message);
// Expects an assertion failure.
#define EXPECT_ASSERT(stmt, message) \
EXPECT_THROW_MSG(stmt, AssertionFailure, message
)
FMT_TEST_THROW_(stmt, assertion_failure, message, GTEST_NONFATAL_FAILURE_
)
#endif // FMT_TEST_ASSERT_H
#endif // FMT_TEST_ASSERT_H
_
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment