mirror of
https://github.com/ThrowTheSwitch/CMock.git
synced 2026-06-23 05:50:32 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d482f56066 | |||
| 1ef72ca854 | |||
| 85058d1407 | |||
| 411f6852f9 | |||
| 383c43246c |
@@ -378,6 +378,23 @@ generate a skeleton instead:
|
||||
ruby cmock.rb --skeleton ../create/c/for/this.h
|
||||
```
|
||||
|
||||
Using CMock Without Ceedling
|
||||
----------------------------
|
||||
|
||||
CMock depends on the Unity test framework, but it does not *require* Ceedling. You can use the
|
||||
generated mocks directly with the Unity test framework in whatever build system you prefer. One
|
||||
important thing to remember when doing this is that you will need to call the `_Init` function
|
||||
for each of your mocks BEFORE the tests and the `_Verify` function for each mock AFTER each test.
|
||||
This allows CMock to perform all of its internal accounting. If you're running into problems where
|
||||
some errors aren't getting caught, this is likely what you are missing.
|
||||
|
||||
There are many ways to accomplish this. Any is valid:
|
||||
|
||||
- These actions can be performed as part of `setUp` and `tearDown` in each test file
|
||||
- You can hand-write your own RUN_TEST macro. If so, protect `_Verify` calls in `TEST_PROTECT`
|
||||
- You can use Unity's test runner generator and it will automatically take care of this for you.
|
||||
- You can use Ceedling and it will automatically take care of this for you.
|
||||
|
||||
Config Options:
|
||||
---------------
|
||||
|
||||
|
||||
@@ -16,6 +16,17 @@ class CMockFileWriter
|
||||
require 'fileutils'
|
||||
FileUtils.mkdir_p "#{@config.mock_path}/" unless Dir.exist?("#{@config.mock_path}/")
|
||||
FileUtils.mkdir_p "#{@config.mock_path}/#{"#{subdir}/" if subdir}" if subdir && !Dir.exist?("#{@config.mock_path}/#{"#{subdir}/" if subdir}")
|
||||
rescue SystemCallError => e
|
||||
raise "Unable to create mock output directory: #{e.message}. Check :mock_path ('#{@config.mock_path}') configuration."
|
||||
end
|
||||
|
||||
def create_skeleton_subdir(subdir)
|
||||
require 'fileutils'
|
||||
base = effective_skeleton_path
|
||||
FileUtils.mkdir_p base
|
||||
FileUtils.mkdir_p "#{base}/#{subdir}" if subdir
|
||||
rescue SystemCallError => e
|
||||
raise "Unable to create skeleton output directory: #{e.message}. Check :skeleton_path ('#{base}') configuration."
|
||||
end
|
||||
|
||||
def create_file(filename, subdir)
|
||||
@@ -27,6 +38,27 @@ class CMockFileWriter
|
||||
yield(file, filename)
|
||||
end
|
||||
update_file(full_file_name_done, full_file_name_temp)
|
||||
rescue SystemCallError => e
|
||||
raise "Unable to write mock file '#{full_file_name_done}': #{e.message}. Check :mock_path ('#{@config.mock_path}') and :subdir ('#{subdir}') configuration."
|
||||
end
|
||||
|
||||
def create_skeleton_file(filename, subdir)
|
||||
raise "Where's the block of data to create?" unless block_given?
|
||||
|
||||
base = effective_skeleton_path
|
||||
full_file_name_temp = "#{base}/#{"#{subdir}/" if subdir}#{filename}.new"
|
||||
full_file_name_done = "#{base}/#{"#{subdir}/" if subdir}#{filename}"
|
||||
File.open(full_file_name_temp, 'w') do |file|
|
||||
yield(file, filename)
|
||||
end
|
||||
update_file(full_file_name_done, full_file_name_temp)
|
||||
rescue SystemCallError => e
|
||||
raise "Unable to write skeleton file '#{full_file_name_done}': #{e.message}. Check :skeleton_path ('#{base}') and :subdir ('#{subdir}') configuration."
|
||||
end
|
||||
|
||||
def skeleton_file_path(filename, subdir)
|
||||
base = effective_skeleton_path
|
||||
"#{base}/#{"#{subdir}/" if subdir}#{filename}"
|
||||
end
|
||||
|
||||
def append_file(filename, subdir)
|
||||
@@ -40,6 +72,11 @@ class CMockFileWriter
|
||||
|
||||
private ###################################
|
||||
|
||||
def effective_skeleton_path
|
||||
path = @config.skeleton_path
|
||||
path.nil? || path.empty? ? @config.mock_path : path
|
||||
end
|
||||
|
||||
def update_file(dest, src)
|
||||
require 'fileutils'
|
||||
FileUtils.rm(dest, :force => true)
|
||||
|
||||
+27
-20
@@ -85,6 +85,7 @@ class CMockGenerator
|
||||
:skeleton => true
|
||||
}
|
||||
|
||||
@file_writer.create_skeleton_subdir(@subdir)
|
||||
create_skeleton_source_file(mock_project)
|
||||
end
|
||||
|
||||
@@ -133,9 +134,9 @@ class CMockGenerator
|
||||
end
|
||||
|
||||
def create_skeleton_source_file(mock_project)
|
||||
filename = "#{@config.mock_path}/#{"#{@subdir}/" if @subdir}#{mock_project[:module_name]}.c"
|
||||
filename = @file_writer.skeleton_file_path("#{mock_project[:module_name]}.c", @subdir)
|
||||
existing = File.exist?(filename) ? File.read(filename) : ''
|
||||
@file_writer.create_file("#{mock_project[:module_name]}.c", @subdir) do |file, fullname|
|
||||
@file_writer.create_skeleton_file("#{mock_project[:module_name]}.c", @subdir) do |file, fullname|
|
||||
blank_project = mock_project.clone
|
||||
blank_project[:parsed_stuff] = { :functions => [] }
|
||||
if existing.empty?
|
||||
@@ -210,24 +211,30 @@ class CMockGenerator
|
||||
|
||||
def create_source_header_section(file, filename, mock_project)
|
||||
header_file = (mock_project[:folder] || '') + filename.sub(/.*\K\.c/, mock_project[:module_ext])
|
||||
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless mock_project[:parsed_stuff][:functions].empty?
|
||||
file << "#include <string.h>\n"
|
||||
file << "#include <stdlib.h>\n"
|
||||
unless @exclude_setjmp_h
|
||||
file << "#include <setjmp.h>\n"
|
||||
end
|
||||
file << "#include \"cmock.h\"\n"
|
||||
@includes_c_pre_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "#include \"#{header_file}\"\n"
|
||||
@includes_c_post_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "\n"
|
||||
strs = []
|
||||
mock_project[:parsed_stuff][:functions].each do |func|
|
||||
strs << func[:name]
|
||||
func[:args].each { |arg| strs << arg[:name] }
|
||||
end
|
||||
strs.uniq.sort.each do |str|
|
||||
file << "static const char* CMockString_#{str} = \"#{str}\";\n"
|
||||
if mock_project[:skeleton]
|
||||
@includes_c_pre_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "#include \"#{header_file}\"\n"
|
||||
@includes_c_post_header.each { |inc| file << "#include #{inc}\n" }
|
||||
else
|
||||
file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless mock_project[:parsed_stuff][:functions].empty?
|
||||
file << "#include <string.h>\n"
|
||||
file << "#include <stdlib.h>\n"
|
||||
unless @exclude_setjmp_h
|
||||
file << "#include <setjmp.h>\n"
|
||||
end
|
||||
file << "#include \"cmock.h\"\n"
|
||||
@includes_c_pre_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "#include \"#{header_file}\"\n"
|
||||
@includes_c_post_header.each { |inc| file << "#include #{inc}\n" }
|
||||
file << "\n"
|
||||
strs = []
|
||||
mock_project[:parsed_stuff][:functions].each do |func|
|
||||
strs << func[:name]
|
||||
func[:args].each { |arg| strs << arg[:name] }
|
||||
end
|
||||
strs.uniq.sort.each do |str|
|
||||
file << "static const char* CMockString_#{str} = \"#{str}\";\n"
|
||||
end
|
||||
end
|
||||
file << "\n"
|
||||
end
|
||||
|
||||
@@ -253,12 +253,23 @@ class CMockHeaderParser
|
||||
# enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them
|
||||
# forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes
|
||||
source.gsub!(/^[\w\s]*struct[^;{}()]+;/m, '') # remove forward declared structs
|
||||
source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^}]+\}[\w\s*,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces
|
||||
source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s()]*\{[^}]+\}[\w\s*,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces
|
||||
# remove problem keywords
|
||||
source.gsub!(/(\W)(?:register|auto|restrict)(\W)/, '\1\2')
|
||||
source.gsub!(/(\W)(?:static)(\W)/, '\1\2') unless cpp
|
||||
|
||||
source.gsub!(/\s*=\s*['"a-zA-Z0-9_.]+\s*/, '') # remove default value statements from argument lists
|
||||
|
||||
# strip macro decorator patterns that cannot be C function prototypes.
|
||||
# must run after default-value removal so "= \"str\"" doesn't trigger string detection.
|
||||
# neutralize string literals (never valid in C prototype args), eliminating any parentheses
|
||||
# inside string content (e.g. "msg()") that would fool subsequent brace-matching.
|
||||
source.gsub!(/"[^"]*"/, '""')
|
||||
# strip WORD("") -- any call with a string literal arg cannot be a C function prototype
|
||||
source.gsub!(/\b\w+\s*\([^)]*""[^)]*\)/, '')
|
||||
# strip WORD(N...) -- any call whose first arg starts with a digit cannot be a C prototype
|
||||
source.gsub!(/\b\w+\s*\(\s*\d[^)]*\)/, '')
|
||||
|
||||
source.gsub!(/^(?:[\w\s]*\W)?typedef\W[^;]*/m, '') # remove typedef statements
|
||||
source.gsub!(/\)(\w)/, ') \1') # add space between parenthese and alphanumeric
|
||||
source.gsub!(/(^|\W+)(?:#{@c_strippables.join('|')})(?=$|\W+)/, '\1') unless @c_strippables.empty? # remove known attributes slated to be stripped
|
||||
@@ -636,6 +647,7 @@ class CMockHeaderParser
|
||||
|
||||
rettype = parsed[:type]
|
||||
rettype = 'void' if @local_as_void.include?(rettype.strip)
|
||||
rettype = 'void' if rettype.empty? && !(@standards + @local_as_void).include?(parsed[:name]) # all return-type tokens were stripped (e.g. bare decorator macros)
|
||||
retstr = parsed[:const_ptr?] ? "#{rettype} const" : rettype
|
||||
decl[:return] = { :type => rettype,
|
||||
:name => 'cmock_to_return',
|
||||
|
||||
@@ -15,3 +15,6 @@
|
||||
:treat_as_void:
|
||||
- OSEK_TASK
|
||||
- VOID_TYPE_CRAZINESS
|
||||
:strippables:
|
||||
- SAMPLE_EXTERN
|
||||
- SAMPLE_MODE
|
||||
|
||||
@@ -18,6 +18,16 @@ typedef struct _POINT_T
|
||||
int y;
|
||||
} POINT_T;
|
||||
|
||||
/* The comments in the following enum are important, as are the newlines and commas */
|
||||
/* This combination caused a curious bug when used together. Make sure it doesn't come back. */
|
||||
typedef enum
|
||||
{
|
||||
MY_ERROR_ID = -18,/**< Driver not ready */
|
||||
MY_OTHER_ID = -19 /**< Node-id is in LSS unconfigured
|
||||
state. If objects are handled properly,
|
||||
his may not be an error. */
|
||||
} MY_STATE_ID_T;
|
||||
|
||||
/* typedef edge case;
|
||||
not ANSI C but it has been done and will break cmock if not handled */
|
||||
typedef void VOID_TYPE_CRAZINESS;
|
||||
@@ -101,3 +111,19 @@ inline int stuff(int num)
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* it seems like CMock didn't love a func-looking struct before a func */
|
||||
#define FOO_TYPE(a) foo_##a
|
||||
struct FOO_TYPE(bar) { int baz; };
|
||||
char b(void);
|
||||
|
||||
/* Here are more macros that sorta look like functions. Only sample_func is real */
|
||||
#define SAMPLE_EXTERN
|
||||
#define SAMPLE_MODE
|
||||
#define SAMPLE_DEPRECATED(a,b)
|
||||
struct struct_a;
|
||||
struct struct_b;
|
||||
SAMPLE_EXTERN SAMPLE_MODE SAMPLE_DEPRECATED(1.1.2, "It was bad. real bad()")
|
||||
void sample_func(struct struct_a **a,
|
||||
struct struct_b **b,
|
||||
...);
|
||||
|
||||
Reference in New Issue
Block a user