ESP32-basiertes Kraftort-Suchger�t mit GPS, LED-Ring und PlatformIO-Firmware.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

319 wiersze
11KB

  1. # =========================================================================
  2. # Unity - A Test Framework for C
  3. # ThrowTheSwitch.org
  4. # Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams
  5. # SPDX-License-Identifier: MIT
  6. # =========================================================================
  7. # This script creates all the files with start code necessary for a new module.
  8. # A simple module only requires a source file, header file, and test file.
  9. # Triad modules require a source, header, and test file for each triad type (like model, conductor, and hardware).
  10. require 'rubygems'
  11. require 'fileutils'
  12. require 'pathname'
  13. # TEMPLATE_TST
  14. TEMPLATE_TST ||= '#ifdef %5$s
  15. #include "unity.h"
  16. %2$s#include "%1$s.h"
  17. void setUp(void)
  18. {
  19. }
  20. void tearDown(void)
  21. {
  22. }
  23. void test_%4$s_NeedToImplement(void)
  24. {
  25. TEST_IGNORE_MESSAGE("Need to Implement %1$s");
  26. }
  27. #endif // %5$s
  28. '.freeze
  29. # TEMPLATE_SRC
  30. TEMPLATE_SRC ||= '%2$s#include "%1$s.h"
  31. '.freeze
  32. # TEMPLATE_INC
  33. TEMPLATE_INC ||= '#ifndef %3$s_H
  34. #define %3$s_H
  35. %2$s
  36. #endif // %3$s_H
  37. '.freeze
  38. class UnityModuleGenerator
  39. ############################
  40. def initialize(options = nil)
  41. @options = UnityModuleGenerator.default_options
  42. case options
  43. when NilClass then @options
  44. when String then @options.merge!(UnityModuleGenerator.grab_config(options))
  45. when Hash then @options.merge!(options)
  46. else raise 'If you specify arguments, it should be a filename or a hash of options'
  47. end
  48. # Create default file paths if none were provided
  49. @options[:path_src] = "#{__dir__}/../src/" if @options[:path_src].nil?
  50. @options[:path_inc] = @options[:path_src] if @options[:path_inc].nil?
  51. @options[:path_tst] = "#{__dir__}/../test/" if @options[:path_tst].nil?
  52. @options[:path_src] += '/' unless @options[:path_src][-1] == 47
  53. @options[:path_inc] += '/' unless @options[:path_inc][-1] == 47
  54. @options[:path_tst] += '/' unless @options[:path_tst][-1] == 47
  55. # Built in patterns
  56. @patterns = {
  57. 'src' => {
  58. '' => { inc: [] }
  59. },
  60. 'test' => {
  61. '' => { inc: [] }
  62. },
  63. 'dh' => {
  64. 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h')] },
  65. 'Hardware' => { inc: [] }
  66. },
  67. 'dih' => {
  68. 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h'), create_filename('%1$s', 'Interrupt.h')] },
  69. 'Interrupt' => { inc: [create_filename('%1$s', 'Hardware.h')] },
  70. 'Hardware' => { inc: [] }
  71. },
  72. 'mch' => {
  73. 'Model' => { inc: [] },
  74. 'Conductor' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'Hardware.h')] },
  75. 'Hardware' => { inc: [] }
  76. },
  77. 'mvp' => {
  78. 'Model' => { inc: [] },
  79. 'Presenter' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'View.h')] },
  80. 'View' => { inc: [] }
  81. }
  82. }
  83. end
  84. ############################
  85. def self.default_options
  86. {
  87. pattern: 'src',
  88. includes: {
  89. src: [],
  90. inc: [],
  91. tst: []
  92. },
  93. update_svn: false,
  94. boilerplates: {},
  95. test_prefix: 'Test',
  96. mock_prefix: 'Mock',
  97. test_define: 'TEST'
  98. }
  99. end
  100. ############################
  101. def self.grab_config(config_file)
  102. options = default_options
  103. unless config_file.nil? || config_file.empty?
  104. require_relative 'yaml_helper'
  105. yaml_guts = YamlHelper.load_file(config_file)
  106. options.merge!(yaml_guts[:unity] || yaml_guts[:cmock])
  107. raise "No :unity or :cmock section found in #{config_file}" unless options
  108. end
  109. options
  110. end
  111. ############################
  112. def files_to_operate_on(module_name, pattern = nil)
  113. # strip any leading path information from the module name and save for later
  114. subfolder = File.dirname(module_name)
  115. module_name = File.basename(module_name)
  116. # create triad definition
  117. prefix = @options[:test_prefix] || 'Test'
  118. triad = [{ ext: '.c', path: @options[:path_src], prefix: '', template: TEMPLATE_SRC, inc: :src, boilerplate: @options[:boilerplates][:src] },
  119. { ext: '.h', path: @options[:path_inc], prefix: '', template: TEMPLATE_INC, inc: :inc, boilerplate: @options[:boilerplates][:inc] },
  120. { ext: '.c', path: @options[:path_tst], prefix: prefix, template: TEMPLATE_TST, inc: :tst, boilerplate: @options[:boilerplates][:tst], test_define: @options[:test_define] }]
  121. # prepare the pattern for use
  122. pattern = (pattern || @options[:pattern] || 'src').downcase
  123. patterns = @patterns[pattern]
  124. raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil?
  125. # single file patterns (currently just 'test') can reject the other parts of the triad
  126. triad.select! { |v| v[:inc] == :tst } if pattern == 'test'
  127. # Assemble the path/names of the files we need to work with.
  128. files = []
  129. triad.each do |cfg|
  130. patterns.each_pair do |pattern_file, pattern_traits|
  131. submodule_name = create_filename(module_name, pattern_file)
  132. filename = cfg[:prefix] + submodule_name + cfg[:ext]
  133. files << {
  134. path: (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath,
  135. name: submodule_name,
  136. template: cfg[:template],
  137. test_define: cfg[:test_define],
  138. boilerplate: cfg[:boilerplate],
  139. includes: case (cfg[:inc])
  140. when :src then (@options[:includes][:src] || []) | (pattern_traits[:inc].map { |f| format(f, module_name) })
  141. when :inc then @options[:includes][:inc] || []
  142. when :tst then (@options[:includes][:tst] || []) | (pattern_traits[:inc].map { |f| format("#{@options[:mock_prefix]}#{f}", module_name) })
  143. end
  144. }
  145. end
  146. end
  147. files
  148. end
  149. ############################
  150. def neutralize_filename(name, start_cap: true)
  151. return name if name.empty?
  152. name = name.split(/(?:\s+|_|(?=[A-Z][a-z]))|(?<=[a-z])(?=[A-Z])/).map(&:capitalize).join('_')
  153. name = name[0].downcase + name[1..] unless start_cap
  154. name
  155. end
  156. ############################
  157. def create_filename(part1, part2 = '')
  158. name = part2.empty? ? part1 : "#{part1}_#{part2}"
  159. case (@options[:naming])
  160. when 'bumpy' then neutralize_filename(name, start_cap: false).delete('_')
  161. when 'camel' then neutralize_filename(name).delete('_')
  162. when 'snake' then neutralize_filename(name).downcase
  163. when 'caps' then neutralize_filename(name).upcase
  164. else name
  165. end
  166. end
  167. ############################
  168. def generate(module_name, pattern = nil)
  169. files = files_to_operate_on(module_name, pattern)
  170. # Abort if all of the module files already exist
  171. all_files_exist = true
  172. files.each do |file|
  173. all_files_exist = false unless File.exist?(file[:path])
  174. end
  175. raise "ERROR: File #{files[0][:name]} already exists. Exiting." if all_files_exist
  176. # Create Source Modules
  177. files.each_with_index do |file, _i|
  178. # If this file already exists, don't overwrite it.
  179. if File.exist?(file[:path])
  180. puts "File #{file[:path]} already exists!"
  181. next
  182. end
  183. # Create the path first if necessary.
  184. FileUtils.mkdir_p(File.dirname(file[:path]), verbose: false)
  185. File.open(file[:path], 'w') do |f|
  186. f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil?
  187. f.write(file[:template] % [file[:name],
  188. file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join,
  189. file[:name].upcase.tr('-', '_'),
  190. file[:name].tr('-', '_'),
  191. file[:test_define]])
  192. end
  193. if @options[:update_svn]
  194. `svn add \"#{file[:path]}\"`
  195. if $!.exitstatus.zero?
  196. puts "File #{file[:path]} created and added to source control"
  197. else
  198. puts "File #{file[:path]} created but FAILED adding to source control!"
  199. end
  200. else
  201. puts "File #{file[:path]} created"
  202. end
  203. end
  204. puts 'Generate Complete'
  205. end
  206. ############################
  207. def destroy(module_name, pattern = nil)
  208. files_to_operate_on(module_name, pattern).each do |filespec|
  209. file = filespec[:path]
  210. if File.exist?(file)
  211. if @options[:update_svn]
  212. `svn delete \"#{file}\" --force`
  213. puts "File #{file} deleted and removed from source control"
  214. else
  215. FileUtils.remove(file)
  216. puts "File #{file} deleted"
  217. end
  218. else
  219. puts "File #{file} does not exist so cannot be removed."
  220. end
  221. end
  222. puts 'Destroy Complete'
  223. end
  224. end
  225. ############################
  226. # Handle As Command Line If Called That Way
  227. if $0 == __FILE__
  228. destroy = false
  229. options = {}
  230. module_name = nil
  231. # Parse the command line parameters.
  232. ARGV.each do |arg|
  233. case arg
  234. when /^-d/ then destroy = true
  235. when /^-u/ then options[:update_svn] = true
  236. when /^-p"?(\w+)"?/ then options[:pattern] = Regexp.last_match(1)
  237. when /^-s"?(.+)"?/ then options[:path_src] = Regexp.last_match(1)
  238. when /^-i"?(.+)"?/ then options[:path_inc] = Regexp.last_match(1)
  239. when /^-t"?(.+)"?/ then options[:path_tst] = Regexp.last_match(1)
  240. when /^-n"?(.+)"?/ then options[:naming] = Regexp.last_match(1)
  241. when /^-y"?(.+)"?/ then options = UnityModuleGenerator.grab_config(Regexp.last_match(1))
  242. when /^(\w+)/
  243. raise "ERROR: You can't have more than one Module name specified!" unless module_name.nil?
  244. module_name = arg
  245. when /^-(h|-help)/
  246. ARGV = [].freeze
  247. else
  248. raise "ERROR: Unknown option specified '#{arg}'"
  249. end
  250. end
  251. unless ARGV[0]
  252. puts ["\nGENERATE MODULE\n-------- ------",
  253. "\nUsage: ruby generate_module [options] module_name",
  254. " -i\"include\" sets the path to output headers to 'include' (DEFAULT ../src)",
  255. " -s\"../src\" sets the path to output source to '../src' (DEFAULT ../src)",
  256. " -t\"C:/test\" sets the path to output source to 'C:/test' (DEFAULT ../test)",
  257. ' -p"MCH" sets the output pattern to MCH.',
  258. ' dh - driver hardware.',
  259. ' dih - driver interrupt hardware.',
  260. ' mch - model conductor hardware.',
  261. ' mvp - model view presenter.',
  262. ' src - just a source module, header and test. (DEFAULT)',
  263. ' test - just a test file.',
  264. ' -d destroy module instead of creating it.',
  265. ' -n"camel" sets the file naming convention.',
  266. ' bumpy - BumpyCaseFilenames.',
  267. ' camel - camelCaseFilenames.',
  268. ' snake - snake_case_filenames.',
  269. ' caps - CAPS_CASE_FILENAMES.',
  270. ' -u update subversion too (requires subversion command line)',
  271. ' -y"my.yml" selects a different yaml config file for module generation',
  272. ''].join("\n")
  273. exit
  274. end
  275. raise 'ERROR: You must have a Module name specified! (use option -h for help)' if module_name.nil?
  276. if destroy
  277. UnityModuleGenerator.new(options).destroy(module_name)
  278. else
  279. UnityModuleGenerator.new(options).generate(module_name)
  280. end
  281. end