def _verify_request( self, *, variables: VariableMap, lsp_id: Json, entry: TranscriptEntry, request: "_RequestSpec", ) -> Optional["_ErrorDescription"]: actual_result = entry.received.get("result") actual_powered_by = entry.received.get("powered_by") if request.comment is not None: request_description = ( f"Request with ID {lsp_id!r} (comment: {request.comment!r})" ) else: request_description = f"Request with ID {lsp_id!r}" # Because of the way hack allocates a different HHI folder for each running # process, let's replace the standard HHI foldername actual_result = fixup_hhi_json(actual_result) expected_result = interpolate_variables( payload=request.result, variables=variables ) expected_result = fixup_hhi_json(expected_result) if actual_result != expected_result: error_description = self._pretty_print_diff( actual=actual_result, expected=expected_result ) description = f"""\ {request_description} got an incorrect result: {error_description}""" request_context = self._get_context_for_traceback(request.traceback) context = f"""\ This was the associated request: {request_context}""" remediation = self._describe_response_for_remediation( variables=variables, request=request, actual_response=entry.received ) return _ErrorDescription( description=description, context=context, remediation=remediation ) elif entry.received.get("powered_by") != request.powered_by: description = f"""\ {request_description} had an incorrect value for the `powered_by` field (expected {request.powered_by!r}; got {actual_powered_by!r}) """ request_context = self._get_context_for_traceback(request.traceback) context = f"""\ This was the associated request: {request_context}""" remediation = self._describe_response_for_remediation( variables=variables, request=request, actual_response=entry.received ) return _ErrorDescription( description=description, context=context, remediation=remediation )
def test_interpolate_variables_multiple(self) -> None: variables = {"foo": "bar", "baz": "qux"} payload = { "hello": "${foo} ${baz}", "nested": { "foo": "${foo}" }, "in key: ${baz}": True, } expected = { "hello": "bar qux", "nested": { "foo": "bar" }, "in key: qux": True } self.assertEqual(interpolate_variables(payload, variables), expected) self.assertEqual(uninterpolate_variables(expected, variables), payload)
def _flag_unhandled_messages( self, handled_entries: AbstractSet[str], variables: VariableMap, transcript: Transcript, # pyre-fixme[11]: Annotation `_LspIdMap` is not defined as a type. lsp_id_map: _LspIdMap, ) -> Iterable["_ErrorDescription"]: for transcript_id, entry in transcript.items(): if transcript_id in handled_entries: continue received = entry.received if received is None: continue if entry.sent is not None: # We received a request and responded to it. continue method = received["method"] params = received["params"] payload = self._pretty_print_snippet(received) if "id" in received: description = f"""\ An unexpected request of type {method!r} was sent by the language server. Here is the request payload: {payload} """ at_nocommit = "@" + "nocommit" remediation = f"""\ 1) If this was unexpected, then the language server is buggy and should be fixed. 2) If all requests of type {method!r} with theses params should be ignored, add this directive anywhere in your test: .{self.ignore_requests.__name__}(method={method!r}, params={params!r}) 3) To handle this request, add this directive to your test to wait for it and respond to it before proceeding: .{self.wait_for_server_request.__name__}( method={method!r}, params={params!r}, result={{ "{at_nocommit}": "fill in request data here", }}, ) """ else: if any( isinstance(message, _WaitForNotificationSpec) and message.method == method and interpolate_variables(payload=message.params, variables=variables) == params for message in self._messages): # This was a notification we we explicitly waiting for, so skip # it. continue uninterpolated_params = uninterpolate_variables( payload=params, variables=variables) description = f"""\ An unexpected notification of type {method!r} was sent by the language server. Here is the notification payload: {payload} """ remediation = f"""\ 1) If this was unexpected, then the language server is buggy and should be fixed. 2) If all notifications of type {method!r} should be ignored, add this directive anywhere in your test: .{self.ignore_notifications.__name__}(method={method!r}) 3) If this single instance of the notification was expected, add this directive to your test to wait for it before proceeding: .{self.wait_for_notification.__name__}( method={method!r}, params={uninterpolated_params!r}, ) """ previous_request = self._find_previous_request( transcript, lsp_id_map, current_id=transcript_id) if previous_request is not None: request_context = self._get_context_for_call_site_info( previous_request.call_site_info) else: request_context = "<no previous request was found>" context = f"""\ This was the most recent request issued from the language client before it received the notification: {request_context}""" yield _ErrorDescription(description=description, context=context, remediation=remediation)
def _get_json_commands( self, variables: VariableMap # pyre-fixme[11]: Annotation `_LspIdMap` is not defined as a type. ) -> Tuple[Sequence[Json], "_LspIdMap"]: """Transforms this test spec into something the LSP command processor can interpret.""" json_commands = [] lsp_id_map = {} current_id = 0 for message in self._messages: current_id += 1 lsp_id_map[message] = current_id if isinstance(message, _RequestSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "id": current_id, "method": message.method, "params": interpolate_variables(message.params, variables=variables), }) if message.wait_id is None: # Assume that if no wait ID was explicitly passed, we want # to wait on the response before sending the next message. json_commands.append({ "jsonrpc": "2.0", "method": "$test/waitForResponse", "params": { "id": current_id }, }) elif isinstance(message, _DebugRequestSpec): json_commands.append({ "jsonrpc": "2.0", "id": current_id, "method": "telemetry/rage", "params": {}, }) elif isinstance(message, _NotificationSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "method": message.method, "params": interpolate_variables(message.params, variables=variables), }) elif isinstance(message, _WaitForRequestSpec): params = { "method": message.method, "params": interpolate_variables(message.params, variables=variables), } if not isinstance(message.result, NoResponse): params["result"] = message.result json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "method": "$test/waitForRequest", "params": params, }) elif isinstance(message, _WaitForNotificationSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "method": "$test/waitForNotification", "params": { "method": message.method, "params": interpolate_variables(message.params, variables=variables), }, }) elif isinstance(message, _WaitForResponseSpec): lsp_ids = [ lsp_id for previous_message, lsp_id in lsp_id_map.items() if isinstance(previous_message, _RequestSpec) and previous_message.wait_id == message.wait_id ] assert len(lsp_ids) == 1, ( f"Should have had exactly one previous message with wait ID {message.wait_id!r}, " + "but got {len(lsp_ids)}") [lsp_id] = lsp_ids json_commands.append({ "jsonrpc": "2.0", "method": "$test/waitForResponse", "params": { "id": lsp_id }, }) elif isinstance(message, _WaitForHhServerReadySpec): json_commands.append({ "jsonrpc": "2.0", "method": "$test/waitForHhServerReady", "params": {}, }) else: raise ValueError( f"unhandled message type {message.__class__.__name__}") return (json_commands, lsp_id_map)
def parse_test_data(self, file: str, variables: Mapping[str, str]) -> Json: text = self.read_repo_file(file) data: Json = json.loads(text) data = interpolate_variables(data, variables) return data
def _get_json_commands( self, variables: Mapping[str, str]) -> Tuple[Sequence[Json], "_LspIdMap"]: """Transforms this test spec into something the LSP command processor can interpret.""" json_commands = [] lsp_id_map = {} current_id = 0 for message in self._messages: current_id += 1 lsp_id_map[message] = current_id if isinstance(message, _RequestSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "id": current_id, "method": message.method, "params": interpolate_variables(message.params, variables=variables), }) if message.wait: json_commands.append({ "jsonrpc": "2.0", "method": "$test/waitForResponse", "params": { "id": current_id }, }) elif isinstance(message, _NotificationSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "method": message.method, "params": interpolate_variables(message.params, variables=variables), }) elif isinstance(message, _WaitForRequestSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "method": "$test/waitForRequest", "params": { "method": message.method, "params": message.params, "result": message.result, }, }) elif isinstance(message, _WaitForNotificationSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "method": "$test/waitForNotification", "params": { "method": message.method, "params": message.params }, }) else: raise ValueError( f"unhandled message type {message.__class__.__name__}") return (json_commands, lsp_id_map)
def _get_json_commands( self, variables: VariableMap) -> Tuple[Sequence[Json], "_LspIdMap"]: """Transforms this test spec into something the LSP command processor can interpret.""" json_commands = [] lsp_id_map = {} current_id = 0 for message in self._messages: current_id += 1 lsp_id_map[message] = current_id if isinstance(message, _RequestSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "id": current_id, "method": message.method, "params": interpolate_variables(message.params, variables=variables), }) if message.wait_id is None: # Assume that if no wait ID was explicitly passed, we want # to wait on the response before sending the next message. json_commands.append({ "jsonrpc": "2.0", "method": "$test/waitForResponse", "params": { "id": current_id }, }) elif isinstance(message, _DebugRequestSpec): json_commands.append({ "jsonrpc": "2.0", "id": current_id, "method": "telemetry/rage", "params": {}, }) elif isinstance(message, _NotificationSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "method": message.method, "params": interpolate_variables(message.params, variables=variables), }) elif isinstance(message, _WaitForRequestSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "method": "$test/waitForRequest", "params": { "method": message.method, "params": message.params, "result": message.result, }, }) elif isinstance(message, _WaitForNotificationSpec): json_commands.append({ "jsonrpc": "2.0", "comment": message.comment, "method": "$test/waitForNotification", "params": { "method": message.method, "params": message.params }, }) elif isinstance(message, _WaitForResponseSpec): [lsp_id] = [ lsp_id for previous_message, lsp_id in lsp_id_map.items() if isinstance(previous_message, _RequestSpec) and previous_message.wait_id == message.wait_id ] json_commands.append({ "jsonrpc": "2.0", "method": "$test/waitForResponse", "params": { "id": lsp_id }, }) elif isinstance(message, _WaitForHhServerReadySpec): json_commands.append({ "jsonrpc": "2.0", "method": "$test/waitForHhServerReady", "params": {}, }) else: raise ValueError( f"unhandled message type {message.__class__.__name__}") return (json_commands, lsp_id_map)
def test_interpolate_variables_simple(self) -> None: variables = {"foo": "bar"} payload = {"hello": "hi ${foo} hi"} expected = {"hello": "hi bar hi"} self.assertEqual(interpolate_variables(payload, variables), expected) self.assertEqual(uninterpolate_variables(expected, variables), payload)
def test_undefined_variable_raises_exception(self) -> None: variables = {} payload = {"hello": "bar ${foo} bar"} with self.assertRaises(ValueError): interpolate_variables(payload, variables)