1# -*- coding: utf-8 -*- 2# Copyright 2015 Google Inc. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Tests for yapf.__init__.main.""" 16 17from contextlib import contextmanager 18import sys 19import unittest 20import yapf 21 22from yapf.yapflib import py3compat 23 24from yapftests import yapf_test_helper 25 26 27class IO(object): 28 """IO is a thin wrapper around StringIO. 29 30 This is strictly to wrap the Python 3 StringIO object so that it can supply a 31 "buffer" attribute. 32 """ 33 34 class Buffer(object): 35 36 def __init__(self): 37 self.string_io = py3compat.StringIO() 38 39 def write(self, s): 40 if py3compat.PY3 and isinstance(s, bytes): 41 s = str(s, 'utf-8') 42 self.string_io.write(s) 43 44 def getvalue(self): 45 return self.string_io.getvalue() 46 47 def __init__(self): 48 self.buffer = self.Buffer() 49 50 def write(self, s): 51 self.buffer.write(s) 52 53 def getvalue(self): 54 return self.buffer.getvalue() 55 56 57@contextmanager 58def captured_output(): 59 new_out, new_err = IO(), IO() 60 old_out, old_err = sys.stdout, sys.stderr 61 try: 62 sys.stdout, sys.stderr = new_out, new_err 63 yield sys.stdout, sys.stderr 64 finally: 65 sys.stdout, sys.stderr = old_out, old_err 66 67 68@contextmanager 69def patched_input(code): 70 """Monkey patch code as though it were coming from stdin.""" 71 72 def lines(): 73 for line in code.splitlines(): 74 yield line 75 raise EOFError() 76 77 def patch_raw_input(lines=lines()): 78 return next(lines) 79 80 try: 81 orig_raw_import = yapf.py3compat.raw_input 82 yapf.py3compat.raw_input = patch_raw_input 83 yield 84 finally: 85 yapf.py3compat.raw_input = orig_raw_import 86 87 88class RunMainTest(yapf_test_helper.YAPFTest): 89 90 def testShouldHandleYapfError(self): 91 """run_main should handle YapfError and sys.exit(1).""" 92 expected_message = 'yapf: input filenames did not match any python files\n' 93 sys.argv = ['yapf', 'foo.c'] 94 with captured_output() as (out, err): 95 with self.assertRaises(SystemExit): 96 yapf.run_main() 97 self.assertEqual(out.getvalue(), '') 98 self.assertEqual(err.getvalue(), expected_message) 99 100 101class MainTest(yapf_test_helper.YAPFTest): 102 103 def testNoPythonFilesMatched(self): 104 with self.assertRaisesRegex(yapf.errors.YapfError, 105 'did not match any python files'): 106 yapf.main(['yapf', 'foo.c']) 107 108 def testEchoInput(self): 109 code = 'a = 1\nb = 2\n' 110 with patched_input(code): 111 with captured_output() as (out, _): 112 ret = yapf.main([]) 113 self.assertEqual(ret, 0) 114 self.assertEqual(out.getvalue(), code) 115 116 def testEchoInputWithStyle(self): 117 code = 'def f(a = 1\n\n):\n return 2*a\n' 118 yapf_code = 'def f(a=1):\n return 2 * a\n' 119 with patched_input(code): 120 with captured_output() as (out, _): 121 ret = yapf.main(['-', '--style=yapf']) 122 self.assertEqual(ret, 0) 123 self.assertEqual(out.getvalue(), yapf_code) 124 125 def testEchoBadInput(self): 126 bad_syntax = ' a = 1\n' 127 with patched_input(bad_syntax): 128 with captured_output() as (_, _): 129 with self.assertRaisesRegex(yapf.errors.YapfError, 'unexpected indent'): 130 yapf.main([]) 131 132 def testHelp(self): 133 with captured_output() as (out, _): 134 ret = yapf.main(['-', '--style-help', '--style=pep8']) 135 self.assertEqual(ret, 0) 136 help_message = out.getvalue() 137 self.assertIn('indent_width=4', help_message) 138 self.assertIn('The number of spaces required before a trailing comment.', 139 help_message) 140