Skip to content

Commit bf9a13a

Browse files
authored
Merge pull request #508 from rstahn/rstahn-patch-1
Add option 'create_error_stubs' (default is 'true')
2 parents bb0b0e0 + ad9ce63 commit bf9a13a

20 files changed

Lines changed: 357 additions & 55 deletions

.gitmodules

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
[submodule "vendor/unity"]
2-
path = vendor/unity
3-
url = https://github.com/throwtheswitch/unity.git
4-
branch = master
51
[submodule "vendor/c_exception"]
62
path = vendor/c_exception
73
url = https://github.com/throwtheswitch/cexception.git
84
branch = master
5+
[submodule "vendor/unity"]
6+
path = vendor/unity
7+
url = https://github.com/throwtheswitch/unity.git
8+
branch = master

docs/CMock_Summary.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
CMock: A Summary
22
================
33

4-
*[ThrowTheSwitch.org](http://throwtheswitch.org)*
4+
*[ThrowTheSwitch.org](http://throwtheswitch.org)*
55

66
*This documentation is released under a Creative Commons 3.0 Attribution Share-Alike License*
77

@@ -138,7 +138,7 @@ that resembles a pointer or array, it breaks the argument into TWO arguments.
138138
The first is the original pointer. The second specify the number of elements
139139
it is to verify of that array. If you specify 1, it'll check one object. If 2,
140140
it'll assume your pointer is pointing at the first of two elements in an array.
141-
If you specify zero elements and `UNITY_COMPARE_PTRS_ON_ZERO_ARRAY` is defined,
141+
If you specify zero elements and `UNITY_COMPARE_PTRS_ON_ZERO_ARRAY` is defined,
142142
then this assertion can also be used to directly compare the pointers to verify
143143
that they are pointing to the same memory address.
144144

@@ -420,6 +420,27 @@ from the defaults. We've tried to specify what the defaults are below.
420420

421421
* default: *nil*
422422

423+
* `:create_error_stubs`:
424+
New users of CMock sometimes struggle with the concept that CMock is
425+
generating differently named interface functions for a mock depending
426+
on whether a function to be mocked has a return value or not (see
427+
description of the plugins `:Expect`, `:ExpectAnyArgs`, `:Array`, `:Ignore`,
428+
`:IgnoreStateless` above). They are looking e.g. for a function `func_Expect()`
429+
while CMock generated the function `func_ExpectAndReturn()` instead.
430+
This has proven to be a significant hurdle, because it is not easy to
431+
spot the slightly different named function within the generated mock.
432+
Therefore CMock (v2.6.0 and newer) is generating *both* functions
433+
per default, although one of them has only a "stub" functionality:
434+
on call it will always fail the test emitting a helpful error message
435+
pointing towards the correct function.
436+
437+
Experienced CMock users on the other hand might prefer the original
438+
behavior, where no additional error stubs are generated - e.g. to
439+
ensure code completion offers only "real" functionality of the mock.
440+
In this case, the option `:create_error_stubs` can be set to false.
441+
442+
* default: true
443+
423444
* `:enforce_strict_ordering`:
424445
CMock always enforces the order that you call a particular function,
425446
so if you expect GrabNabber(int size) to be called three times, it
@@ -637,7 +658,7 @@ from the defaults. We've tried to specify what the defaults are below.
637658
for the test code to also see the newly generated header ,and not
638659
the old header with inline functions, the build system has to add
639660
the mock folder to the include paths.
640-
661+
641662
Furthermore, we need to keep the order of include paths in mind. We
642663
have to set the mock folder before the other includes to avoid the
643664
test code including the original header instead of the newly

lib/cmock_config.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class CMockConfig
4343
:array_size_name => 'size|len',
4444
:skeleton => false,
4545
:exclude_setjmp_h => false,
46+
:create_error_stubs => true,
4647

4748
# Format to look for inline functions.
4849
# This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static")

lib/cmock_generator_plugin_array.rb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def initialize(config, utils)
1313
@config = config
1414
@ptr_handling = @config.when_ptr
1515
@ordered = @config.enforce_strict_ordering
16+
@error_stubs = @config.create_error_stubs
1617
@utils = utils
1718
@unity_helper = @utils.helpers[:unity_helper]
1819
@priority = 8
@@ -33,15 +34,17 @@ def mock_function_declarations(function)
3334
type = @utils.arg_type_with_const(m)
3435
m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}"
3536
end.join(', ')
37+
lines = ''
3638
if function[:return][:void?]
37-
"#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call_i}, cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectWithArray (not AndReturn)\");\n" \
38-
"#define #{function[:name]}_ExpectWithArray(#{args_call_i}) #{function[:name]}_CMockExpectWithArray(__LINE__, #{args_call_o})\n" \
39-
"void #{function[:name]}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string});\n"
39+
lines << "#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call_i}, cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectWithArray (not AndReturn)\");\n" if @error_stubs
40+
lines << "#define #{function[:name]}_ExpectWithArray(#{args_call_i}) #{function[:name]}_CMockExpectWithArray(__LINE__, #{args_call_o})\n"
41+
lines << "void #{function[:name]}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, #{args_string});\n"
4042
else
41-
"#define #{function[:name]}_ExpectWithArray(#{args_call_i}) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectWithArrayAndReturn\");\n" \
42-
"#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call_i}, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, #{args_call_o}, cmock_retval)\n" \
43-
"void #{function[:name]}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]});\n"
43+
lines << "#define #{function[:name]}_ExpectWithArray(#{args_call_i}) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectWithArrayAndReturn\");\n" if @error_stubs
44+
lines << "#define #{function[:name]}_ExpectWithArrayAndReturn(#{args_call_i}, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, #{args_call_o}, cmock_retval)\n"
45+
lines << "void #{function[:name]}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, #{args_string}, #{function[:return][:str]});\n"
4446
end
47+
lines
4548
end
4649

4750
def mock_interfaces(function)

lib/cmock_generator_plugin_expect.rb

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def initialize(config, utils)
1313
@config = config
1414
@ptr_handling = @config.when_ptr
1515
@ordered = @config.enforce_strict_ordering
16+
@error_stubs = @config.create_error_stubs
1617
@utils = utils
1718
@unity_helper = @utils.helpers[:unity_helper]
1819
@priority = 5
@@ -35,25 +36,27 @@ def instance_typedefs(function)
3536
end
3637

3738
def mock_function_declarations(function)
39+
lines = ''
3840
if function[:args].empty?
3941
if function[:return][:void?]
40-
"#define #{function[:name]}_ExpectAndReturn(cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _Expect (not AndReturn)\");\n" \
41-
"#define #{function[:name]}_Expect() #{function[:name]}_CMockExpect(__LINE__)\n" \
42-
"void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line);\n"
42+
lines << "#define #{function[:name]}_ExpectAndReturn(cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _Expect (not AndReturn)\");\n" if @error_stubs
43+
lines << "#define #{function[:name]}_Expect() #{function[:name]}_CMockExpect(__LINE__)\n"
44+
lines << "void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line);\n"
4345
else
44-
"#define #{function[:name]}_Expect() TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectAndReturn\");\n" \
45-
"#define #{function[:name]}_ExpectAndReturn(cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, cmock_retval)\n" \
46-
"void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
46+
lines << "#define #{function[:name]}_Expect() TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectAndReturn\");\n" if @error_stubs
47+
lines << "#define #{function[:name]}_ExpectAndReturn(cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, cmock_retval)\n"
48+
lines << "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
4749
end
4850
elsif function[:return][:void?]
49-
"#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _Expect (not AndReturn)\");\n" \
50-
"#define #{function[:name]}_Expect(#{function[:args_call]}) #{function[:name]}_CMockExpect(__LINE__, #{function[:args_call]})\n" \
51-
"void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]});\n"
51+
lines << "#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _Expect (not AndReturn)\");\n" if @error_stubs
52+
lines << "#define #{function[:name]}_Expect(#{function[:args_call]}) #{function[:name]}_CMockExpect(__LINE__, #{function[:args_call]})\n"
53+
lines << "void #{function[:name]}_CMockExpect(UNITY_LINE_TYPE cmock_line, #{function[:args_string]});\n"
5254
else
53-
"#define #{function[:name]}_Expect(#{function[:args_call]}) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectAndReturn\");\n" \
54-
"#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, #{function[:args_call]}, cmock_retval)\n" \
55-
"void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]});\n"
55+
lines << "#define #{function[:name]}_Expect(#{function[:args_call]}) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectAndReturn\");\n" if @error_stubs
56+
lines << "#define #{function[:name]}_ExpectAndReturn(#{function[:args_call]}, cmock_retval) #{function[:name]}_CMockExpectAndReturn(__LINE__, #{function[:args_call]}, cmock_retval)\n"
57+
lines << "void #{function[:name]}_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:args_string]}, #{function[:return][:str]});\n"
5658
end
59+
lines
5760
end
5861

5962
def mock_implementation_always_check_args(function)

lib/cmock_generator_plugin_expect_any_args.rb

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class CMockGeneratorPluginExpectAnyArgs
1010

1111
def initialize(config, utils)
1212
@config = config
13+
@error_stubs = @config.create_error_stubs
1314
@utils = utils
1415
@priority = 3
1516
end
@@ -19,17 +20,19 @@ def instance_typedefs(_function)
1920
end
2021

2122
def mock_function_declarations(function)
23+
lines = ''
2224
if function[:args].empty?
23-
''
25+
lines << ''
2426
elsif function[:return][:void?]
25-
"#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectAnyArgs (not AndReturn)\");\n" \
26-
"#define #{function[:name]}_ExpectAnyArgs() #{function[:name]}_CMockExpectAnyArgs(__LINE__)\n" \
27-
"void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line);\n"
27+
lines << "#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectAnyArgs (not AndReturn)\");\n" if @error_stubs
28+
lines << "#define #{function[:name]}_ExpectAnyArgs() #{function[:name]}_CMockExpectAnyArgs(__LINE__)\n"
29+
lines << "void #{function[:name]}_CMockExpectAnyArgs(UNITY_LINE_TYPE cmock_line);\n"
2830
else
29-
"#define #{function[:name]}_ExpectAnyArgs() TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectAnyArgsAndReturn\");\n" \
30-
"#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) #{function[:name]}_CMockExpectAnyArgsAndReturn(__LINE__, cmock_retval)\n" \
31-
"void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
31+
lines << "#define #{function[:name]}_ExpectAnyArgs() TEST_FAIL_MESSAGE(\"#{function[:name]} requires _ExpectAnyArgsAndReturn\");\n" if @error_stubs
32+
lines << "#define #{function[:name]}_ExpectAnyArgsAndReturn(cmock_retval) #{function[:name]}_CMockExpectAnyArgsAndReturn(__LINE__, cmock_retval)\n"
33+
lines << "void #{function[:name]}_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
3234
end
35+
lines
3336
end
3437

3538
def mock_interfaces(function)

lib/cmock_generator_plugin_ignore.rb

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class CMockGeneratorPluginIgnore
1010

1111
def initialize(config, utils)
1212
@config = config
13+
@error_stubs = @config.create_error_stubs
1314
@utils = utils
1415
@priority = 2
1516
end
@@ -23,15 +24,16 @@ def instance_structure(function)
2324
end
2425

2526
def mock_function_declarations(function)
26-
lines = if function[:return][:void?]
27-
"#define #{function[:name]}_IgnoreAndReturn(cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _Ignore (not AndReturn)\");\n" \
28-
"#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" \
29-
"void #{function[:name]}_CMockIgnore(void);\n"
30-
else
31-
"#define #{function[:name]}_Ignore() TEST_FAIL_MESSAGE(\"#{function[:name]} requires _IgnoreAndReturn\");\n" \
32-
"#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(__LINE__, cmock_retval)\n" \
33-
"void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
34-
end
27+
lines = ''
28+
if function[:return][:void?]
29+
lines << "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _Ignore (not AndReturn)\");\n" if @error_stubs
30+
lines << "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n"
31+
lines << "void #{function[:name]}_CMockIgnore(void);\n"
32+
else
33+
lines << "#define #{function[:name]}_Ignore() TEST_FAIL_MESSAGE(\"#{function[:name]} requires _IgnoreAndReturn\");\n" if @error_stubs
34+
lines << "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(__LINE__, cmock_retval)\n"
35+
lines << "void #{function[:name]}_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, #{function[:return][:str]});\n"
36+
end
3537

3638
# Add stop ignore function. it does not matter if there are any args
3739
lines << "#define #{function[:name]}_StopIgnore() #{function[:name]}_CMockStopIgnore()\n" \

lib/cmock_generator_plugin_ignore_stateless.rb

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class CMockGeneratorPluginIgnoreStateless
1010

1111
def initialize(config, utils)
1212
@config = config
13+
@error_stubs = @config.create_error_stubs
1314
@utils = utils
1415
@priority = 2
1516
end
@@ -23,15 +24,16 @@ def instance_structure(function)
2324
end
2425

2526
def mock_function_declarations(function)
26-
lines = if function[:return][:void?]
27-
"#define #{function[:name]}_IgnoreAndReturn(cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _Ignore (not AndReturn)\");\n" \
28-
"#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n" \
29-
"void #{function[:name]}_CMockIgnore(void);\n"
30-
else
31-
"#define #{function[:name]}_Ignore() TEST_FAIL_MESSAGE(\"#{function[:name]} requires _IgnoreAndReturn\");\n" \
32-
"#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(cmock_retval)\n" \
33-
"void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]});\n"
34-
end
27+
lines = ''
28+
if function[:return][:void?]
29+
lines << "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) TEST_FAIL_MESSAGE(\"#{function[:name]} requires _Ignore (not AndReturn)\");\n" if @error_stubs
30+
lines << "#define #{function[:name]}_Ignore() #{function[:name]}_CMockIgnore()\n"
31+
lines << "void #{function[:name]}_CMockIgnore(void);\n"
32+
else
33+
lines << "#define #{function[:name]}_Ignore() TEST_FAIL_MESSAGE(\"#{function[:name]} requires _IgnoreAndReturn\");\n" if @error_stubs
34+
lines << "#define #{function[:name]}_IgnoreAndReturn(cmock_retval) #{function[:name]}_CMockIgnoreAndReturn(cmock_retval)\n"
35+
lines << "void #{function[:name]}_CMockIgnoreAndReturn(#{function[:return][:str]});\n"
36+
end
3537

3638
# Add stop ignore function. it does not matter if there are any args
3739
lines << "#define #{function[:name]}_StopIgnore() #{function[:name]}_CMockStopIgnore()\n" \
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# =========================================================================
2+
# CMock - Automatic Mock Generation for C
3+
# ThrowTheSwitch.org
4+
# Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams
5+
# SPDX-License-Identifier: MIT
6+
# =========================================================================
7+
8+
require File.expand_path(File.dirname(__FILE__)) + "/../test_helper"
9+
require File.expand_path(File.dirname(__FILE__)) + '/../../lib/cmock_generator_plugin_array'
10+
require File.expand_path(File.dirname(__FILE__)) + '/../../lib/cmock_generator_utils'
11+
12+
class UtilsStub
13+
def helpers
14+
{}
15+
end
16+
def arg_type_with_const(arg)
17+
CMockGeneratorUtils.arg_type_with_const(arg)
18+
end
19+
def code_add_base_expectation(func)
20+
"mock_retval_0"
21+
end
22+
end
23+
24+
describe CMockGeneratorPluginArray, "Verify Generation Of Mock Function Declarations Without Error Stubs By CMockPGeneratorluginArray Module" do
25+
before do
26+
#no strict ordering
27+
@config = create_stub(
28+
:when_ptr => :compare_data,
29+
:enforce_strict_ordering => false,
30+
:respond_to? => true,
31+
:create_error_stubs => false)
32+
33+
@utils = UtilsStub.new
34+
35+
@cmock_generator_plugin_array = CMockGeneratorPluginArray.new(@config, @utils)
36+
end
37+
38+
after do
39+
end
40+
41+
it "add another mock function declaration for functions of style 'void func(int* tofu)'" do
42+
function = {:name => "Pine",
43+
:args => [{ :type => "int*",
44+
:name => "tofu",
45+
:ptr? => true,
46+
}],
47+
:return => test_return[:void],
48+
:contains_ptr? => true }
49+
50+
expected = "#define #{function[:name]}_ExpectWithArray(tofu, tofu_Depth) #{function[:name]}_CMockExpectWithArray(__LINE__, tofu, (tofu_Depth))\n" +
51+
"void #{function[:name]}_CMockExpectWithArray(UNITY_LINE_TYPE cmock_line, int* tofu, int tofu_Depth);\n"
52+
returned = @cmock_generator_plugin_array.mock_function_declarations(function)
53+
assert_equal(expected, returned)
54+
end
55+
56+
it "add another mock function declaration for functions of style 'const char* func(int* tofu)'" do
57+
function = {:name => "Pine",
58+
:args => [{ :type => "int*",
59+
:name => "tofu",
60+
:ptr? => true,
61+
}],
62+
:return => test_return[:string],
63+
:contains_ptr? => true }
64+
65+
expected = "#define #{function[:name]}_ExpectWithArrayAndReturn(tofu, tofu_Depth, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, tofu, (tofu_Depth), cmock_retval)\n" +
66+
"void #{function[:name]}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, int* tofu, int tofu_Depth, const char* cmock_to_return);\n"
67+
returned = @cmock_generator_plugin_array.mock_function_declarations(function)
68+
assert_equal(expected, returned)
69+
end
70+
71+
it "add another mock function declaration for functions of style 'const char* func(const int* tofu)'" do
72+
function = {:name => "Pine",
73+
:args => [{ :type => "const int*",
74+
:name => "tofu",
75+
:ptr? => true,
76+
:const? => true,
77+
}],
78+
:return => test_return[:string],
79+
:contains_ptr? => true }
80+
81+
expected = "#define #{function[:name]}_ExpectWithArrayAndReturn(tofu, tofu_Depth, cmock_retval) #{function[:name]}_CMockExpectWithArrayAndReturn(__LINE__, tofu, (tofu_Depth), cmock_retval)\n" +
82+
"void #{function[:name]}_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, const int* tofu, int tofu_Depth, const char* cmock_to_return);\n"
83+
returned = @cmock_generator_plugin_array.mock_function_declarations(function)
84+
assert_equal(expected, returned)
85+
end
86+
87+
end

0 commit comments

Comments
 (0)