PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/python/pants_test/java/test_nailgun_protocol.py

https://gitlab.com/Ivy001/pants
Python | 217 lines | 206 code | 8 blank | 3 comment | 0 complexity | 4b88158be90ab2b21548fa06d128e7ac MD5 | raw file
  1. # coding=utf-8
  2. # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
  3. # Licensed under the Apache License, Version 2.0 (see LICENSE).
  4. from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
  5. unicode_literals, with_statement)
  6. import socket
  7. import unittest
  8. import mock
  9. from pants.java.nailgun_protocol import ChunkType, NailgunProtocol
  10. class TestChunkType(unittest.TestCase):
  11. def test_chunktype_constants(self):
  12. self.assertIsNotNone(ChunkType.ARGUMENT)
  13. self.assertIsNotNone(ChunkType.ENVIRONMENT)
  14. self.assertIsNotNone(ChunkType.WORKING_DIR)
  15. self.assertIsNotNone(ChunkType.COMMAND)
  16. self.assertIsNotNone(ChunkType.STDIN)
  17. self.assertIsNotNone(ChunkType.STDOUT)
  18. self.assertIsNotNone(ChunkType.STDERR)
  19. self.assertIsNotNone(ChunkType.START_READING_INPUT)
  20. self.assertIsNotNone(ChunkType.STDIN_EOF)
  21. self.assertIsNotNone(ChunkType.EXIT)
  22. class TestNailgunProtocol(unittest.TestCase):
  23. EMPTY_PAYLOAD = ''
  24. TEST_COMMAND = 'test'
  25. TEST_OUTPUT = 't e s t'
  26. TEST_UNICODE_PAYLOAD = u'([\d0-9]{1,4}\s?[年月日])'.encode('utf-8')
  27. TEST_WORKING_DIR = '/path/to/a/repo'
  28. TEST_ARGUMENTS = ['t', 'e', 's', 't']
  29. TEST_ENVIRON = dict(TEST_VAR='success')
  30. def setUp(self):
  31. self.client_sock, self.server_sock = socket.socketpair()
  32. def tearDown(self):
  33. self.client_sock.close()
  34. self.server_sock.close()
  35. def test_send_and_parse_request(self):
  36. # Send a test request over the client socket.
  37. NailgunProtocol.send_request(
  38. self.client_sock,
  39. self.TEST_WORKING_DIR,
  40. self.TEST_COMMAND,
  41. *self.TEST_ARGUMENTS,
  42. **self.TEST_ENVIRON
  43. )
  44. # Receive the request from the server-side context.
  45. working_dir, command, arguments, environment = NailgunProtocol.parse_request(self.server_sock)
  46. self.assertEqual(working_dir, self.TEST_WORKING_DIR)
  47. self.assertEqual(command, self.TEST_COMMAND)
  48. self.assertEqual(arguments, self.TEST_ARGUMENTS)
  49. self.assertEqual(environment, self.TEST_ENVIRON)
  50. def test_send_and_parse_request_bad_chunktype(self):
  51. INVALID_CHUNK_TYPE = b';'
  52. NailgunProtocol.write_chunk(self.client_sock, INVALID_CHUNK_TYPE, '1729')
  53. with self.assertRaises(NailgunProtocol.ProtocolError):
  54. NailgunProtocol.parse_request(self.server_sock)
  55. def test_read_until(self):
  56. recv_chunks = ['1', '234', '56', '789', '0']
  57. mock_socket = mock.Mock()
  58. mock_socket.recv.side_effect = recv_chunks
  59. self.assertEqual(NailgunProtocol._read_until(mock_socket, 10), '1234567890')
  60. self.assertEqual(mock_socket.recv.call_count, len(recv_chunks))
  61. def test_read_until_truncated_recv(self):
  62. self.server_sock.sendall(b'x')
  63. self.server_sock.close()
  64. with self.assertRaises(NailgunProtocol.TruncatedRead):
  65. NailgunProtocol._read_until(self.client_sock, 3)
  66. def test_iter_chunks(self):
  67. expected_chunks = [
  68. (ChunkType.COMMAND, self.TEST_COMMAND),
  69. (ChunkType.STDOUT, self.TEST_OUTPUT),
  70. (ChunkType.STDERR, self.TEST_OUTPUT),
  71. (ChunkType.EXIT, self.EMPTY_PAYLOAD)
  72. # N.B. without an EXIT chunk here (or socket failure), this test will deadlock in iter_chunks.
  73. ]
  74. for chunk_type, payload in expected_chunks:
  75. NailgunProtocol.write_chunk(self.server_sock, chunk_type, payload)
  76. for i, chunk in enumerate(NailgunProtocol.iter_chunks(self.client_sock)):
  77. self.assertEqual(chunk, expected_chunks[i])
  78. def test_read_and_write_chunk(self):
  79. # Write a command chunk to the server socket.
  80. NailgunProtocol.write_chunk(self.server_sock, ChunkType.COMMAND, self.TEST_COMMAND)
  81. # Read the chunk from the client socket.
  82. chunk_type, payload = NailgunProtocol.read_chunk(self.client_sock)
  83. self.assertEqual(
  84. (chunk_type, payload),
  85. (ChunkType.COMMAND, self.TEST_COMMAND)
  86. )
  87. def test_read_chunk_truncated_during_header(self):
  88. """Construct a chunk and truncate to the first 3 bytes ([:3]), an incomplete header."""
  89. truncated_chunk = NailgunProtocol.construct_chunk(ChunkType.STDOUT, self.TEST_OUTPUT)[:3]
  90. self.server_sock.sendall(truncated_chunk)
  91. self.server_sock.close()
  92. with self.assertRaises(NailgunProtocol.TruncatedHeaderError):
  93. NailgunProtocol.read_chunk(self.client_sock)
  94. def test_read_chunk_truncated_before_payload(self):
  95. """Construct a chunk and send exactly the header (first 5 bytes) and truncate the remainder."""
  96. truncated_chunk = NailgunProtocol.construct_chunk(ChunkType.STDOUT, self.TEST_OUTPUT)[:5]
  97. self.server_sock.sendall(truncated_chunk)
  98. self.server_sock.close()
  99. with self.assertRaises(NailgunProtocol.TruncatedPayloadError):
  100. NailgunProtocol.read_chunk(self.client_sock)
  101. def test_read_chunk_truncated_during_payload(self):
  102. """Construct a chunk and truncate the last 3 bytes of the payload ([:-3])."""
  103. truncated_chunk = NailgunProtocol.construct_chunk(ChunkType.STDOUT, self.TEST_OUTPUT)[:-3]
  104. self.server_sock.sendall(truncated_chunk)
  105. self.server_sock.close()
  106. with self.assertRaises(NailgunProtocol.TruncatedPayloadError):
  107. NailgunProtocol.read_chunk(self.client_sock)
  108. def test_send_start_reading_input(self):
  109. NailgunProtocol.send_start_reading_input(self.server_sock)
  110. chunk_type, payload = NailgunProtocol.read_chunk(self.client_sock)
  111. self.assertEqual(
  112. (chunk_type, payload),
  113. (ChunkType.START_READING_INPUT, self.EMPTY_PAYLOAD)
  114. )
  115. def test_send_stdout(self):
  116. NailgunProtocol.send_stdout(self.server_sock, self.TEST_OUTPUT)
  117. chunk_type, payload = NailgunProtocol.read_chunk(self.client_sock)
  118. self.assertEqual(
  119. (chunk_type, payload),
  120. (ChunkType.STDOUT, self.TEST_OUTPUT)
  121. )
  122. def test_send_stderr(self):
  123. NailgunProtocol.send_stderr(self.server_sock, self.TEST_OUTPUT)
  124. chunk_type, payload = NailgunProtocol.read_chunk(self.client_sock)
  125. self.assertEqual(
  126. (chunk_type, payload),
  127. (ChunkType.STDERR, self.TEST_OUTPUT)
  128. )
  129. def test_send_exit_default(self):
  130. NailgunProtocol.send_exit(self.server_sock)
  131. chunk_type, payload = NailgunProtocol.read_chunk(self.client_sock)
  132. self.assertEqual(
  133. (chunk_type, payload),
  134. (ChunkType.EXIT, self.EMPTY_PAYLOAD)
  135. )
  136. def test_send_exit(self):
  137. NailgunProtocol.send_exit(self.server_sock, self.TEST_OUTPUT)
  138. chunk_type, payload = NailgunProtocol.read_chunk(self.client_sock)
  139. self.assertEqual(
  140. (chunk_type, payload),
  141. (ChunkType.EXIT, self.TEST_OUTPUT)
  142. )
  143. def test_send_unicode_chunk(self):
  144. NailgunProtocol.send_stdout(self.server_sock, self.TEST_UNICODE_PAYLOAD)
  145. chunk_type, payload = NailgunProtocol.read_chunk(self.client_sock, return_bytes=True)
  146. self.assertEqual(
  147. (chunk_type, payload),
  148. (ChunkType.STDOUT, self.TEST_UNICODE_PAYLOAD)
  149. )
  150. def test_isatty_from_empty_env(self):
  151. self.assertEquals(NailgunProtocol.isatty_from_env({}), (False, False, False))
  152. def test_isatty_from_env(self):
  153. self.assertEquals(
  154. NailgunProtocol.isatty_from_env({
  155. 'NAILGUN_TTY_0': '1',
  156. 'NAILGUN_TTY_1': '0',
  157. 'NAILGUN_TTY_2': '1'
  158. }),
  159. (True, False, True)
  160. )
  161. def test_isatty_from_env_mixed(self):
  162. self.assertEquals(
  163. NailgunProtocol.isatty_from_env({
  164. 'NAILGUN_TTY_0': '0',
  165. 'NAILGUN_TTY_1': '1'
  166. }),
  167. (False, True, False)
  168. )
  169. def test_construct_chunk(self):
  170. with self.assertRaises(TypeError):
  171. NailgunProtocol.construct_chunk(ChunkType.STDOUT, 1111)
  172. def test_construct_chunk_unicode(self):
  173. NailgunProtocol.construct_chunk(ChunkType.STDOUT, u'Ø')
  174. def test_construct_chunk_bytes(self):
  175. NailgunProtocol.construct_chunk(ChunkType.STDOUT, b'yes')