@@ -114,10 +114,10 @@ object MainTests extends TestSuite {
114114 assert((res, out, err) == ((0 , expectedOut, " " )))
115115
116116 val helloDestStr = os.read(multiDest / " hello" )
117- assert(helloDestStr == " 1" )
117+ assert(helloDestStr == " 1\n " )
118118
119119 val worldDestStr = os.read(multiDest / " world" )
120- assert(worldDestStr == expectedWorldDestStr)
120+ assert(worldDestStr == expectedWorldDestStr + " \n " )
121121 }
122122
123123 test(" multiOutputFile" ) {
@@ -132,10 +132,10 @@ object MainTests extends TestSuite {
132132 assert(destStr == expectedOut)
133133
134134 val helloDestStr = os.read(multiDest / " hello" )
135- assert(helloDestStr == " 1" )
135+ assert(helloDestStr == " 1\n " )
136136
137137 val worldDestStr = os.read(multiDest / " world" )
138- assert(worldDestStr == expectedWorldDestStr)
138+ assert(worldDestStr == expectedWorldDestStr + " \n " )
139139 }
140140
141141 test(" multiYamlOut" ) {
@@ -146,16 +146,167 @@ object MainTests extends TestSuite {
146146 assert((res, out, err) == ((0 , expectedOut, " " )))
147147
148148 val helloDestStr = os.read(multiDest / " hello" )
149- assert(helloDestStr == " 1" )
149+ assert(helloDestStr == " 1\n " )
150150
151151 val worldDestStr = os.read(multiDest / " world" )
152152 assert(
153153 worldDestStr ==
154154 """ - 2
155155 |- three
156- |- true""" .stripMargin
156+ |- true
157+ |""" .stripMargin
157158 )
158159 }
160+
161+ // -- Default trailing newline behavior (with newline) --
162+
163+ test(" execString" ) {
164+ val source = """ "hello""""
165+ val (res, out, err) = runMain(source, " --exec" , " --string" )
166+ assert((res, out, err) == ((0 , " hello\n " , " " )))
167+ }
168+
169+ test(" multiStringOutput" ) {
170+ val source = testSuiteRoot / " db" / " multi_string.jsonnet"
171+ val multiDest = os.temp.dir()
172+ val (res, out, err) = runMain(source, " --multi" , multiDest, " --string" )
173+ assert(res == 0 )
174+ assert(err.isEmpty)
175+
176+ val helloDestStr = os.read(multiDest / " hello.txt" )
177+ assert(helloDestStr == " hello world\n " )
178+
179+ val barDestStr = os.read(multiDest / " bar.txt" )
180+ assert(barDestStr == " bar\n " )
181+ }
182+
183+ // -- No trailing newline behavior --
184+
185+ test(" noTrailingNewline" ) {
186+ // Simple scalar output — default has trailing newline
187+ val (resDefault, outDefault, _) = runMain(" 42" , " --exec" )
188+ assert((resDefault, outDefault) == ((0 , " 42\n " )))
189+
190+ // Simple scalar output — no trailing newline
191+ val (res1, out1, err1) = runMain(" 42" , " --exec" , " --no-trailing-newline" )
192+ assert((res1, out1, err1) == ((0 , " 42" , " " )))
193+
194+ // Object output — default has trailing newline
195+ val (resObj, outObj, _) = runMain(""" {"a": 1, "b": 2}""" , " --exec" )
196+ val expectedJsonWithNewline =
197+ """ {
198+ | "a": 1,
199+ | "b": 2
200+ |}
201+ |""" .stripMargin
202+ assert((resObj, outObj) == ((0 , expectedJsonWithNewline)))
203+
204+ // Object output — no trailing newline
205+ val (res2, out2, err2) =
206+ runMain(""" {"a": 1, "b": 2}""" , " --exec" , " --no-trailing-newline" )
207+ val expectedJson =
208+ """ {
209+ | "a": 1,
210+ | "b": 2
211+ |}""" .stripMargin
212+ assert((res2, out2, err2) == ((0 , expectedJson, " " )))
213+
214+ // String output — default has trailing newline
215+ val (resStr, outStr, _) = runMain(""" "hello"""" , " --exec" , " --string" )
216+ assert((resStr, outStr) == ((0 , " hello\n " )))
217+
218+ // String output — no trailing newline
219+ val (res3, out3, err3) =
220+ runMain(""" "hello"""" , " --exec" , " --string" , " --no-trailing-newline" )
221+ assert((res3, out3, err3) == ((0 , " hello" , " " )))
222+ }
223+
224+ test(" noTrailingNewlineMulti" ) {
225+ // Default multi — files have trailing newline
226+ val source = testSuiteRoot / " db" / " multi.jsonnet"
227+ val multiDestDefault = os.temp.dir()
228+ val (resDefault, _, _) = runMain(source, " --multi" , multiDestDefault)
229+ assert(resDefault == 0 )
230+ assert(os.read(multiDestDefault / " hello" ) == " 1\n " )
231+ assert(os.read(multiDestDefault / " world" ) == expectedWorldDestStr + " \n " )
232+
233+ // No trailing newline multi — files have no trailing newline,
234+ // but the file list on stdout still ends with \n (matching go-jsonnet behavior)
235+ val multiDest = os.temp.dir()
236+ val (res, out, err) =
237+ runMain(source, " --multi" , multiDest, " --no-trailing-newline" )
238+ val expectedOut = s " $multiDest/hello \n $multiDest/world \n "
239+ assert((res, out, err) == ((0 , expectedOut, " " )))
240+ assert(os.read(multiDest / " hello" ) == " 1" )
241+ assert(os.read(multiDest / " world" ) == expectedWorldDestStr)
242+ }
243+
244+ test(" noTrailingNewlineMultiString" ) {
245+ val source = testSuiteRoot / " db" / " multi_string.jsonnet"
246+
247+ // Default multi+string — files have trailing newline
248+ val multiDestDefault = os.temp.dir()
249+ val (resDefault, _, _) = runMain(source, " --multi" , multiDestDefault, " --string" )
250+ assert(resDefault == 0 )
251+ assert(os.read(multiDestDefault / " hello.txt" ) == " hello world\n " )
252+ assert(os.read(multiDestDefault / " bar.txt" ) == " bar\n " )
253+
254+ // No trailing newline multi+string — files have no trailing newline,
255+ // but the file list on stdout still ends with \n (matching go-jsonnet behavior)
256+ val multiDest = os.temp.dir()
257+ val (res, out, err) =
258+ runMain(source, " --multi" , multiDest, " --string" , " --no-trailing-newline" )
259+ val expectedOut = s " $multiDest/bar.txt \n $multiDest/hello.txt \n "
260+ assert((res, out, err) == ((0 , expectedOut, " " )))
261+ assert(os.read(multiDest / " hello.txt" ) == " hello world" )
262+ assert(os.read(multiDest / " bar.txt" ) == " bar" )
263+ }
264+
265+ test(" noTrailingNewlineOutputFile" ) {
266+ // Default output-file — file has content without trailing newline (file mode)
267+ val source = " 42"
268+ val destDefault = os.temp()
269+ val (resDefault, _, _) = runMain(source, " --exec" , " --output-file" , destDefault)
270+ assert(resDefault == 0 )
271+ val defaultContent = os.read(destDefault)
272+ assert(defaultContent == " 42" )
273+
274+ // No trailing newline output-file — same behavior
275+ val dest = os.temp()
276+ val (res, out, err) =
277+ runMain(source, " --exec" , " --no-trailing-newline" , " --output-file" , dest)
278+ assert((res, out, err) == ((0 , " " , " " )))
279+ assert(os.read(dest) == " 42" )
280+ }
281+
282+ test(" noTrailingNewlineYamlStreamError" ) {
283+ val source = testSuiteRoot / " db" / " stream.jsonnet"
284+ val (res, out, err) =
285+ runMain(source, " --yaml-stream" , " --no-trailing-newline" )
286+ assert(res == 1 )
287+ assert(out.isEmpty)
288+ assert(err.contains(" cannot use --no-trailing-newline with --yaml-stream" ))
289+ }
290+
291+ test(" noTrailingNewlineYamlOut" ) {
292+ // Default yaml-out — has trailing newline
293+ val source = " local x = [1]; local y = [2]; x + y"
294+ val (resDefault, outDefault, _) = runMain(source, " --exec" , " --yaml-out" )
295+ val expectedYamlWithNewline =
296+ """ - 1
297+ |- 2
298+ |
299+ |""" .stripMargin
300+ assert((resDefault, outDefault) == ((0 , expectedYamlWithNewline)))
301+
302+ // No trailing newline yaml-out — no trailing newline
303+ val (res, out, err) =
304+ runMain(source, " --exec" , " --yaml-out" , " --no-trailing-newline" )
305+ val expectedYaml =
306+ """ - 1
307+ |- 2""" .stripMargin
308+ assert((res, out, err) == ((0 , expectedYaml, " " )))
309+ }
159310 }
160311
161312 def runMain (args : os.Shellable * ): (Int , String , String ) = {
0 commit comments