You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

373 lines
14 KiB

2 years ago
  1. /*****************************************************************************
  2. *
  3. * CLIPP - command line interfaces for modern C++
  4. *
  5. * released under MIT license
  6. *
  7. * (c) 2017-2018 André Müller; foss@andremueller-online.de
  8. *
  9. *****************************************************************************/
  10. #include "testing.h"
  11. using namespace clipp;
  12. //-------------------------------------------------------------------
  13. void test(int lineNo, const doc_formatting& fmt,
  14. const group& cli, const std::string& expected)
  15. {
  16. auto us = usage_lines(cli,fmt).str();
  17. // std::cout << "\n======================================\n\n" << us << "\n";
  18. // return;
  19. if(us != expected) {
  20. std::cout << "\nEXPECTED:\n" << expected << "\nOBSERVED:\n" << us << '\n';
  21. throw std::runtime_error{"failed " + std::string( __FILE__ ) +
  22. " in line " + std::to_string(lineNo)};
  23. }
  24. }
  25. //-------------------------------------------------------------------
  26. int main()
  27. {
  28. try {
  29. auto fmt = doc_formatting{}
  30. .first_column(3)
  31. .doc_column(9)
  32. .last_column(80)
  33. .empty_label("?")
  34. .param_separator("%")
  35. .group_separator("#")
  36. .alternative_param_separator("|")
  37. .alternative_group_separator("^")
  38. .flag_separator("_")
  39. .surround_labels("<", ">")
  40. .surround_optional("[", "]")
  41. .surround_repeat(",,,", "...")
  42. .surround_alternatives("§(§", "§)§")
  43. .surround_group("(", ")")
  44. .surround_joinable("$($", "$)$")
  45. .max_flags_per_param_in_doc(2)
  46. .max_flags_per_param_in_usage(1)
  47. .line_spacing(0)
  48. .paragraph_spacing(3)
  49. .alternatives_min_split_size(3)
  50. .merge_alternative_flags_with_common_prefix(true)
  51. .merge_joinable_with_common_prefix(true)
  52. .split_alternatives(true);
  53. test(__LINE__, fmt, group( option("-a") ), " [-a]");
  54. test(__LINE__, fmt, group( option("-a", "--all") ), " [-a]");
  55. test(__LINE__, fmt, group( option("-a").repeatable(true) ), " ,,,[-a]...");
  56. test(__LINE__, fmt, group( required("-a") ), " -a");
  57. test(__LINE__, fmt, group( required("-a", "--all") ), " -a");
  58. test(__LINE__, fmt, group( required("-a").repeatable(true) ), " ,,,-a...");
  59. test(__LINE__, fmt, group( command("-a") ), " -a");
  60. test(__LINE__, fmt, group( command("-a", "--all") ), " -a");
  61. test(__LINE__, fmt, group( command("-a").repeatable(true) ), " ,,,-a...");
  62. test(__LINE__, fmt, (
  63. value("geometry file"),
  64. option("-translate") & value("x") & value("y") & value("z"),
  65. option("-rotate") & value("azimuth") & value("polar")),
  66. " <geometry file>#[-translate%<x>%<y>%<z>]#[-rotate%<azimuth>%<polar>]");
  67. test(__LINE__, fmt, joinable( values("string")), " ,,,<string>...");
  68. test(__LINE__, fmt, (
  69. (option("-o", "--out") & value("output file")) % "output filename",
  70. with_prefix("-f", option("align") >> []{} | option("noalign") >> []{} ) % "control alignment"),
  71. " [-o%<output file>]#[-f§(§align|noalign§)§]");
  72. test(__LINE__, fmt, (
  73. (option("-n", "--count") & value("count")) % "number of iterations",
  74. (option("-r", "--ratio") & value("ratio")) % "compression ratio",
  75. (option("-m") & opt_value("lines=5")) % "merge lines (default: 5)"),
  76. " [-n%<count>]#[-r%<ratio>]#[-m%[<lines=5>]]");
  77. test(__LINE__, fmt, (
  78. "input files" % values("file"),
  79. "compress results" % option("-c", "--compress"),
  80. "lines to be ignored" % repeatable( option("-i", "--ignore") & integers("line") )),
  81. " ,,,<file>...#[-c]#,,,[-i%,,,<line>...]...");
  82. test(__LINE__, fmt, (
  83. value("file").if_missing([]{}).if_repeated([](int){}),
  84. required("-t") & values("target").if_missing([]{}).if_blocked([]{}),
  85. option("--http") | option("--ftp").if_conflicted([]{}),
  86. any_other()),
  87. " <file>#-t%,,,<target>...#[--§(§http|ftp§)§]#,,,[<?>]...");
  88. test(__LINE__, fmt, (
  89. option("-r", "--recursive") % "recurse into subdirectories",
  90. (required("-i", "--in" ) & value("input dir")) % "sets path to input directory",
  91. (required("-o", "--out") & value("output dir")) % "sets path to output directory"),
  92. " [-r]#-i%<input dir>#-o%<output dir>");
  93. test(__LINE__, fmt, (
  94. values("directory") % "input files",
  95. option("-c", "--compress") % "compress results",
  96. (option("-i", "--ignore") & values("line")) % "lines to be ignored"),
  97. " ,,,<directory>...#[-c]#[-i%,,,<line>...]");
  98. test(__LINE__, fmt, (
  99. value("infile") % "input filename",
  100. value("outfile") % "output filename",
  101. option("-s", "--split") % "split files" ),
  102. " <infile>%<outfile>%[-s]");
  103. test(__LINE__, fmt, (
  104. option("-a") % "activates a",
  105. option("-b") % "activates b",
  106. option("-c", "--noc") % "deactivates c",
  107. option("--hi")([]{}) % "says hi"),
  108. " [-a]%[-b]%[-c]%[--hi]");
  109. test(__LINE__, fmt, (
  110. command("make"),
  111. value("file") % "name of file to make",
  112. option("-f", "--force") % "overwrite existing file"),
  113. " make%<file>%[-f]");
  114. test(__LINE__, fmt, (
  115. option("-a") % "activates a",
  116. option("-b") % "activates b",
  117. option("-c", "--noc") % "deactivates c",
  118. option("--hi")([]{}) % "says hi"),
  119. " [-a]%[-b]%[-c]%[--hi]");
  120. test(__LINE__, fmt,
  121. joinable(repeatable( option(",") , opt_number("number"))),
  122. " ,,,([,]%[<number>])...");
  123. test(__LINE__, fmt, (
  124. ( command("ship"), ( ( command("new"), values("name") ) |
  125. ( value("name"),
  126. command("move"), value("x"), value("y"), (option("--speed=") & value("kn")) % "Speed in knots [default: 10]") |
  127. ( command("shoot"), value("x"), value("y") ) ) )
  128. | ( command("mine"),
  129. ( command("set" )
  130. | command("remove") ),
  131. value("x"), value("y"),
  132. ( option("--moored" ) % "Moored (anchored) mine."
  133. | option("--drifting") % "Drifting mine.")
  134. )
  135. | command("-h", "--help") % "Show this screen."
  136. | command("--version")([]{}) % "Show version."),
  137. " ship#new%,,,<name>...\n"
  138. " ship#<name>#move#<x>#<y>#[--speed=%<kn>]\n"
  139. " ship#shoot%<x>%<y>\n"
  140. " mine#§(§set|remove§)§#<x>#<y>#[--§(§moored|drifting§)§]\n"
  141. " -h\n"
  142. " --version");
  143. test(__LINE__, fmt, ( command("help")
  144. | ( command("build"),
  145. ( command("new") | command("add") ),
  146. values("file"),
  147. option("-v", "--verbose") % "print detailed report",
  148. option("-b", "--buffer") & opt_value(
  149. "size=10") % "sets buffer size in KiByte",
  150. ( option("--init") | option("--no-init") ) % "do or don't initialize"
  151. )
  152. | ( command("query"), value("infile"),
  153. required("-o", "--out") & value("outfile"),
  154. option("-f", "--out-format") % "determine output format"
  155. & value("format")
  156. )),
  157. " help\n"
  158. " build#§(§new|add§)§#,,,<file>...#[-v]#[-b%[<size=10>]]#[--§(§init|no-init§)§]\n"
  159. " query#<infile>#-o%<outfile>#[-f%<format>]");
  160. test(__LINE__, fmt, (
  161. value("file"),
  162. joinable(
  163. option("-r") % "open read-only",
  164. option("-b") % "use backup file",
  165. option("-s") % "use swap file"
  166. ),
  167. joinable(
  168. option(":vim") >> []{}, option(":st3") >> []{},
  169. option(":atom") >> []{}, option(":emacs") >> []{}
  170. ) % "editor(s) to use; multiple possible"),
  171. " <file>#[-rbs]#$($[:vim]%[:st3]%[:atom]%[:emacs]$)$");
  172. test(__LINE__, fmt, (
  173. (option("x") % "sets X", option("y") % "sets Y"),
  174. (option("a") % "activates A", option("b") % "activates B") % "documented group 1:" ,
  175. "documented group 2:" % (option("-g") % "activates G", option("-h") % "activates H"),
  176. "activates E or F" % (option("-e"), option("-f")))
  177. ,
  178. " [x]#[y]#[a]%[b]#[-g]%[-h]#[-e]%[-f]");
  179. test(__LINE__, fmt, ( ( command("make"), value("wordfile"), required("-dict") & value("dictionary"), option("--progress", "-p")) | (
  180. command("find"), values("infile"), required("-dict") & value("dictionary"),
  181. (option("-o", "--output") & value("outfile")) % "write to file instead of stdout",
  182. ( option("-split" ) | option("-nosplit"))) | command("help"),
  183. option("-v", "--version").call([]{}) % "show version")
  184. ,
  185. " make#<wordfile>#-dict%<dictionary>#[--progress]#[-v]\n"
  186. " find#,,,<infile>...#-dict%<dictionary>#[-o%<outfile>]#[-§(§split|nosplit§)§]#[-v]\n"
  187. " help#[-v]");
  188. test(__LINE__, fmt, (
  189. command("help") |
  190. ( command("build"),
  191. "build commands" %
  192. ( command("new") % "make new database"
  193. | command("add") % "append to existing database"
  194. ),
  195. value("file")
  196. ) |
  197. ( command("query"),
  198. "query settings" %
  199. ( required("-i", "--input") & value("infile") % "input file",
  200. option("-p", "--pretty-print") % "human friendly output")
  201. ) |
  202. ( command("info"),
  203. "database info modes" % (
  204. command("space") % "detailed memory occupation analysis" |
  205. (
  206. command("statistics"),
  207. "statistics analysis" % (
  208. command("words") % "word frequency table" |
  209. command("chars") % "character frequency table"
  210. )
  211. )
  212. )
  213. ) |
  214. "remove mode" % (
  215. command("remove"),
  216. "modify" % ( command("any") | command("all") ),
  217. value("regex") % "regular expression filter"
  218. ) |
  219. ( command("modify"),
  220. "modification opererations" % (
  221. option("-c", "--compress") % "compress database in-memory",
  222. option("-u", "--unique") % "keep only unique entries",
  223. option("-m", "--memlimit") % "max. size in RAM" & value("size")
  224. )
  225. )
  226. ),
  227. " help\n"
  228. " build#§(§new|add§)§#<file>\n"
  229. " query#-i%<infile>#[-p]\n"
  230. " info#space\n"
  231. " info#statistics#§(§words|chars§)§\n"
  232. " remove#a§(§ny|ll§)§#<regex>\n"
  233. " modify#[-c]#[-u]#[-m%<size>]");
  234. test(__LINE__, fmt, (
  235. value("input file"),
  236. option("-r", "--recursive"),
  237. option("-o") & value("output format"),
  238. option("-utf16")),
  239. " <input file>#[-r]#[-o%<output format>]#[-utf16]");
  240. test(__LINE__, fmt,
  241. joinable( repeatable(option("a")([]{})|option("b")([]{})) ),
  242. " ,,,[a|b]...");
  243. test(__LINE__, fmt, (
  244. "user interface options:" % (
  245. option("-v", "--verbose") % "show detailed output",
  246. option("-i", "--interactive") % "use interactive mode"
  247. ),
  248. "copy mode:" % (
  249. command("copy") | command("move"),
  250. option("--all") % "copy all",
  251. option("--replace") % "replace existing files",
  252. option("-f", "--force") % "don't ask for confirmation"
  253. ) |
  254. "compare mode:" % (
  255. command("compare"),
  256. (command("date") | command("content")),
  257. option("-b", "--binary") % "compare files byte by byte",
  258. option("-q", "--quick") % "use heuristics for faster comparison"
  259. ) |
  260. "merge mode:" % (
  261. command("merge"),
  262. (
  263. command("diff") % "merge using diff" |
  264. command("patch") % "merge using patch" |
  265. ( command("content") % "merge based on content",
  266. "content based merge options:" % (
  267. option("--git-style") % "emulate git's merge behavior",
  268. option("-m", "--marker") & value("marker") % "merge marker symbol"
  269. )
  270. )
  271. ),
  272. required("-o") & value("outdir") % "target directory for merge result",
  273. option("--show-conflicts") % "show merge conflicts during run"
  274. ) |
  275. command("list"),
  276. "mode-independent options:" % (
  277. values("files") % "input files",
  278. option("-r", "--recursive") % "descend into subdirectories",
  279. option("-h", "--help") % "show help"
  280. )
  281. ),
  282. " [-v]%[-i]#§(§copy|move§)§#[--all]#[--replace]#[-f]#,,,<files>...%[-r]%[-h]\n"
  283. " [-v]%[-i]#compare#§(§date|content§)§#[-b]#[-q]#,,,<files>...%[-r]%[-h]\n"
  284. " [-v]%[-i]#merge#§(§diff|patch§)§#-o%<outdir>#[--show-conflicts]#,,,<files>...%[-r]%[-h]\n"
  285. " [-v]%[-i]#merge#content#[--git-style]#[-m%<marker>]#-o%<outdir>#[--show-conflicts]#,,,<files>...%[-r]%[-h]\n"
  286. " [-v]%[-i]#list#,,,<files>...%[-r]%[-h]");
  287. test(__LINE__, fmt, (
  288. command("new"),
  289. value("filename"),
  290. (option("-e", "--encoding") & value("enc")).doc("'utf8' or 'cp1252'")
  291. ), " new#<filename>#[-e%<enc>]");
  292. test(__LINE__, fmt, ( command("auto") | ( command("label"), value("character") )),
  293. " §(§auto^(label%<character>)§)§");
  294. test(__LINE__, fmt, (
  295. values("file") % "input filenames",
  296. (required("-s") & value("expr")) % "string to look for",
  297. option("any") % "report as soon as any matches" |
  298. option("all") % "report only if all match"
  299. ),
  300. " ,,,<file>...#-s%<expr>#[a§(§ny|ll§)§]");
  301. test(__LINE__, fmt, ( value(""), option("-x"), option("-y") ),
  302. " <?>%[-x]%[-y]");
  303. }
  304. catch(std::exception& e) {
  305. std::cerr << e.what() << std::endl;
  306. return 1;
  307. }
  308. }