printf.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. * Copyright (C) 2024 Puter Technologies Inc.
  3. *
  4. * This file is part of Phoenix Shell.
  5. *
  6. * Phoenix Shell is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published
  8. * by the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import assert from 'assert';
  20. import { MakeTestContext } from './harness.js'
  21. import builtins from '../../src/puter-shell/coreutils/__exports__.js';
  22. export const runPrintfTests = () => {
  23. describe('printf', function () {
  24. const testCases = [
  25. {
  26. description: 'outputs format verbatim if no operands were given',
  27. input: [ 'hello' ],
  28. expectedStdout: 'hello',
  29. expectedStderr: '',
  30. },
  31. {
  32. description: 'outputs octal escape sequences',
  33. input: [ '\\0\\41\\041' ],
  34. expectedStdout: '\0!!',
  35. expectedStderr: '',
  36. },
  37. {
  38. description: 'outputs a trailing backslash as itself',
  39. input: [ '\\' ],
  40. expectedStdout: '\\',
  41. expectedStderr: '',
  42. },
  43. {
  44. description: 'outputs unrecognized escape sequences as themselves',
  45. input: [ '\\z\\@\\#' ],
  46. expectedStdout: '\\z\\@\\#',
  47. expectedStderr: '',
  48. },
  49. {
  50. description: 'outputs escape sequences',
  51. input: [ '\\a\\b\\f\\n\\r\\t\\v' ],
  52. expectedStdout: '\x07\x08\x0C\n\r\t\x0B',
  53. expectedStderr: '',
  54. },
  55. {
  56. description: 'rejects empty format specifier',
  57. input: [ '%' ],
  58. expectedStdout: '',
  59. expectedStderr: 'printf: Invalid conversion specifier \'%\'\n',
  60. expectedFail: true,
  61. },
  62. {
  63. description: 'outputs `%%` as `%`',
  64. input: [ '%%' ],
  65. expectedStdout: '%',
  66. expectedStderr: '',
  67. },
  68. //
  69. // %c: Character
  70. //
  71. {
  72. description: 'outputs single characters for `%c`',
  73. input: [ '%c', 'hello', '123' ],
  74. expectedStdout: 'h1',
  75. expectedStderr: '',
  76. },
  77. {
  78. description: 'outputs single characters for `%c`',
  79. input: [ '%c', 'hello', '123' ],
  80. expectedStdout: 'h1',
  81. expectedStderr: '',
  82. },
  83. {
  84. description: 'supports padding and alignment for `%c`',
  85. input: [ '"%-12c" "%12c"', 'hello', '123' ],
  86. expectedStdout: '"h " " 1"',
  87. expectedStderr: '',
  88. },
  89. //
  90. // %s: String
  91. //
  92. {
  93. description: 'outputs whole value as string for `%s`',
  94. input: [ '%s', 'hello', '123' ],
  95. expectedStdout: 'hello123',
  96. expectedStderr: '',
  97. },
  98. {
  99. description: 'supports padding and alignment for `%s`',
  100. input: [ '"%-12s" "%12s"', 'hello', '123' ],
  101. expectedStdout: '"hello " " 123"',
  102. expectedStderr: '',
  103. },
  104. {
  105. description: 'supports precision for `%s`',
  106. input: [ '%.4s\n', 'hello', '123' ],
  107. expectedStdout: 'hell\n123\n',
  108. expectedStderr: '',
  109. },
  110. //
  111. // %d and %i: Signed decimal integer
  112. //
  113. {
  114. description: 'outputs a signed decimal integer for `%d` or `%i`',
  115. input: [ '%d %i\n', '13', '13', '-127', '-127' ],
  116. expectedStdout: '13 13\n-127 -127\n',
  117. expectedStderr: '',
  118. },
  119. {
  120. description: 'supports padding for `%d` and `%i`',
  121. input: [ '"%5d" "%05i"\n', '13', '13', '-127', '-127' ],
  122. expectedStdout: '" 13" "00013"\n" -127" "-0127"\n',
  123. expectedStderr: '',
  124. },
  125. {
  126. description: 'supports alignment for `%d` and `%i`',
  127. input: [ '"%-5d" "%0-5i"\n', '13', '13', '-127', '-127' ],
  128. expectedStdout: '"13 " "13 "\n"-127 " "-127 "\n',
  129. expectedStderr: '',
  130. },
  131. {
  132. description: 'supports `+` flag for `%d` and `%i`',
  133. input: [ '"%+5d" "%+05i"\n', '13', '13', '-127', '-127' ],
  134. expectedStdout: '" +13" "+0013"\n" -127" "-0127"\n',
  135. expectedStderr: '',
  136. },
  137. {
  138. description: 'supports `+` flag with alignment for `%d` and `%i`',
  139. input: [ '"%+-5d" "%+-05i"\n', '13', '13', '-127', '-127' ],
  140. expectedStdout: '"+13 " "+13 "\n"-127 " "-127 "\n',
  141. expectedStderr: '',
  142. },
  143. {
  144. description: 'supports ` ` flag for `%d` and `%i`',
  145. input: [ '"% 5d" "% 05i"\n', '13', '13', '-127', '-127' ],
  146. expectedStdout: '" 13" " 0013"\n" -127" "-0127"\n',
  147. expectedStderr: '',
  148. },
  149. {
  150. description: 'supports ` ` flag with alignment for `%d` and `%i`',
  151. input: [ '"% -5d" "% -05i"\n', '13', '13', '-127', '-127' ],
  152. expectedStdout: '" 13 " " 13 "\n"-127 " "-127 "\n',
  153. expectedStderr: '',
  154. },
  155. {
  156. description: '`+` flag overrides ` ` for `%d` and `%i`',
  157. input: [ '"%+ -5d" "%+ 05i"\n', '13', '13', '-127', '-127' ],
  158. expectedStdout: '"+13 " "+0013"\n"-127 " "-0127"\n',
  159. expectedStderr: '',
  160. },
  161. {
  162. description: 'supports precision for `%d` and `%i`',
  163. input: [ '"%.5d" "%0.5i"\n', '13', '13', '-127', '-127' ],
  164. expectedStdout: '"00013" "00013"\n"-00127" "-00127"\n',
  165. expectedStderr: '',
  166. },
  167. {
  168. description: '0 precision for `%d` and `%i`',
  169. input: [ '"%.d" "%.0i"\n', '13', '13', '-127', '-127', '0', '0' ],
  170. expectedStdout: '"13" "13"\n"-127" "-127"\n"" ""\n',
  171. expectedStderr: '',
  172. },
  173. //
  174. // %u: Unsigned decimal integer
  175. //
  176. {
  177. description: 'outputs an unsigned decimal integer for `%u`',
  178. input: [ '%u\n', '13', '0', '-127' ],
  179. expectedStdout: '13\n0\n4294967169\n',
  180. expectedStderr: '',
  181. },
  182. {
  183. description: 'supports padding for `%u`',
  184. input: [ '"%5u" "%05u"\n', '13', '13', '0', '0', '-127', '-127' ],
  185. expectedStdout: '" 13" "00013"\n" 0" "00000"\n"4294967169" "4294967169"\n',
  186. expectedStderr: '',
  187. },
  188. {
  189. description: 'supports alignment for `%u`',
  190. input: [ '"%-5u" "%0-5u"\n', '13', '13', '0', '0', '-127', '-127' ],
  191. expectedStdout: '"13 " "13 "\n"0 " "0 "\n"4294967169" "4294967169"\n',
  192. expectedStderr: '',
  193. },
  194. {
  195. description: 'supports precision for `%u`',
  196. input: [ '"%.5u" "%0.5u"\n', '13', '13', '0', '0', '-127', '-127' ],
  197. expectedStdout: '"00013" "00013"\n"00000" "00000"\n"4294967169" "4294967169"\n',
  198. expectedStderr: '',
  199. },
  200. {
  201. description: 'ignores `+` and ` ` flags for `%u`',
  202. input: [ '"%+u" "% u"\n', '13', '13', '0', '0', '-127', '-127' ],
  203. expectedStdout: '"13" "13"\n"0" "0"\n"4294967169" "4294967169"\n',
  204. expectedStderr: '',
  205. },
  206. {
  207. description: '0 precision for `%u`',
  208. input: [ '"%.u" "%.0u"\n', '13', '13', '0', '0', '-127', '-127' ],
  209. expectedStdout: '"13" "13"\n"" ""\n"4294967169" "4294967169"\n',
  210. expectedStderr: '',
  211. },
  212. //
  213. // %o: Unsigned octal integer
  214. //
  215. {
  216. description: 'outputs an unsigned octal integer for `%o`',
  217. input: [ '%o\n', '13', '0', '-127' ],
  218. expectedStdout: '15\n0\n37777777601\n',
  219. expectedStderr: '',
  220. },
  221. {
  222. description: 'supports padding for `%o`',
  223. input: [ '"%5o" "%05o"\n', '13', '13', '0', '0', '-127', '-127' ],
  224. expectedStdout: '" 15" "00015"\n" 0" "00000"\n"37777777601" "37777777601"\n',
  225. expectedStderr: '',
  226. },
  227. {
  228. description: 'supports alignment for `%o`',
  229. input: [ '"%-5o" "%0-5o"\n', '13', '13', '0', '0', '-127', '-127' ],
  230. expectedStdout: '"15 " "15 "\n"0 " "0 "\n"37777777601" "37777777601"\n',
  231. expectedStderr: '',
  232. },
  233. {
  234. description: 'supports precision for `%o`',
  235. input: [ '"%.5o" "%0.5o"\n', '13', '13', '0', '0', '-127', '-127' ],
  236. expectedStdout: '"00015" "00015"\n"00000" "00000"\n"37777777601" "37777777601"\n',
  237. expectedStderr: '',
  238. },
  239. {
  240. description: 'ignores `+` and ` ` flags for `%o`',
  241. input: [ '"%+o" "% o"\n', '13', '13', '0', '0', '-127', '-127' ],
  242. expectedStdout: '"15" "15"\n"0" "0"\n"37777777601" "37777777601"\n',
  243. expectedStderr: '',
  244. },
  245. {
  246. description: '0 precision for `%o`',
  247. input: [ '"%.o" "%.0o"\n', '13', '13', '0', '0', '-127', '-127' ],
  248. expectedStdout: '"15" "15"\n"" ""\n"37777777601" "37777777601"\n',
  249. expectedStderr: '',
  250. },
  251. {
  252. description: 'ensures a starting `0` when using the `#` flag for `%o`',
  253. input: [ '"%#o" "%#0o"\n', '13', '13', '0', '0', '-127', '-127' ],
  254. expectedStdout: '"015" "015"\n"0" "0"\n"037777777601" "037777777601"\n',
  255. expectedStderr: '',
  256. },
  257. //
  258. // %x and %X: Unsigned hexadecimal integer
  259. //
  260. {
  261. description: 'outputs an unsigned hexadecimal integer for `%x` and `%X`',
  262. input: [ '"%x" "%X"\n', '13', '13', '0', '0', '-127', '-127' ],
  263. expectedStdout: '"d" "D"\n"0" "0"\n"ffffff81" "FFFFFF81"\n',
  264. expectedStderr: '',
  265. },
  266. {
  267. description: 'supports padding for `%x` and `%X`',
  268. input: [ '"%5x" "%05X"\n', '13', '13', '0', '0', '-127', '-127' ],
  269. expectedStdout: '" d" "0000D"\n" 0" "00000"\n"ffffff81" "FFFFFF81"\n',
  270. expectedStderr: '',
  271. },
  272. {
  273. description: 'supports alignment for `%x` and `%X`',
  274. input: [ '"%-5x" "%0-5X"\n', '13', '13', '0', '0', '-127', '-127' ],
  275. expectedStdout: '"d " "D "\n"0 " "0 "\n"ffffff81" "FFFFFF81"\n',
  276. expectedStderr: '',
  277. },
  278. {
  279. description: 'supports precision for `%x` and `%X`',
  280. input: [ '"%.5x" "%0.5X"\n', '13', '13', '0', '0', '-127', '-127' ],
  281. expectedStdout: '"0000d" "0000D"\n"00000" "00000"\n"ffffff81" "FFFFFF81"\n',
  282. expectedStderr: '',
  283. },
  284. {
  285. description: 'ignores `+` and ` ` flags for `%x` and `%X`',
  286. input: [ '"%+x" "% X"\n', '13', '13', '0', '0', '-127', '-127' ],
  287. expectedStdout: '"d" "D"\n"0" "0"\n"ffffff81" "FFFFFF81"\n',
  288. expectedStderr: '',
  289. },
  290. {
  291. description: '0 precision for `%x` and `%X`',
  292. input: [ '"%.x" "%.0X"\n', '13', '13', '0', '0', '-127', '-127' ],
  293. expectedStdout: '"d" "D"\n"" ""\n"ffffff81" "FFFFFF81"\n',
  294. expectedStderr: '',
  295. },
  296. {
  297. description: 'ensures a starting `0x` or `0X` when using the `#` flag for `%x` and `%X`',
  298. input: [ '"%#x" "%#0X"\n', '13', '13', '0', '0', '-127', '-127' ],
  299. expectedStdout: '"0xd" "0XD"\n"0x0" "0X0"\n"0xffffff81" "0XFFFFFF81"\n',
  300. expectedStderr: '',
  301. },
  302. //
  303. // %f and %F: Floating point, decimal notation
  304. //
  305. {
  306. description: 'outputs a floating point number in decimal notation for `%f` and `%F`',
  307. input: [ '"%f" "%F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  308. expectedStdout: '"13.000000" "13.000000"\n"-12345.678900" "-12345.678900"\n"0.000010" "0.000010"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  309. expectedStderr: '',
  310. },
  311. {
  312. description: 'supports padding for `%f` and `%F`',
  313. input: [ '"%12f" "%012F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  314. expectedStdout: '" 13.000000" "00013.000000"\n"-12345.678900" "-12345.678900"\n" 0.000010" "00000.000010"\n' +
  315. '" infinity" " INFINITY"\n" nan" " NAN"\n',
  316. expectedStderr: '',
  317. },
  318. {
  319. description: 'supports padding and alignment for `%f` and `%F`',
  320. input: [ '"%-12f" "%-012F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  321. expectedStdout: '"13.000000 " "13.000000 "\n"-12345.678900" "-12345.678900"\n"0.000010 " "0.000010 "\n' +
  322. '"infinity " "INFINITY "\n"nan " "NAN "\n',
  323. expectedStderr: '',
  324. },
  325. {
  326. description: 'supports `+` flag for `%f` and `%F`',
  327. input: [ '"%+12f" "%+012F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  328. expectedStdout: '" +13.000000" "+0013.000000"\n"-12345.678900" "-12345.678900"\n" +0.000010" "+0000.000010"\n' +
  329. '" +infinity" " +INFINITY"\n" +nan" " +NAN"\n',
  330. expectedStderr: '',
  331. },
  332. {
  333. description: 'supports `+` flag with alignment for `%f` and `%F`',
  334. input: [ '"%+-12f" "%+-012F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  335. expectedStdout: '"+13.000000 " "+13.000000 "\n"-12345.678900" "-12345.678900"\n"+0.000010 " "+0.000010 "\n' +
  336. '"+infinity " "+INFINITY "\n"+nan " "+NAN "\n',
  337. expectedStderr: '',
  338. },
  339. {
  340. description: 'supports ` ` flag for `%f` and `%F`',
  341. input: [ '"% 12f" "% 012F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  342. expectedStdout: '" 13.000000" " 0013.000000"\n"-12345.678900" "-12345.678900"\n" 0.000010" " 0000.000010"\n' +
  343. '" infinity" " INFINITY"\n" nan" " NAN"\n',
  344. expectedStderr: '',
  345. },
  346. {
  347. description: 'supports ` ` flag with alignment for `%f` and `%F`',
  348. input: [ '"% -12f" "% -012F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  349. expectedStdout: '" 13.000000 " " 13.000000 "\n"-12345.678900" "-12345.678900"\n" 0.000010 " " 0.000010 "\n' +
  350. '" infinity " " INFINITY "\n" nan " " NAN "\n',
  351. expectedStderr: '',
  352. },
  353. {
  354. description: '`+` flag overrides ` ` for `%f` and `%F`',
  355. input: [ '"% +12f" "% +012F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  356. expectedStdout: '" +13.000000" "+0013.000000"\n"-12345.678900" "-12345.678900"\n" +0.000010" "+0000.000010"\n' +
  357. '" +infinity" " +INFINITY"\n" +nan" " +NAN"\n',
  358. expectedStderr: '',
  359. },
  360. {
  361. description: 'supports precision for `%f` and `%F`',
  362. input: [ '"%.3f" "%0.3F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  363. expectedStdout: '"13.000" "13.000"\n"-12345.679" "-12345.679"\n"0.000" "0.000"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  364. expectedStderr: '',
  365. },
  366. {
  367. description: 'zero precision removes decimal point for `%f` and `%F`',
  368. input: [ '"%.0f" "%0.0F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  369. expectedStdout: '"13" "13"\n"-12346" "-12346"\n"0" "0"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  370. expectedStderr: '',
  371. },
  372. {
  373. description: 'zero precision with `#` flag forces a decimal point for `%f` and `%F`',
  374. input: [ '"%#.0f" "%0#.0F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  375. expectedStdout: '"13." "13."\n"-12346." "-12346."\n"0." "0."\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  376. expectedStderr: '',
  377. },
  378. {
  379. description: 'supports width and precision for `%f` and `%F`',
  380. input: [ '"%12.3f" "%012.3F"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  381. expectedStdout: '" 13.000" "00000013.000"\n" -12345.679" "-0012345.679"\n" 0.000" "00000000.000"\n' +
  382. '" infinity" " INFINITY"\n" nan" " NAN"\n',
  383. expectedStderr: '',
  384. },
  385. //
  386. // %e and %E: Floating point, exponential notation
  387. //
  388. {
  389. description: 'outputs a floating point number in exponential notation for `%e` and `%E`',
  390. input: [ '"%e" "%E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  391. expectedStdout: '"1.300000e+01" "1.300000E+01"\n"-1.234568e+04" "-1.234568E+04"\n"1.000000e-05" "1.000000E-05"\n' +
  392. '"infinity" "INFINITY"\n"nan" "NAN"\n',
  393. expectedStderr: '',
  394. },
  395. {
  396. description: 'supports padding for `%e` and `%E`',
  397. input: [ '"%15e" "%015E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  398. expectedStdout: '" 1.300000e+01" "0001.300000E+01"\n" -1.234568e+04" "-001.234568E+04"\n" 1.000000e-05" "0001.000000E-05"\n' +
  399. '" infinity" " INFINITY"\n" nan" " NAN"\n',
  400. expectedStderr: '',
  401. },
  402. {
  403. description: 'supports padding and alignment for `%e` and `%E`',
  404. input: [ '"%-15e" "%-015E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  405. expectedStdout: '"1.300000e+01 " "1.300000E+01 "\n"-1.234568e+04 " "-1.234568E+04 "\n"1.000000e-05 " "1.000000E-05 "\n' +
  406. '"infinity " "INFINITY "\n"nan " "NAN "\n',
  407. expectedStderr: '',
  408. },
  409. {
  410. description: 'supports `+` flag for `%e` and `%E`',
  411. input: [ '"%+15e" "%+015E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  412. expectedStdout: '" +1.300000e+01" "+001.300000E+01"\n" -1.234568e+04" "-001.234568E+04"\n" +1.000000e-05" "+001.000000E-05"\n' +
  413. '" +infinity" " +INFINITY"\n" +nan" " +NAN"\n',
  414. expectedStderr: '',
  415. },
  416. {
  417. description: 'supports `+` flag with alignment for `%e` and `%E`',
  418. input: [ '"%+-15e" "%+-015E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  419. expectedStdout: '"+1.300000e+01 " "+1.300000E+01 "\n"-1.234568e+04 " "-1.234568E+04 "\n"+1.000000e-05 " "+1.000000E-05 "\n' +
  420. '"+infinity " "+INFINITY "\n"+nan " "+NAN "\n',
  421. expectedStderr: '',
  422. },
  423. {
  424. description: 'supports ` ` flag for `%e` and `%E`',
  425. input: [ '"% 15e" "% 015E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  426. expectedStdout: '" 1.300000e+01" " 001.300000E+01"\n" -1.234568e+04" "-001.234568E+04"\n" 1.000000e-05" " 001.000000E-05"\n' +
  427. '" infinity" " INFINITY"\n" nan" " NAN"\n',
  428. expectedStderr: '',
  429. },
  430. {
  431. description: 'supports ` ` flag with alignment for `%e` and `%E`',
  432. input: [ '"% -15e" "% -015E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  433. expectedStdout: '" 1.300000e+01 " " 1.300000E+01 "\n"-1.234568e+04 " "-1.234568E+04 "\n" 1.000000e-05 " " 1.000000E-05 "\n' +
  434. '" infinity " " INFINITY "\n" nan " " NAN "\n',
  435. expectedStderr: '',
  436. },
  437. {
  438. description: '`+` flag overrides ` ` for `%e` and `%E`',
  439. input: [ '"% +15e" "% +015E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  440. expectedStdout: '" +1.300000e+01" "+001.300000E+01"\n" -1.234568e+04" "-001.234568E+04"\n" +1.000000e-05" "+001.000000E-05"\n' +
  441. '" +infinity" " +INFINITY"\n" +nan" " +NAN"\n',
  442. expectedStderr: '',
  443. },
  444. {
  445. description: 'supports precision for `%e` and `%E`',
  446. input: [ '"%.3e" "%0.3E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  447. expectedStdout: '"1.300e+01" "1.300E+01"\n"-1.235e+04" "-1.235E+04"\n"1.000e-05" "1.000E-05"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  448. expectedStderr: '',
  449. },
  450. {
  451. description: 'zero precision removes decimal point for `%e` and `%E`',
  452. input: [ '"%.0e" "%0.0E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  453. expectedStdout: '"1e+01" "1E+01"\n"-1e+04" "-1E+04"\n"1e-05" "1E-05"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  454. expectedStderr: '',
  455. },
  456. {
  457. description: 'zero precision with `#` flag forces a decimal point for `%e` and `%E`',
  458. input: [ '"%#.0e" "%0#.0E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  459. expectedStdout: '"1.e+01" "1.E+01"\n"-1.e+04" "-1.E+04"\n"1.e-05" "1.E-05"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  460. expectedStderr: '',
  461. },
  462. {
  463. description: 'supports width and precision for `%e` and `%E`',
  464. input: [ '"%15.3e" "%015.3E"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  465. expectedStdout: '" 1.300e+01" "0000001.300E+01"\n" -1.235e+04" "-000001.235E+04"\n" 1.000e-05" "0000001.000E-05"\n' +
  466. '" infinity" " INFINITY"\n" nan" " NAN"\n',
  467. expectedStderr: '',
  468. },
  469. //
  470. // %g and %G: Floating point, set number of significant digits, may be decimal or exponential notation
  471. //
  472. {
  473. description: 'outputs a floating point number for `%g` and `%G`',
  474. input: [ '"%g" "%G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  475. expectedStdout: '"13" "13"\n"-12345.7" "-12345.7"\n"1e-05" "1E-05"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  476. expectedStderr: '',
  477. },
  478. {
  479. description: 'supports padding for `%g` and `%G`',
  480. input: [ '"%12g" "%012G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  481. expectedStdout: '" 13" "000000000013"\n" -12345.7" "-000012345.7"\n" 1e-05" "00000001E-05"\n' +
  482. '" infinity" " INFINITY"\n" nan" " NAN"\n',
  483. expectedStderr: '',
  484. },
  485. {
  486. description: 'supports padding and alignment for `%g` and `%G`',
  487. input: [ '"%-12g" "%-012G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  488. expectedStdout: '"13 " "13 "\n"-12345.7 " "-12345.7 "\n"1e-05 " "1E-05 "\n' +
  489. '"infinity " "INFINITY "\n"nan " "NAN "\n',
  490. expectedStderr: '',
  491. },
  492. {
  493. description: 'supports `+` flag for `%g` and `%G`',
  494. input: [ '"%+12g" "%+012G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  495. expectedStdout: '" +13" "+00000000013"\n" -12345.7" "-000012345.7"\n" +1e-05" "+0000001E-05"\n' +
  496. '" +infinity" " +INFINITY"\n" +nan" " +NAN"\n',
  497. expectedStderr: '',
  498. },
  499. {
  500. description: 'supports `+` flag with alignment for `%g` and `%G`',
  501. input: [ '"%+-12g" "%+-012G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  502. expectedStdout: '"+13 " "+13 "\n"-12345.7 " "-12345.7 "\n"+1e-05 " "+1E-05 "\n' +
  503. '"+infinity " "+INFINITY "\n"+nan " "+NAN "\n',
  504. expectedStderr: '',
  505. },
  506. {
  507. description: 'supports ` ` flag for `%g` and `%G`',
  508. input: [ '"% 12g" "% 012G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  509. expectedStdout: '" 13" " 00000000013"\n" -12345.7" "-000012345.7"\n" 1e-05" " 0000001E-05"\n' +
  510. '" infinity" " INFINITY"\n" nan" " NAN"\n',
  511. expectedStderr: '',
  512. },
  513. {
  514. description: 'supports ` ` flag with alignment for `%g` and `%G`',
  515. input: [ '"% -12g" "% -012G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  516. expectedStdout: '" 13 " " 13 "\n"-12345.7 " "-12345.7 "\n" 1e-05 " " 1E-05 "\n' +
  517. '" infinity " " INFINITY "\n" nan " " NAN "\n',
  518. expectedStderr: '',
  519. },
  520. {
  521. description: '`+` flag overrides ` ` for `%g` and `%G`',
  522. input: [ '"% +12g" "% +012G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  523. expectedStdout: '" +13" "+00000000013"\n" -12345.7" "-000012345.7"\n" +1e-05" "+0000001E-05"\n' +
  524. '" +infinity" " +INFINITY"\n" +nan" " +NAN"\n',
  525. expectedStderr: '',
  526. },
  527. {
  528. description: 'supports precision for `%g` and `%G`',
  529. input: [ '"%.3g" "%0.3G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  530. expectedStdout: '"13" "13"\n"-1.23e+04" "-1.23E+04"\n"1e-05" "1E-05"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  531. expectedStderr: '',
  532. },
  533. {
  534. description: 'zero precision removes decimal point for `%g` and `%G`',
  535. input: [ '"%.0g" "%0.0G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  536. expectedStdout: '"1e+01" "1E+01"\n"-1e+04" "-1E+04"\n"1e-05" "1E-05"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  537. expectedStderr: '',
  538. },
  539. {
  540. description: 'zero precision with `#` flag forces a decimal point for `%g` and `%G`',
  541. input: [ '"%#.0g" "%0#.0G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  542. expectedStdout: '"1.e+01" "1.E+01"\n"-1.e+04" "-1.E+04"\n"1.e-05" "1.E-05"\n"infinity" "INFINITY"\n"nan" "NAN"\n',
  543. expectedStderr: '',
  544. },
  545. {
  546. description: 'supports width and precision for `%g` and `%G`',
  547. input: [ '"%12.3g" "%012.3G"\n', '13', '13', '-12345.67890', '-12345.67890', '0.00001', '0.00001', 'Infinity', 'Infinity', 'NaN', 'NaN' ],
  548. expectedStdout: '" 13" "000000000013"\n" -1.23e+04" "-0001.23E+04"\n" 1e-05" "00000001E-05"\n' +
  549. '" infinity" " INFINITY"\n" nan" " NAN"\n',
  550. expectedStderr: '',
  551. },
  552. ];
  553. for (const { description, input, expectedStdout, expectedStderr, expectedFail } of testCases) {
  554. it(description, async () => {
  555. let ctx = MakeTestContext(builtins.printf, { positionals: input });
  556. let hadError = false;
  557. try {
  558. const result = await builtins.printf.execute(ctx);
  559. if (!expectedFail) {
  560. assert.equal(result, undefined, 'should exit successfully, returning nothing');
  561. }
  562. } catch (e) {
  563. hadError = true;
  564. if (!expectedFail) {
  565. assert.fail(e);
  566. }
  567. }
  568. if (expectedFail && !hadError) {
  569. assert.fail('should have returned an error code');
  570. }
  571. assert.equal(ctx.externs.out.output, expectedStdout, 'wrong output written to stdout');
  572. assert.equal(ctx.externs.err.output, expectedStderr, 'wrong output written to stderr');
  573. });
  574. }
  575. });
  576. };