Skip to content

Commit a9d3e40

Browse files
committed
gh-131178: Add more tests for json command-line interface
Cover error paths (invalid JSON input, missing input file, unknown and mutually exclusive options) and --json-lines combined with --compact or --no-indent.
1 parent 7e98deb commit a9d3e40

2 files changed

Lines changed: 90 additions & 1 deletion

File tree

Lib/test/test_json/test_tool.py

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from test import support
1010
from test.support import force_colorized, force_not_colorized, os_helper
11-
from test.support.script_helper import assert_python_ok
11+
from test.support.script_helper import assert_python_failure, assert_python_ok
1212

1313
from _colorize import get_theme
1414

@@ -327,6 +327,92 @@ def test_colors(self):
327327
stdout = stdout.strip()
328328
self.assertEqual(stdout, expected)
329329

330+
@force_not_colorized
331+
def test_invalid_json_input(self):
332+
# Malformed JSON on stdin: ValueError is reported on stderr and the
333+
# process exits with status 1.
334+
args = sys.executable, '-m', self.module
335+
process = subprocess.run(args, input='not valid json',
336+
capture_output=True, text=True)
337+
self.assertEqual(process.returncode, 1)
338+
self.assertEqual(process.stdout, '')
339+
self.assertIn('Expecting value', process.stderr)
340+
341+
@force_not_colorized
342+
def test_empty_input(self):
343+
# Empty stdin is treated as missing JSON value and reported as an
344+
# error.
345+
args = sys.executable, '-m', self.module
346+
process = subprocess.run(args, input='', capture_output=True,
347+
text=True)
348+
self.assertEqual(process.returncode, 1)
349+
self.assertEqual(process.stdout, '')
350+
self.assertIn('Expecting value', process.stderr)
351+
352+
def test_missing_infile(self):
353+
infile = os_helper.TESTFN + '.does-not-exist'
354+
rc, out, err = assert_python_failure('-m', self.module, infile,
355+
PYTHON_COLORS='0')
356+
self.assertNotEqual(rc, 0)
357+
self.assertIn(b'FileNotFoundError', err)
358+
359+
def test_unknown_option(self):
360+
rc, out, err = assert_python_failure('-m', self.module,
361+
'--bogus-option',
362+
PYTHON_COLORS='0')
363+
self.assertEqual(rc, 2)
364+
self.assertIn(b'unrecognized arguments', err)
365+
366+
def test_mutually_exclusive_indent_and_tab(self):
367+
rc, out, err = assert_python_failure('-m', self.module,
368+
'--indent', '2', '--tab',
369+
PYTHON_COLORS='0')
370+
self.assertEqual(rc, 2)
371+
self.assertIn(b'not allowed with', err)
372+
373+
def test_mutually_exclusive_compact_and_no_indent(self):
374+
rc, out, err = assert_python_failure('-m', self.module,
375+
'--compact', '--no-indent',
376+
PYTHON_COLORS='0')
377+
self.assertEqual(rc, 2)
378+
self.assertIn(b'not allowed with', err)
379+
380+
@force_not_colorized
381+
def test_jsonlines_compact(self):
382+
# --json-lines combined with --compact emits valid JSON Lines output:
383+
# one compact JSON object per line.
384+
expect = ('{"ingredients":["frog","water","chocolate","glucose"]}\n'
385+
'{"ingredients":["chocolate","steel bolts"]}\n')
386+
args = (sys.executable, '-m', self.module,
387+
'--json-lines', '--compact')
388+
process = subprocess.run(args, input=self.jsonlines_raw,
389+
capture_output=True, text=True, check=True)
390+
self.assertEqual(process.stdout, expect)
391+
self.assertEqual(process.stderr, '')
392+
393+
@force_not_colorized
394+
def test_jsonlines_no_indent(self):
395+
# --json-lines combined with --no-indent emits valid JSON Lines
396+
# output: one single-line JSON object per line.
397+
expect = ('{"ingredients": ["frog", "water", "chocolate", "glucose"]}\n'
398+
'{"ingredients": ["chocolate", "steel bolts"]}\n')
399+
args = (sys.executable, '-m', self.module,
400+
'--json-lines', '--no-indent')
401+
process = subprocess.run(args, input=self.jsonlines_raw,
402+
capture_output=True, text=True, check=True)
403+
self.assertEqual(process.stdout, expect)
404+
self.assertEqual(process.stderr, '')
405+
406+
@force_not_colorized
407+
def test_jsonlines_invalid_line(self):
408+
# An invalid line in --json-lines mode causes the command to fail
409+
# after emitting any already-processed lines.
410+
args = sys.executable, '-m', self.module, '--json-lines'
411+
process = subprocess.run(args, input='{"a": 1}\nnot valid\n',
412+
capture_output=True, text=True)
413+
self.assertEqual(process.returncode, 1)
414+
self.assertIn('Expecting value', process.stderr)
415+
330416

331417
@support.requires_subprocess()
332418
@support.skip_if_pgo_task
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add tests for the :mod:`json` command-line interface, covering error paths
2+
(invalid JSON, missing input file, unknown and mutually exclusive options)
3+
as well as ``--json-lines`` combined with ``--compact`` or ``--no-indent``.

0 commit comments

Comments
 (0)