From c287ff13e166edf7a191fbf5482a125648b8986a Mon Sep 17 00:00:00 2001 From: Michal Bali <38988507+michalbali256@users.noreply.github.com> Date: Thu, 17 Dec 2020 21:55:34 +0100 Subject: [PATCH] feat: Diagnostics suppression for files with no pgm_conf configuration (#89) --- clients/vscode-hlasmplugin/CHANGELOG.md | 8 + clients/vscode-hlasmplugin/README.md | 19 ++ clients/vscode-hlasmplugin/package.json | 5 + clients/vscode-hlasmplugin/pgm_conf_schema | 6 + language_server/src/dap/dap_server.cpp | 7 + language_server/src/dap/dap_server.h | 11 +- language_server/src/feature.h | 1 + .../src/lsp/feature_workspace_folders.cpp | 37 ++- .../src/lsp/feature_workspace_folders.h | 13 +- language_server/src/lsp/lsp_server.cpp | 64 +++- language_server/src/lsp/lsp_server.h | 16 +- language_server/src/server.h | 1 + .../test/dap/feature_launch_test.cpp | 1 + language_server/test/dispatcher_test.cpp | 1 + language_server/test/lsp/lsp_server_test.cpp | 115 ++++++- .../test/lsp/workspace_folders_test.cpp | 80 ++++- language_server/test/request_manager_test.cpp | 1 + language_server/test/response_provider_mock.h | 1 + language_server/test/ws_mngr_mock.h | 4 +- parser_library/include/lib_config.h | 56 ++++ parser_library/include/message_consumer.h | 41 +++ parser_library/include/workspace_manager.h | 8 +- parser_library/src/lib_config.cpp | 63 ++++ parser_library/src/workspace_manager.cpp | 9 +- parser_library/src/workspace_manager_impl.h | 19 +- parser_library/src/workspaces/file_impl.cpp | 4 + parser_library/src/workspaces/workspace.cpp | 168 +++++++--- parser_library/src/workspaces/workspace.h | 41 ++- .../test/debugging/debugger_test.cpp | 10 +- parser_library/test/message_consumer_mock.h | 33 ++ .../test_wks/{ => .hlasmplugin}/pgm_conf.json | 4 + .../{ => .hlasmplugin}/proc_grps.json | 0 .../test/workspace/diags_suppress_test.cpp | 178 ++++++++++ parser_library/test/workspace/empty_configs.h | 25 ++ .../test/workspace/load_config_test.cpp | 306 ++++++++++++++++++ .../test/workspace/workspace_test.cpp | 237 +------------- .../test/workspace_manager_test.cpp | 29 +- 37 files changed, 1303 insertions(+), 319 deletions(-) create mode 100644 parser_library/include/lib_config.h create mode 100644 parser_library/include/message_consumer.h create mode 100644 parser_library/src/lib_config.cpp create mode 100644 parser_library/test/message_consumer_mock.h rename parser_library/test/res/test_wks/{ => .hlasmplugin}/pgm_conf.json (69%) rename parser_library/test/res/test_wks/{ => .hlasmplugin}/proc_grps.json (100%) create mode 100644 parser_library/test/workspace/diags_suppress_test.cpp create mode 100644 parser_library/test/workspace/empty_configs.h create mode 100644 parser_library/test/workspace/load_config_test.cpp diff --git a/clients/vscode-hlasmplugin/CHANGELOG.md b/clients/vscode-hlasmplugin/CHANGELOG.md index 07f456497..7beb3fa8e 100644 --- a/clients/vscode-hlasmplugin/CHANGELOG.md +++ b/clients/vscode-hlasmplugin/CHANGELOG.md @@ -2,6 +2,14 @@ ## ****Unreleased**** +#### Added +- Diagnostic suppression for files that have no processor group configuration. +- Support for light and contrast themes. + +#### Fixed +- Parsing issues regarding conditional assembly expressions and attribute lookahead. +- Several issues causing the extension to crash. + ## [0.11.1] - 2020-11-09 #### Fixed diff --git a/clients/vscode-hlasmplugin/README.md b/clients/vscode-hlasmplugin/README.md index 41bb0ab77..d5e1b5f9b 100644 --- a/clients/vscode-hlasmplugin/README.md +++ b/clients/vscode-hlasmplugin/README.md @@ -182,6 +182,25 @@ The following example of `pgm_conf.json` specifies that the processor group `GRO } ``` +### Suppression of Diagnostics + +For files, that use macros extensively, but do not have the definitions available, it is very probable that diagnostics reported by HLASM Language support will not be helpful. For those cases, there is the setting `diagnosticsSuppressLimit`, which can be set either in editor settings, or in `pgm_conf.json`. For files with no processor group configuration in `pgm_conf.json`, all the diagnostics will be suppressed, if there is too many of them (more than the configured limit). + +``` +{ + "pgms": [ + { + "program": "source_code", + "pgroup": "GROUP1" + } + ], + "diagnosticsSuppressLimit" : 15 +} +``` +With the `pgm_conf.json` above, the `source_code` file has configuration, so all discovered diagnostics will be always shown. However, if you open another file and do not assign a processor group to it, its diagnostcs will not be shown, if there are more than 15 of them. + + + ## Questions, issues, feature requests, and contributions - If you have a question about how to accomplish something with the extension, or come across a problem file an issue on [GitHub](https://github.com/eclipse/che-che4z-lsp-for-hlasm) - Contributions are always welcome! Please see our [GitHub](https://github.com/eclipse/che-che4z-lsp-for-hlasm) repository for more information. diff --git a/clients/vscode-hlasmplugin/package.json b/clients/vscode-hlasmplugin/package.json index 7ee295fc4..037ccb3b4 100644 --- a/clients/vscode-hlasmplugin/package.json +++ b/clients/vscode-hlasmplugin/package.json @@ -332,6 +332,11 @@ "type": "boolean", "default": false, "description": "Disable in case you experience lags when typing. Note: Extension will be restarted upon changing this option." + }, + "hlasm.diagnosticsSuppressLimit": { + "type": "integer", + "default": 10, + "description": "This option limits number of diagnostics shown for an open code when there is no configuration in pgm_conf.json." } } } diff --git a/clients/vscode-hlasmplugin/pgm_conf_schema b/clients/vscode-hlasmplugin/pgm_conf_schema index d4f30aa14..909848e90 100644 --- a/clients/vscode-hlasmplugin/pgm_conf_schema +++ b/clients/vscode-hlasmplugin/pgm_conf_schema @@ -28,6 +28,12 @@ "type" : "string", "description" : "Wildcard" } + }, + "diagnosticsSuppressLimit": + { + "description":"An integer that configures the limit of diagnostics shown when there is no configuration available for the opened file.\nFor files, that use macros extensively, but do not have the definitions available, it is very probable that diagnostics reported by HLASM Language support will not be helpful. That is why we provide the option to suppress the diagnostics if there are too many of them. Set to zero to disable all diagnostics for files without configuration.", + "type": "integer", + "minimum" : 0 } }, "required" : ["pgms"] diff --git a/language_server/src/dap/dap_server.cpp b/language_server/src/dap/dap_server.cpp index f3be46766..9738f0c4c 100644 --- a/language_server/src/dap/dap_server.cpp +++ b/language_server/src/dap/dap_server.cpp @@ -31,6 +31,13 @@ server::server(parser_library::workspace_manager& ws_mngr) register_methods(); } +void server::request(const json&, const std::string&, const json&, method) +{ + // Currently, there are no supported DAP requests from client to server + /*send_message_->reply(json { + { "seq", request_seq }, { "type", "request" }, { "command", requested_command }, { "arguments", args } });*/ +} + void server::respond(const json& request_seq, const std::string& requested_command, const json& args) { send_message_->reply(json { { "seq", ++last_seq_ }, diff --git a/language_server/src/dap/dap_server.h b/language_server/src/dap/dap_server.h index b60479151..f6c7fc980 100644 --- a/language_server/src/dap/dap_server.h +++ b/language_server/src/dap/dap_server.h @@ -35,18 +35,19 @@ class server : public hlasm_plugin::language_server::server public: explicit server(parser_library::workspace_manager& ws_mngr); - // Inherited via server - virtual void respond(const json& id, const std::string& requested_method, const json& args) override; + void request(const json& id, const std::string& requested_method, const json& args, method handler) override; - virtual void notify(const std::string& method, const json& args) override; + void respond(const json& id, const std::string& requested_method, const json& args) override; - virtual void respond_error(const json& id, + void notify(const std::string& method, const json& args) override; + + void respond_error(const json& id, const std::string& requested_method, int err_code, const std::string& err_message, const json& error) override; - virtual void message_received(const json& message) override; + void message_received(const json& message) override; diff --git a/language_server/src/feature.h b/language_server/src/feature.h index 61332940a..6b751dac6 100644 --- a/language_server/src/feature.h +++ b/language_server/src/feature.h @@ -29,6 +29,7 @@ namespace hlasm_plugin::language_server { class response_provider { public: + virtual void request(const json& id, const std::string& requested_method, const json& args, method handler) = 0; virtual void respond(const json& id, const std::string& requested_method, const json& args) = 0; virtual void notify(const std::string& method, const json& args) = 0; virtual void respond_error(const json& id, diff --git a/language_server/src/lsp/feature_workspace_folders.cpp b/language_server/src/lsp/feature_workspace_folders.cpp index cd3251964..e79f338ac 100644 --- a/language_server/src/lsp/feature_workspace_folders.cpp +++ b/language_server/src/lsp/feature_workspace_folders.cpp @@ -19,11 +19,14 @@ #include "network/uri/uri.hpp" #include "../logger.h" +#include "lib_config.h" + namespace hlasm_plugin::language_server::lsp { -feature_workspace_folders::feature_workspace_folders(parser_library::workspace_manager& ws_mngr) - : feature(ws_mngr) +feature_workspace_folders::feature_workspace_folders( + parser_library::workspace_manager& ws_mngr, response_provider& response_provider) + : feature(ws_mngr, response_provider) {} void feature_workspace_folders::register_methods(std::map& methods) @@ -36,6 +39,9 @@ void feature_workspace_folders::register_methods(std::map& methods.emplace("workspace/didChangeWatchedFiles", std::bind( &feature_workspace_folders::did_change_watched_files, this, std::placeholders::_1, std::placeholders::_2)); + methods.emplace("workspace/didChangeConfiguration", + std::bind( + &feature_workspace_folders::did_change_configuration, this, std::placeholders::_1, std::placeholders::_2)); } json feature_workspace_folders::register_capabilities() @@ -46,6 +52,9 @@ json feature_workspace_folders::register_capabilities() void feature_workspace_folders::initialize_feature(const json& initialize_params) { + // Get config at initialization + send_configuration_request(); + bool ws_folders_support = false; auto capabs = initialize_params["capabilities"]; auto ws = capabs.find("workspace"); @@ -134,4 +143,28 @@ void feature_workspace_folders::did_change_watched_files(const json&, const json paths.begin(), paths.end(), std::back_inserter(c_uris), [](const std::string& s) { return s.c_str(); }); ws_mngr_.did_change_watched_files(c_uris.data(), c_uris.size()); } + +void feature_workspace_folders::send_configuration_request() +{ + static const json config_request_args { { "items", { { { "section", "hlasm" } } } } }; + response_->request("config_request_" + std::to_string(config_request_number_), + "workspace/configuration", + config_request_args, + std::bind(&feature_workspace_folders::configuration, this, std::placeholders::_1, std::placeholders::_2)); + ++config_request_number_; +} + +void feature_workspace_folders::configuration(const json&, const json& params) const +{ + if (params.size() == 0) + { + LOG_WARNING("Empty configuration response received."); + return; + } + + ws_mngr_.configuration_changed(parser_library::lib_config::load_from_json(params[0])); +} + +void feature_workspace_folders::did_change_configuration(const json&, const json&) { send_configuration_request(); } + } // namespace hlasm_plugin::language_server::lsp diff --git a/language_server/src/lsp/feature_workspace_folders.h b/language_server/src/lsp/feature_workspace_folders.h index 34c10f887..898315966 100644 --- a/language_server/src/lsp/feature_workspace_folders.h +++ b/language_server/src/lsp/feature_workspace_folders.h @@ -28,9 +28,10 @@ namespace hlasm_plugin::language_server::lsp { class feature_workspace_folders : public feature { public: - explicit feature_workspace_folders(parser_library::workspace_manager& ws_mngr); + explicit feature_workspace_folders( + parser_library::workspace_manager& ws_mngr, response_provider& response_provider); - // Adds workspace/didChangeWorkspaceFolders method to the map. + // Adds workspace/* methods to the map. void register_methods(std::map&) override; // Returns workspaces capability. json virtual register_capabilities() override; @@ -41,6 +42,11 @@ class feature_workspace_folders : public feature // Handles workspace/didChangeWorkspaceFolders notification. void on_did_change_workspace_folders(const json& id, const json& params); void did_change_watched_files(const json&, const json& params); + + void configuration(const json&, const json& params) const; + void did_change_configuration(const json&, const json& params); + + // Adds all workspaces specified in the json to the workspace manager. void add_workspaces(const json& added); @@ -49,6 +55,9 @@ class feature_workspace_folders : public feature // Adds one workspace to the workspace manager. void add_workspace(const std::string& name, const std::string& uri); + + uint64_t config_request_number_ = 0; + void send_configuration_request(); }; } // namespace hlasm_plugin::language_server::lsp diff --git a/language_server/src/lsp/lsp_server.cpp b/language_server/src/lsp/lsp_server.cpp index 1d0c19b71..785d99c77 100644 --- a/language_server/src/lsp/lsp_server.cpp +++ b/language_server/src/lsp/lsp_server.cpp @@ -22,26 +22,64 @@ #include "feature_language_features.h" #include "feature_text_synchronization.h" #include "feature_workspace_folders.h" +#include "lib_config.h" namespace hlasm_plugin::language_server::lsp { server::server(parser_library::workspace_manager& ws_mngr) : language_server::server(ws_mngr) { - features_.push_back(std::make_unique(ws_mngr_)); + features_.push_back(std::make_unique(ws_mngr_, *this)); features_.push_back(std::make_unique(ws_mngr_, *this)); features_.push_back(std::make_unique(ws_mngr_, *this)); register_feature_methods(); register_methods(); ws_mngr_.register_diagnostics_consumer(this); + ws_mngr_.set_message_consumer(this); } void server::message_received(const json& message) { - // we do not support any requests sent from this server - // thus we do not accept any responses auto id_found = message.find("id"); + + + auto result_found = message.find("result"); + auto error_result_found = message.find("error"); + + if (result_found != message.end()) + { + // we received a response to our request that was successful + if (id_found == message.end()) + { + LOG_WARNING("A response with no id field received."); + return; + } + + auto handler_found = request_handlers_.find(*id_found); + if (handler_found == request_handlers_.end()) + { + LOG_WARNING("A response with no registered handler received."); + return; + } + + method handler = handler_found->second; + request_handlers_.erase(handler_found); + handler(id_found.value(), result_found.value()); + return; + } + else if (error_result_found != message.end()) + { + auto message_found = error_result_found->find("message"); + std::string warn_message; + if (message_found != error_result_found->end()) + warn_message = message_found->dump(); + else + warn_message = "Request with id " + id_found->dump() + " returned with unspecified error."; + LOG_WARNING(warn_message); + return; + } + auto params_found = message.find("params"); auto method_found = message.find("method"); @@ -71,6 +109,13 @@ void server::message_received(const json& message) } } +void server::request(const json& id, const std::string& requested_method, const json& args, method handler) +{ + json reply { { "jsonrpc", "2.0" }, { "id", id }, { "method", requested_method }, { "params", args } }; + request_handlers_.emplace(id, handler); + send_message_->reply(reply); +} + void server::respond(const json& id, const std::string&, const json& args) { json reply { { "jsonrpc", "2.0" }, { "id", id }, { "result", args } }; @@ -100,6 +145,11 @@ void server::register_methods() methods_.emplace("exit", std::bind(&server::on_exit, this, std::placeholders::_1, std::placeholders::_2)); } +void empty_handler(json, const json&) +{ + // Does nothing +} + void server::on_initialize(json id, const json& param) { // send server capabilities back @@ -124,6 +174,12 @@ void server::on_initialize(json id, const json& param) respond(id, "", capabilities); + json register_configuration_changed_args { + { { "registrations", { { { "id", "configureRegister" }, { "method", "workspace/didChangeConfiguration" } } } } } + }; + + request("register1", "client/registerCapability", register_configuration_changed_args, &empty_handler); + for (auto& f : features_) { @@ -142,7 +198,7 @@ void server::on_shutdown(json id, const json&) void server::on_exit(json, const json&) { exit_notification_received_ = true; } -void server::show_message(const std::string& message, message_type type) +void server::show_message(const std::string& message, parser_library::message_type type) { json m { { "type", (int)type }, { "message", message } }; notify("window/showMessage", m); diff --git a/language_server/src/lsp/lsp_server.h b/language_server/src/lsp/lsp_server.h index 0ca253fda..5f0747481 100644 --- a/language_server/src/lsp/lsp_server.h +++ b/language_server/src/lsp/lsp_server.h @@ -28,18 +28,12 @@ namespace hlasm_plugin::language_server::lsp { -enum class message_type -{ - MT_ERROR = 1, - MT_WARNING = 2, - MT_INFO = 3, - MT_LOG = 4 -}; - // Implements LSP server (session-controlling methods like initialize and shutdown). // Integrates 3 features: language features, text synchronization and workspace folders. // Consumes diagnostics that come from the parser library and sends them to LSP client. -class server final : public hlasm_plugin::language_server::server, public parser_library::diagnostics_consumer +class server final : public hlasm_plugin::language_server::server, + public parser_library::diagnostics_consumer, + public parser_library::message_consumer { public: // Creates the server with workspace_manager as entry point to parser library. @@ -49,6 +43,8 @@ class server final : public hlasm_plugin::language_server::server, public parser void message_received(const json& message) override; protected: + // Sends request to LSP client using send_message_provider. + void request(const json& id, const std::string& requested_method, const json& args, method handler) override; // Sends respond to request to LSP client using send_message_provider. void respond(const json& id, const std::string& requested_method, const json& args) override; // Sends notification to LSP client using send_message_provider. @@ -78,7 +74,7 @@ class server final : public hlasm_plugin::language_server::server, public parser // client notifications // Implements the LSP showMessage request. - void show_message(const std::string& message, message_type type); + void show_message(const std::string& message, parser_library::message_type type) override; // Remembers name of files for which were sent diagnostics the last time // diagnostics were sent to client. Used to clear diagnostics in client diff --git a/language_server/src/server.h b/language_server/src/server.h index 5b569a193..b1d8146f5 100644 --- a/language_server/src/server.h +++ b/language_server/src/server.h @@ -58,6 +58,7 @@ class server : public response_provider std::vector> features_; std::map methods_; + std::unordered_map request_handlers_; bool shutdown_request_received_ = false; bool exit_notification_received_ = false; diff --git a/language_server/test/dap/feature_launch_test.cpp b/language_server/test/dap/feature_launch_test.cpp index 27275f745..570cd223c 100644 --- a/language_server/test/dap/feature_launch_test.cpp +++ b/language_server/test/dap/feature_launch_test.cpp @@ -55,6 +55,7 @@ struct notif_mock struct response_provider_mock : public response_provider { + void request(const json&, const std::string&, const json&, method) override {}; void respond(const json& id, const std::string& requested_method, const json& args) override { responses.push_back({ id, requested_method, args }); diff --git a/language_server/test/dispatcher_test.cpp b/language_server/test/dispatcher_test.cpp index 138480ac3..b8f0e5501 100644 --- a/language_server/test/dispatcher_test.cpp +++ b/language_server/test/dispatcher_test.cpp @@ -36,6 +36,7 @@ class server_mock : public server std::vector messages; + virtual void request(const json&, const std::string&, const json&, method) override {} virtual void respond(const json&, const std::string&, const json&) override {} virtual void notify(const std::string&, const json&) override {} virtual void respond_error(const json&, const std::string&, int, const std::string&, const json&) override {} diff --git a/language_server/test/lsp/lsp_server_test.cpp b/language_server/test/lsp/lsp_server_test.cpp index 68b36c944..04c1003b4 100644 --- a/language_server/test/lsp/lsp_server_test.cpp +++ b/language_server/test/lsp/lsp_server_test.cpp @@ -46,15 +46,21 @@ TEST(lsp_server, initialize) json server_capab; + json second; json show_message = R"({"jsonrpc":"2.0", "method" : "window/showMessage", "params" : {"message":"The capabilities of hlasm language server were sent!", "type" : 3}})"_json; + json register_message = + R"({"jsonrpc":"2.0", "id":"register1", "method" : "client/registerCapability", "params" : [{"registrations":[{"id":"configureRegister", "method":"workspace/didChangeConfiguration"}]}]})"_json; + json config_request_message = + R"({"id":"config_request_0","jsonrpc":"2.0","method":"workspace/configuration","params":{"items":[{"section":"hlasm"}]}})"_json; EXPECT_CALL(smpm, reply(::testing::_)).WillOnce(::testing::SaveArg<0>(&server_capab)); EXPECT_CALL(smpm, reply(show_message)).Times(::testing::AtMost(1)); - s.message_received(j); - + EXPECT_CALL(smpm, reply(register_message)).Times(::testing::AtMost(1)); + EXPECT_CALL(smpm, reply(config_request_message)).Times(::testing::AtMost(1)); + s.message_received(j); EXPECT_NE(server_capab.find("jsonrpc"), server_capab.end()); ASSERT_NE(server_capab.find("id"), server_capab.end()); @@ -62,6 +68,10 @@ TEST(lsp_server, initialize) ASSERT_NE(server_capab.find("result"), server_capab.end()); EXPECT_NE(server_capab["result"].find("capabilities"), server_capab["result"].end()); + // provide response to the register request + json register_response = R"({"jsonrpc":"2.0","id":"register1","result":null})"_json; + s.message_received(register_response); + json shutdown_request = R"({"jsonrpc":"2.0","id":48,"method":"shutdown","params":null})"_json; json shutdown_response = R"({"jsonrpc":"2.0","id":48,"result":null})"_json; json exit_notification = R"({"jsonrpc":"2.0","method":"exit","params":null})"_json; @@ -87,6 +97,107 @@ TEST(lsp_server, not_implemented_method) s.message_received(j); // No result is tested, server should ignore unknown LSP method } +class request_handler +{ +public: + void handle(json id, json args) + { + ++counter; + received_id = id; + received_args = args; + } + + int counter = 0; + json received_id; + json received_args; +}; + +TEST(lsp_server, request_correct) +{ + parser_library::workspace_manager mngr; + send_message_provider_mock message_provider; + lsp::server s(mngr); + s.set_send_message_provider(&message_provider); + response_provider& rp = s; + request_handler handler; + + json expected_message = + R"({"id":"a_request","jsonrpc":"2.0","method":"client_method","params":"a_json_parameter"})"_json; + + EXPECT_CALL(message_provider, reply(expected_message)); + + rp.request("a_request", + "client_method", + "a_json_parameter", + std::bind(&request_handler::handle, &handler, std::placeholders::_1, std::placeholders::_2)); + + json request_response = R"({"id":"a_request","jsonrpc":"2.0","result":"response_result"})"_json; + + s.message_received(request_response); + + ASSERT_EQ(handler.counter, 1); + EXPECT_EQ(handler.received_id, "a_request"); + EXPECT_EQ(handler.received_args, "response_result"); +} + +TEST(lsp_server, request_no_handler) +{ + parser_library::workspace_manager mngr; + send_message_provider_mock message_provider; + lsp::server s(mngr); + s.set_send_message_provider(&message_provider); + + json request_response = R"({"id":"a_request","jsonrpc":"2.0","result":"response_result"})"_json; + + EXPECT_CALL(message_provider, reply(::testing::_)).Times(0); + + s.message_received(request_response); +} + +TEST(lsp_server, request_no_id) +{ + parser_library::workspace_manager mngr; + send_message_provider_mock message_provider; + lsp::server s(mngr); + s.set_send_message_provider(&message_provider); + + json request_response = R"({"jsonrpc":"2.0","result":"response_result"})"_json; + + EXPECT_CALL(message_provider, reply(::testing::_)).Times(0); + + s.message_received(request_response); +} + + + +TEST(lsp_server, request_error) +{ + parser_library::workspace_manager mngr; + send_message_provider_mock message_provider; + lsp::server s(mngr); + s.set_send_message_provider(&message_provider); + + json request_response = R"({"id":"a_request","jsonrpc":"2.0","error":{"message":"the_error_message"}})"_json; + + EXPECT_CALL(message_provider, reply(::testing::_)).Times(0); + + s.message_received(request_response); +} + +TEST(lsp_server, request_error_no_message) +{ + parser_library::workspace_manager mngr; + send_message_provider_mock message_provider; + lsp::server s(mngr); + s.set_send_message_provider(&message_provider); + + json request_response = R"({"id":"a_request","jsonrpc":"2.0","error":null})"_json; + + EXPECT_CALL(message_provider, reply(::testing::_)).Times(0); + + s.message_received(request_response); +} + TEST(lsp_server_test, wrong_message_received) { ws_mngr_mock ws_mngr; diff --git a/language_server/test/lsp/workspace_folders_test.cpp b/language_server/test/lsp/workspace_folders_test.cpp index 41c0fcf99..2ebde1dea 100644 --- a/language_server/test/lsp/workspace_folders_test.cpp +++ b/language_server/test/lsp/workspace_folders_test.cpp @@ -15,8 +15,11 @@ #include +#include "../response_provider_mock.h" #include "../ws_mngr_mock.h" +#include "lib_config.h" #include "lsp/feature_workspace_folders.h" + using namespace hlasm_plugin::language_server; #ifdef _WIN32 const std::string ws1_uri = "file:///c%3A/path/to/W%20S/OneDrive"; @@ -47,7 +50,9 @@ const std::string ws1_path_json_string = R"(/path/to/W S/OneDrive)"; TEST(workspace_folders, did_change_workspace_folders) { ws_mngr_mock ws_mngr; - lsp::feature_workspace_folders f(ws_mngr); + response_provider_mock rpm; + + lsp::feature_workspace_folders f(ws_mngr, rpm); EXPECT_CALL(ws_mngr, add_workspace(::testing::StrEq("OneDrive"), ::testing::StrEq(ws1_path))); @@ -79,7 +84,8 @@ TEST(workspace_folders, did_change_workspace_folders) TEST(workspace_folders, did_change_watchedfiles_invalid_uri) { ws_mngr_mock ws_mngr; - lsp::feature_workspace_folders f(ws_mngr); + response_provider_mock rpm; + lsp::feature_workspace_folders f(ws_mngr, rpm); std::map notifs; @@ -92,7 +98,16 @@ TEST(workspace_folders, initialize_folders) { using namespace ::testing; ws_mngr_mock ws_mngr; - lsp::feature_workspace_folders f(ws_mngr); + response_provider_mock rpm; + lsp::feature_workspace_folders f(ws_mngr, rpm); + + for (int config_request_number = 0; config_request_number < 5; ++config_request_number) + EXPECT_CALL(rpm, + request(json("config_request_" + std::to_string(config_request_number)), + std::string("workspace/configuration"), + _, + _)) + .Times(1); // workspace folders on, but no workspaces provided json init1 = R"({"processId":5236, @@ -102,6 +117,7 @@ TEST(workspace_folders, initialize_folders) "workspaceFolders":null})"_json; EXPECT_CALL(ws_mngr, add_workspace(_, _)).Times(0); + f.initialize_feature(init1); @@ -144,3 +160,61 @@ TEST(workspace_folders, initialize_folders) EXPECT_CALL(ws_mngr, add_workspace(_, ::testing::StrEq(ws1_path))); f.initialize_feature(init5); } + +TEST(workspace_folders, did_change_configuration) +{ + using namespace lsp; + ws_mngr_mock ws_mngr; + + response_provider_mock provider; + feature_workspace_folders feat(ws_mngr, provider); + + + std::map methods; + feat.register_methods(methods); + + + method handler; + json config_request_args { { "items", { { { "section", "hlasm" } } } } }; + + EXPECT_CALL( + provider, request(json("config_request_0"), "workspace/configuration", config_request_args, ::testing::_)) + .WillOnce(::testing::SaveArg<3>(&handler)); + + methods["workspace/didChangeConfiguration"]("did_change_configuration_id", "{}"_json); + + lib_config expected_config; + expected_config.diag_supress_limit = 42; + + EXPECT_CALL(ws_mngr, configuration_changed(::testing::Eq(expected_config))); + + handler("config_respond", R"([{"diagnosticsSuppressLimit":42}])"_json); +} + +TEST(workspace_folders, did_change_configuration_empty_configuration_params) +{ + using namespace lsp; + + ws_mngr_mock ws_mngr; + + response_provider_mock provider; + feature_workspace_folders feat(ws_mngr, provider); + + + std::map methods; + feat.register_methods(methods); + + + method handler; + json config_request_args { { "items", { { { "section", "hlasm" } } } } }; + + EXPECT_CALL( + provider, request(json("config_request_0"), "workspace/configuration", config_request_args, ::testing::_)) + .WillOnce(::testing::SaveArg<3>(&handler)); + + methods["workspace/didChangeConfiguration"]("did_change_configuration_id", "{}"_json); + + EXPECT_CALL(ws_mngr, configuration_changed(::testing::_)).Times(0); + + handler("config_respond", R"([])"_json); +} diff --git a/language_server/test/request_manager_test.cpp b/language_server/test/request_manager_test.cpp index c60419e83..857bb9bf8 100644 --- a/language_server/test/request_manager_test.cpp +++ b/language_server/test/request_manager_test.cpp @@ -43,6 +43,7 @@ class server_mock_rm : public server } } + virtual void request(const json&, const std::string&, const json&, method) override {} virtual void respond(const json&, const std::string&, const json&) override {} virtual void notify(const std::string&, const json&) override {} virtual void respond_error(const json&, const std::string&, int, const std::string&, const json&) override {} diff --git a/language_server/test/response_provider_mock.h b/language_server/test/response_provider_mock.h index 131be3e23..73321374a 100644 --- a/language_server/test/response_provider_mock.h +++ b/language_server/test/response_provider_mock.h @@ -21,6 +21,7 @@ namespace hlasm_plugin::language_server { class response_provider_mock : public response_provider { public: + MOCK_METHOD4(request, void(const json& id, const std::string& requested_method, const json& args, method handler)); MOCK_METHOD3(respond, void(const json& id, const std::string& requested_method, const json& args)); MOCK_METHOD2(notify, void(const std::string& method, const json& args)); MOCK_METHOD5(respond_error, diff --git a/language_server/test/ws_mngr_mock.h b/language_server/test/ws_mngr_mock.h index 6b4575446..12dd14299 100644 --- a/language_server/test/ws_mngr_mock.h +++ b/language_server/test/ws_mngr_mock.h @@ -35,9 +35,11 @@ class ws_mngr_mock : public workspace_manager void(const char* document_uri, version_t version, const document_change* changes, size_t ch_size)); MOCK_METHOD1(did_close_file, void(const char* document_uri)); + MOCK_METHOD1(configuration_changed, void(const lib_config& new_config)); + MOCK_METHOD(position_uri, definition, (const char* document_uri, const position pos), (override)); MOCK_METHOD(position_uris, references, (const char* document_uri, const position pos), (override)); - MOCK_METHOD(const string_array, hover, (const char* document_uri, const position pos), (override)); + MOCK_METHOD(string_array, hover, (const char* document_uri, const position pos), (override)); MOCK_METHOD(completion_list, completion, (const char* document_uri, const position pos, const char trigger_char, int trigger_kind), diff --git a/parser_library/include/lib_config.h b/parser_library/include/lib_config.h new file mode 100644 index 000000000..8c799be2b --- /dev/null +++ b/parser_library/include/lib_config.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#ifndef HLASMPLUGIN_PARSERLIBRARY_LIB_CONFIG_H +#define HLASMPLUGIN_PARSERLIBRARY_LIB_CONFIG_H + +#include +#include + +#include "json.hpp" + +#include "parser_library_export.h" +namespace hlasm_plugin::parser_library { + +// Encapsulates user defined settings of library and individual workspaces +class PARSER_LIBRARY_EXPORT lib_config +{ +public: + // Creates an instance of lib_config with values from input json. + [[nodiscard]] static lib_config load_from_json(const nlohmann::json& config); + // Returns a lib_config instance that is a copy of this instance, but the missing settings are replaced with the + // parameter second. If there are more missing settings after that step, they are filled with default values + [[nodiscard]] lib_config fill_missing_settings(const lib_config& second); + + std::optional diag_supress_limit; + + + +private: + // Returns an instance that has missing settings of this filled with not missing setting of the parameter + [[nodiscard]] lib_config combine_two_configs(const lib_config& second) const; + + // Return instance of lib_config with default values. + static lib_config make_default(); + const static lib_config default_config; +}; + +bool operator==(const lib_config& lhs, const lib_config& rhs); + + +} // namespace hlasm_plugin::parser_library + + + +#endif HLASMPLUGIN_PARSERLIBRARY_LIB_CONFIG_H diff --git a/parser_library/include/message_consumer.h b/parser_library/include/message_consumer.h new file mode 100644 index 000000000..746e80566 --- /dev/null +++ b/parser_library/include/message_consumer.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#ifndef HLASMPLUGIN_PARSERLIBRARY_MESSAGE_CONSUMER_H +#define HLASMPLUGIN_PARSERLIBRARY_MESSAGE_CONSUMER_H + +#include + +#include "parser_library_export.h" + +namespace hlasm_plugin::parser_library { + +enum class message_type +{ + MT_ERROR = 1, + MT_WARNING = 2, + MT_INFO = 3, + MT_LOG = 4 +}; + +class PARSER_LIBRARY_EXPORT message_consumer +{ +public: + virtual void show_message(const std::string& message, message_type type) = 0; + virtual ~message_consumer() = default; +}; + +} // namespace hlasm_plugin::parser_library + +#endif diff --git a/parser_library/include/workspace_manager.h b/parser_library/include/workspace_manager.h index 460d68302..16dd186d3 100644 --- a/parser_library/include/workspace_manager.h +++ b/parser_library/include/workspace_manager.h @@ -26,6 +26,8 @@ #include #include +#include "lib_config.h" +#include "message_consumer.h" #include "parser_library_export.h" #include "protocol.h" @@ -97,15 +99,17 @@ class PARSER_LIBRARY_EXPORT workspace_manager virtual position_uri definition(const char* document_uri, const position pos); virtual position_uris references(const char* document_uri, const position pos); - virtual const string_array hover(const char* document_uri, const position pos); + virtual string_array hover(const char* document_uri, const position pos); virtual completion_list completion( const char* document_uri, const position pos, const char trigger_char, int trigger_kind); virtual const std::vector& semantic_tokens(const char* document_uri); + virtual void configuration_changed(const lib_config& new_config); + // implementation of observer pattern - register consumer. Unregistering not implemented (yet). virtual void register_diagnostics_consumer(diagnostics_consumer* consumer); virtual void register_performance_metrics_consumer(performance_metrics_consumer* consumer); - + virtual void set_message_consumer(message_consumer* consumer); // debugger virtual void register_debug_event_consumer(debug_event_consumer& consumer); diff --git a/parser_library/src/lib_config.cpp b/parser_library/src/lib_config.cpp new file mode 100644 index 000000000..26411026d --- /dev/null +++ b/parser_library/src/lib_config.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#include "lib_config.h" + +namespace hlasm_plugin::parser_library { + +const lib_config lib_config::default_config = lib_config::make_default(); + +lib_config lib_config::make_default() +{ + lib_config def_config; + def_config.diag_supress_limit = 10; + + return def_config; +} + +lib_config lib_config::load_from_json(const nlohmann::json& config) +{ + lib_config loaded; + + auto found = config.find("diagnosticsSuppressLimit"); + if (found != config.end()) + { + loaded.diag_supress_limit = found->get(); + if (loaded.diag_supress_limit < 0) + loaded.diag_supress_limit = 0; + } + + + return loaded; +} + +lib_config lib_config::fill_missing_settings(const lib_config& second) +{ + return combine_two_configs(second).combine_two_configs(default_config); +} + +lib_config lib_config::combine_two_configs(const lib_config& second) const +{ + lib_config combined(*this); + if (!combined.diag_supress_limit.has_value()) + combined.diag_supress_limit = second.diag_supress_limit; + return combined; +} + +bool operator==(const lib_config& lhs, const lib_config& rhs) +{ + return lhs.diag_supress_limit == rhs.diag_supress_limit; +} + +} // namespace hlasm_plugin::parser_library diff --git a/parser_library/src/workspace_manager.cpp b/parser_library/src/workspace_manager.cpp index d04f1caa2..96cf7991f 100644 --- a/parser_library/src/workspace_manager.cpp +++ b/parser_library/src/workspace_manager.cpp @@ -77,6 +77,10 @@ void workspace_manager::did_change_file( void workspace_manager::did_close_file(const char* document_uri) { impl_->did_close_file(document_uri); } +void workspace_manager::configuration_changed(const lib_config& new_config) +{ + impl_->configuration_changed(new_config); +} void workspace_manager::register_diagnostics_consumer(diagnostics_consumer* consumer) { @@ -87,6 +91,9 @@ void workspace_manager::register_performance_metrics_consumer(performance_metric { impl_->register_performance_metrics_consumer(consumer); } + +void workspace_manager::set_message_consumer(message_consumer* consumer) { impl_->set_message_consumer(consumer); } + position_uri workspace_manager::definition(const char* document_uri, const position pos) { return impl_->definition(document_uri, pos); @@ -97,7 +104,7 @@ position_uris workspace_manager::references(const char* document_uri, const posi return impl_->references(document_uri, pos); } -const string_array workspace_manager::hover(const char* document_uri, const position pos) +string_array workspace_manager::hover(const char* document_uri, const position pos) { return impl_->hover(document_uri, pos); } diff --git a/parser_library/src/workspace_manager_impl.h b/parser_library/src/workspace_manager_impl.h index 2d2e16d2a..31cfe6191 100644 --- a/parser_library/src/workspace_manager_impl.h +++ b/parser_library/src/workspace_manager_impl.h @@ -31,7 +31,7 @@ class workspace_manager::impl : public diagnosable_impl, public debugging::debug public: impl(std::atomic* cancel = nullptr) : file_manager_(cancel) - , implicit_workspace_({ file_manager_ }) + , implicit_workspace_(file_manager_, global_config_, cancel) , cancel_(cancel) {} impl(const impl&) = delete; @@ -55,7 +55,8 @@ class workspace_manager::impl : public diagnosable_impl, public debugging::debug void add_workspace(std::string name, std::string uri) { - auto ws = workspaces_.emplace(name, workspaces::workspace(uri, name, file_manager_)); + auto ws = workspaces_.emplace(name, workspaces::workspace(uri, name, file_manager_, global_config_, cancel_)); + ws.first->second.set_message_consumer(message_consumer_); ws.first->second.open(); notify_diagnostics_consumers(); @@ -123,6 +124,14 @@ class workspace_manager::impl : public diagnosable_impl, public debugging::debug metrics_consumers_.push_back(consumer); } + void set_message_consumer(message_consumer* consumer) + { + message_consumer_ = consumer; + implicit_workspace_.set_message_consumer(consumer); + for (auto& wks : workspaces_) + wks.second.set_message_consumer(consumer); + } + semantics::position_uri_s found_position; position_uri definition(std::string document_uri, const position pos) { @@ -153,7 +162,7 @@ class workspace_manager::impl : public diagnosable_impl, public debugging::debug std::vector output; std::vector coutput; - const string_array hover(const char* document_uri, const position pos) + string_array hover(const char* document_uri, const position pos) { coutput.clear(); if (cancel_ && *cancel_) @@ -186,6 +195,9 @@ class workspace_manager::impl : public diagnosable_impl, public debugging::debug return completion_result; } + lib_config global_config_; + virtual void configuration_changed(const lib_config& new_config) { global_config_ = new_config; } + std::vector empty_tokens; const std::vector& semantic_tokens(const char* document_uri) { @@ -385,6 +397,7 @@ class workspace_manager::impl : public diagnosable_impl, public debugging::debug std::vector diag_consumers_; std::vector metrics_consumers_; + message_consumer* message_consumer_ = nullptr; }; } // namespace hlasm_plugin::parser_library diff --git a/parser_library/src/workspaces/file_impl.cpp b/parser_library/src/workspaces/file_impl.cpp index cd1b25e37..34cd87ea8 100644 --- a/parser_library/src/workspaces/file_impl.cpp +++ b/parser_library/src/workspaces/file_impl.cpp @@ -196,6 +196,10 @@ version_t file_impl::get_version() { return version_; } bool file_impl::update_and_get_bad() { + // If user is editing file through LSP, do not load from disk. + if (editing_) + return false; + load_text(); return bad_; } diff --git a/parser_library/src/workspaces/workspace.cpp b/parser_library/src/workspaces/workspace.cpp index 0762a35ab..f12305512 100644 --- a/parser_library/src/workspaces/workspace.cpp +++ b/parser_library/src/workspaces/workspace.cpp @@ -18,30 +18,38 @@ #include #include -#include "json.hpp" - +#include "lib_config.h" #include "processor.h" #include "wildcard.h" +using json = nlohmann::json; + namespace hlasm_plugin::parser_library::workspaces { -workspace::workspace(ws_uri uri, std::string name, file_manager& file_manager) - : name_(name) +workspace::workspace(const ws_uri& uri, + const std::string& name, + file_manager& file_manager, + const lib_config& global_config, + std::atomic* cancel) + : cancel_(cancel) + , name_(name) , uri_(uri) , file_manager_(file_manager) , implicit_proc_grp("pg_implicit") , ws_path_(uri) + , global_config_(global_config) { proc_grps_path_ = ws_path_ / HLASM_PLUGIN_FOLDER / FILENAME_PROC_GRPS; pgm_conf_path_ = ws_path_ / HLASM_PLUGIN_FOLDER / FILENAME_PGM_CONF; } -workspace::workspace(ws_uri uri, file_manager& file_manager) - : workspace(uri, uri, file_manager) +workspace::workspace( + const ws_uri& uri, file_manager& file_manager, const lib_config& global_config, std::atomic* cancel) + : workspace(uri, uri, file_manager, global_config, cancel) {} -workspace::workspace(file_manager& file_manager) - : workspace("", file_manager) +workspace::workspace(file_manager& file_manager, const lib_config& global_config, std::atomic* cancel) + : workspace("", file_manager, global_config, cancel) { opened_ = true; } @@ -65,6 +73,31 @@ bool workspace::program_id_match(const std::string& filename, const program_id& return std::regex_match(filename, prg_regex); } +void workspace::delete_diags(processor_file_ptr file) +{ + file->diags().clear(); + + for (const std::string& fname : file->dependencies()) + { + auto dep_file = file_manager_.find_processor_file(fname); + if (dep_file) + dep_file->diags().clear(); + } + + auto notified_found = diag_suppress_notified_.emplace(file->get_file_name(), false); + if (!notified_found.first->second) + show_message("Diagnostics suppressed from " + file->get_file_name() + ", because there is no configuration."); + notified_found.first->second = true; +} + +void workspace::show_message(const std::string& message) +{ + if (message_consumer_) + message_consumer_->show_message(message, message_type::MT_INFO); +} + +lib_config workspace::get_config() { return local_config_.fill_missing_settings(global_config_); } + const processor_group& workspace::get_proc_grp_by_program(const std::string& filename) const { assert(opened_); @@ -93,7 +126,7 @@ void workspace::parse_file(const std::string& file_uri) // add support for hlasm to vscode (auto detection??) and do the decision based on languageid if (file_path == proc_grps_path_ || file_path == pgm_conf_path_) { - if (load_config()) + if (load_and_process_config()) { for (auto fname : dependants_) { @@ -109,7 +142,6 @@ void workspace::parse_file(const std::string& file_uri) filter_and_close_dependencies_(found->files_to_close(), found); } } - return; } // what about removing files??? what if depentands_ points to not existing file? @@ -142,6 +174,18 @@ void workspace::parse_file(const std::string& file_uri) f->parse(*this); if (!f->dependencies().empty()) dependants_.insert(f->get_file_name()); + + + // if there is no processor group assigned to the program, delete diagnostics that may have been created + if (cancel_ && cancel_->load()) // skip, if parsing was cancelled using the cancellation token + continue; + + const processor_group& grp = get_proc_grp_by_program(f->get_file_name()); + f->collect_diags(); + if (&grp == &implicit_proc_grp && (int64_t)f->diags().size() > get_config().diag_supress_limit) + delete_diags(f); + else + diag_suppress_notified_[f->get_file_name()] = false; } // second check after all dependants are there to close all files that used to be dependencies @@ -164,6 +208,7 @@ void workspace::did_open_file(const std::string& file_uri) { parse_file(file_uri void workspace::did_close_file(const std::string& file_uri) { + diag_suppress_notified_[file_uri] = false; // first check whether the file is a dependency // if so, simply close it, no other action is needed if (is_dependency_(file_uri)) @@ -196,10 +241,12 @@ void workspace::did_change_watched_files(const std::string& file_uri) parse_file(file_uri); } -void workspace::open() { load_config(); } +void workspace::open() { load_and_process_config(); } void workspace::close() { opened_ = false; } +void workspace::set_message_consumer(message_consumer* consumer) { message_consumer_ = consumer; } + file_manager& workspace::get_file_manager() { return file_manager_; } const processor_group& workspace::get_proc_grp(const proc_grp_id& proc_grp) const @@ -209,53 +256,21 @@ const processor_group& workspace::get_proc_grp(const proc_grp_id& proc_grp) cons } // open config files and parse them -bool workspace::load_config() +bool workspace::load_and_process_config() { - config_diags_.clear(); - - opened_ = true; - std::filesystem::path ws_path(uri_); - using json = nlohmann::json; - - // proc_grps.json parse - file_ptr proc_grps_file = file_manager_.add_file((ws_path / HLASM_PLUGIN_FOLDER / FILENAME_PROC_GRPS).string()); - - if (proc_grps_file->update_and_get_bad()) - return false; - - json proc_grps_json; - try - { - proc_grps_json = nlohmann::json::parse(proc_grps_file->get_text()); - proc_grps_.clear(); - } - catch (const nlohmann::json::exception&) - { - // could not load proc_grps - config_diags_.push_back(diagnostic_s::error_W002(proc_grps_file->get_file_name(), name_)); - return false; - } - // pgm_conf.json parse - file_ptr pgm_conf_file = file_manager_.add_file((ws_path / HLASM_PLUGIN_FOLDER / FILENAME_PGM_CONF).string()); + config_diags_.clear(); - if (pgm_conf_file->update_and_get_bad()) - return false; + opened_ = true; json pgm_conf_json; + json proc_grps_json; + file_ptr pgm_conf_file; - try - { - pgm_conf_json = nlohmann::json::parse(pgm_conf_file->get_text()); - exact_pgm_conf_.clear(); - regex_pgm_conf_.clear(); - } - catch (const nlohmann::json::exception&) - { - config_diags_.push_back(diagnostic_s::error_W003(pgm_conf_file->get_file_name(), name_)); + bool load_ok = load_config(proc_grps_json, pgm_conf_json, pgm_conf_file); + if (!load_ok) return false; - } // get extensions from pgm conf extension_regex_map extensions; @@ -269,6 +284,15 @@ bool workspace::load_config() extensions.insert({ std::regex_replace(wildcard_str, extension_regex, "$2"), wildcard2regex((ws_path / wildcard_str).string()) }); } + + /*auto suppress_diags_limit_json = pgm_conf_json.find("diagnosticsSuppressLimit"); + if (suppress_diags_limit_json != pgm_conf_json.end()) + { + if (suppress_diags_limit_json->is_number_integer()) + new_config.diag_supress_limit = suppress_diags_limit_json->get(); + }*/ + local_config_ = lib_config::load_from_json(pgm_conf_json); + auto extensions_ptr = std::make_shared(std::move(extensions)); // process processor groups json pgs = proc_grps_json["pgroups"]; @@ -325,6 +349,50 @@ bool workspace::load_config() return true; } +bool workspace::load_config(nlohmann::json& proc_grps_json, nlohmann::json& pgm_conf_json, file_ptr& pgm_conf_file) +{ + std::filesystem::path ws_path(uri_); + + + // proc_grps.json parse + file_ptr proc_grps_file = file_manager_.add_file((ws_path / HLASM_PLUGIN_FOLDER / FILENAME_PROC_GRPS).string()); + + if (proc_grps_file->update_and_get_bad()) + return false; + + + try + { + proc_grps_json = nlohmann::json::parse(proc_grps_file->get_text()); + proc_grps_.clear(); + } + catch (const nlohmann::json::exception&) + { + // could not load proc_grps + config_diags_.push_back(diagnostic_s::error_W002(proc_grps_file->get_file_name(), name_)); + return false; + } + + // pgm_conf.json parse + pgm_conf_file = file_manager_.add_file((ws_path / HLASM_PLUGIN_FOLDER / FILENAME_PGM_CONF).string()); + + if (pgm_conf_file->update_and_get_bad()) + return false; + + try + { + pgm_conf_json = nlohmann::json::parse(pgm_conf_file->get_text()); + exact_pgm_conf_.clear(); + regex_pgm_conf_.clear(); + } + catch (const nlohmann::json::exception&) + { + config_diags_.push_back(diagnostic_s::error_W003(pgm_conf_file->get_file_name(), name_)); + return false; + } + + return true; +} bool workspace::is_wildcard(const std::string& str) { return str.find('*') != std::string::npos || str.find('?') != std::string::npos; diff --git a/parser_library/src/workspaces/workspace.h b/parser_library/src/workspaces/workspace.h index 165da27ac..f7b24612a 100644 --- a/parser_library/src/workspaces/workspace.h +++ b/parser_library/src/workspaces/workspace.h @@ -15,18 +15,24 @@ #ifndef HLASMPLUGIN_PARSERLIBRARY_WORKSPACE_H #define HLASMPLUGIN_PARSERLIBRARY_WORKSPACE_H +#include #include #include #include #include #include +#include "json.hpp" + #include "diagnosable_impl.h" #include "file_manager.h" +#include "lib_config.h" #include "library.h" +#include "message_consumer.h" #include "processor.h" #include "processor_group.h" + namespace hlasm_plugin::parser_library::workspaces { using ws_uri = std::string; @@ -56,9 +62,16 @@ class workspace : public diagnosable_impl, public parse_lib_provider public: // Creates just a dummy workspace with no libraries - no dependencies // between files. - workspace(file_manager& file_manager); - workspace(ws_uri uri, file_manager& file_manager); - workspace(ws_uri uri, std::string name, file_manager& file_manager); + workspace(file_manager& file_manager, const lib_config& global_config, std::atomic* cancel = nullptr); + workspace(const ws_uri& uri, + file_manager& file_manager, + const lib_config& global_config, + std::atomic* cancel = nullptr); + workspace(const ws_uri& uri, + const std::string& name, + file_manager& file_manager, + const lib_config& global_config, + std::atomic* cancel = nullptr); workspace(const workspace& ws) = delete; workspace& operator=(const workspace&) = delete; @@ -88,6 +101,8 @@ class workspace : public diagnosable_impl, public parse_lib_provider void open(); void close(); + void set_message_consumer(message_consumer* consumer); + protected: file_manager& get_file_manager(); @@ -96,6 +111,8 @@ class workspace : public diagnosable_impl, public parse_lib_provider constexpr static char FILENAME_PGM_CONF[] = "pgm_conf.json"; constexpr static char HLASM_PLUGIN_FOLDER[] = ".hlasmplugin"; + std::atomic* cancel_; + std::string name_; ws_uri uri_; file_manager& file_manager_; @@ -111,7 +128,11 @@ class workspace : public diagnosable_impl, public parse_lib_provider bool opened_ = false; - bool load_config(); + + bool load_and_process_config(); + // Loads the pgm_conf.json and proc_grps.json from disk, adds them to file_manager_ and parses both jsons. + // Returns false if there is any error. + bool load_config(nlohmann::json& proc_grps_json, nlohmann::json& pgm_conf_json, file_ptr& pgm_conf_file); bool is_wildcard(const std::string& str); @@ -124,6 +145,18 @@ class workspace : public diagnosable_impl, public parse_lib_provider bool is_dependency_(const std::string& file_uri); bool program_id_match(const std::string& filename, const program_id& program) const; + + void delete_diags(processor_file_ptr file); + + void show_message(const std::string& message); + + message_consumer* message_consumer_ = nullptr; + + // A map that holds true values for files that have diags suppressed and the user was already notified about it + std::unordered_map diag_suppress_notified_; + const lib_config& global_config_; + lib_config local_config_; + lib_config get_config(); }; } // namespace hlasm_plugin::parser_library::workspaces diff --git a/parser_library/test/debugging/debugger_test.cpp b/parser_library/test/debugging/debugger_test.cpp index 32240492f..0a29cc1ea 100644 --- a/parser_library/test/debugging/debugger_test.cpp +++ b/parser_library/test/debugging/debugger_test.cpp @@ -32,7 +32,8 @@ using namespace hlasm_plugin::parser_library::workspaces; TEST(debugger, stopped_on_entry) { file_manager_impl file_manager; - workspace ws("test_workspace", file_manager); + lib_config config; + workspace ws("test_workspace", file_manager, config); debug_event_consumer_s_mock m; debug_config cfg; @@ -66,7 +67,8 @@ TEST(debugger, stopped_on_entry) TEST(debugger, disconnect) { file_manager_impl file_manager; - workspace ws("test_workspace", file_manager); + lib_config config; + workspace ws("test_workspace", file_manager, config); debug_event_consumer_s_mock m; debug_config cfg; @@ -224,9 +226,11 @@ bool check_step( class workspace_mock : public workspace { + lib_config config; + public: workspace_mock(file_manager& file_mngr) - : workspace(file_mngr) + : workspace(file_mngr, config) {} virtual parse_result parse_library( diff --git a/parser_library/test/message_consumer_mock.h b/parser_library/test/message_consumer_mock.h new file mode 100644 index 000000000..01189cb61 --- /dev/null +++ b/parser_library/test/message_consumer_mock.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#ifndef HLASMPLUGIN_PARSERLIBRARY_MESSAGE_CONSUMER_MOCK_H +#define HLASMPLUGIN_PARSERLIBRARY_MESSAGE_CONSUMER_MOCK_H + +#include "message_consumer.h" + +namespace hlasm_plugin::parser_library { + +class message_consumer_mock : public hlasm_plugin::parser_library::message_consumer +{ +public: + virtual void show_message(const std::string& message, message_type type) override + { + messages.push_back(std::make_pair(message, type)); + } + std::vector> messages; +}; + +} // namespace hlasm_plugin::parser_library +#endif diff --git a/parser_library/test/res/test_wks/pgm_conf.json b/parser_library/test/res/test_wks/.hlasmplugin/pgm_conf.json similarity index 69% rename from parser_library/test/res/test_wks/pgm_conf.json rename to parser_library/test/res/test_wks/.hlasmplugin/pgm_conf.json index 1e46266cd..0ac2f447c 100644 --- a/parser_library/test/res/test_wks/pgm_conf.json +++ b/parser_library/test/res/test_wks/.hlasmplugin/pgm_conf.json @@ -7,6 +7,10 @@ { "program": "faulty", "pgroup": "P1" + }, + { + "program": "new_file", + "pgroup": "P1" } ] } \ No newline at end of file diff --git a/parser_library/test/res/test_wks/proc_grps.json b/parser_library/test/res/test_wks/.hlasmplugin/proc_grps.json similarity index 100% rename from parser_library/test/res/test_wks/proc_grps.json rename to parser_library/test/res/test_wks/.hlasmplugin/proc_grps.json diff --git a/parser_library/test/workspace/diags_suppress_test.cpp b/parser_library/test/workspace/diags_suppress_test.cpp new file mode 100644 index 000000000..10325d970 --- /dev/null +++ b/parser_library/test/workspace/diags_suppress_test.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2019 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "../message_consumer_mock.h" +#include "empty_configs.h" +#include "lib_config.h" +#include "workspaces/file_impl.h" +#include "workspaces/file_manager_impl.h" +#include "workspaces/workspace.h" + +using namespace nlohmann; +using namespace hlasm_plugin::parser_library; +using namespace hlasm_plugin::parser_library::workspaces; + +std::string one_proc_grps = R"( +{ + "pgroups": [ + { "name": "P1", "libs": [] } + ] +} +)"; + +TEST(diags_suppress, no_suppress) +{ + file_manager_impl fm; + fm.did_open_file(pgm_conf_name, 0, empty_pgm_conf); + fm.did_open_file(proc_grps_name, 0, one_proc_grps); + + std::string file_name = "a_file"; + + fm.did_open_file(file_name, 0, R"( + LR 1, + LR 1, + LR 1, + LR 1, + LR 1, + LR 1, +)"); + + lib_config config; + workspace ws(fm, config); + ws.open(); + ws.did_open_file(file_name); + + auto pfile = fm.find(file_name); + ASSERT_TRUE(pfile); + + pfile->collect_diags(); + EXPECT_EQ(pfile->diags().size(), 6U); +} + +TEST(diags_suppress, do_suppress) +{ + auto config = lib_config::load_from_json(R"({"diagnosticsSuppressLimit":5})"_json); + + file_manager_impl fm; + fm.did_open_file(pgm_conf_name, 0, empty_pgm_conf); + fm.did_open_file(proc_grps_name, 0, one_proc_grps); + + std::string file_name = "a_file"; + + fm.did_open_file(file_name, 0, R"( + LR 1, + LR 1, + LR 1, + LR 1, + LR 1, + LR 1, +)"); + + message_consumer_mock msg_consumer; + + workspace ws(fm, config); + ws.set_message_consumer(&msg_consumer); + ws.open(); + ws.did_open_file(file_name); + + auto pfile = fm.find(file_name); + ASSERT_TRUE(pfile); + + pfile->collect_diags(); + EXPECT_EQ(pfile->diags().size(), 0U); + + ASSERT_EQ(msg_consumer.messages.size(), 1U); + EXPECT_EQ(msg_consumer.messages[0].first, "Diagnostics suppressed from a_file, because there is no configuration."); + EXPECT_EQ(msg_consumer.messages[0].second, message_type::MT_INFO); +} + +TEST(diags_suppress, pgm_supress_limit_changed) +{ + file_manager_impl fm; + fm.did_open_file(pgm_conf_name, 0, empty_pgm_conf); + fm.did_open_file(proc_grps_name, 0, one_proc_grps); + + std::string file_name = "a_file"; + + fm.did_open_file(file_name, 0, R"( + LR 1, + LR 1, + LR 1, + LR 1, + LR 1, + LR 1, +)"); + + lib_config config; + workspace ws(fm, config); + ws.open(); + ws.did_open_file(file_name); + + auto pfile = fm.find(file_name); + ASSERT_TRUE(pfile); + + pfile->collect_diags(); + EXPECT_EQ(pfile->diags().size(), 6U); + pfile->diags().clear(); + + std::string new_limit_str = R"("diagnosticsSuppressLimit":5,)"; + document_change ch(range({ 0, 1 }, { 0, 1 }), new_limit_str.c_str(), new_limit_str.size()); + + fm.did_change_file(pgm_conf_name, 1, &ch, 1); + ws.did_change_file(pgm_conf_name, &ch, 1); + + ws.did_change_file(file_name, &ch, 1); + + pfile = fm.find(file_name); + ASSERT_TRUE(pfile); + pfile->collect_diags(); + EXPECT_EQ(pfile->diags().size(), 0U); +} + +TEST(diags_suppress, cancel_token) +{ + file_manager_impl fm; + fm.did_open_file(pgm_conf_name, 0, empty_pgm_conf); + fm.did_open_file(proc_grps_name, 0, one_proc_grps); + + std::string file_name = "a_file"; + + fm.did_open_file(file_name, 0, R"( + LR 1, + LR 1, + LR 1, + LR 1, + LR 1, + LR 1, +)"); + + std::atomic cancel = true; + auto config = lib_config::load_from_json(R"({"diagnosticsSuppressLimit":5})"_json); + workspace ws(fm, config, &cancel); + ws.open(); + ws.did_open_file(file_name); + + auto pfile = fm.find(file_name); + ASSERT_TRUE(pfile); + + pfile->collect_diags(); + EXPECT_EQ(pfile->diags().size(), 6U); +} diff --git a/parser_library/test/workspace/empty_configs.h b/parser_library/test/workspace/empty_configs.h new file mode 100644 index 000000000..f506d476f --- /dev/null +++ b/parser_library/test/workspace/empty_configs.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#ifndef HLASMPLUGIN_PARSERLIBRARY_TEST_EMPTY_CONFIGS_H +#define HLASMPLUGIN_PARSERLIBRARY_TEST_EMPTY_CONFIGS_H + +#include + +inline std::string pgm_conf_name = (std::filesystem::path(".hlasmplugin") / "pgm_conf.json").string(); +inline std::string proc_grps_name = (std::filesystem::path(".hlasmplugin") / "proc_grps.json").string(); +inline std::string empty_pgm_conf = R"({ "pgms": []})"; +inline std::string empty_proc_grps = R"({ "pgroups": []})"; + +#endif // !HLASMPLUGIN_PARSERLIBRARY_TEST_EMPTY_CONFIGS_H diff --git a/parser_library/test/workspace/load_config_test.cpp b/parser_library/test/workspace/load_config_test.cpp new file mode 100644 index 000000000..a2dc511f6 --- /dev/null +++ b/parser_library/test/workspace/load_config_test.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2019 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "empty_configs.h" +#include "workspaces/file_impl.h" +#include "workspaces/file_manager_impl.h" +#include "workspaces/workspace.h" + +using namespace hlasm_plugin::parser_library; +using namespace hlasm_plugin::parser_library::workspaces; + +class file_proc_grps : public file_impl +{ +public: + file_proc_grps() + : file_impl("proc_grps.json") + {} + + file_uri uri = "test_uri"; + + virtual const file_uri& get_file_name() override { return uri; } + + virtual const std::string& get_text() override { return file; } + + virtual bool update_and_get_bad() override { return false; } + +#ifdef _WIN32 + std::string file = R"({ + "pgroups": [ + { + "name": "P1", + "libs": [ + "C:\\Users\\Desktop\\ASLib", + "lib", + "libs\\lib2\\", + "", + { + "run": "ftp \\192.168.12.145\\MyASLib", + "list": "", + "cache_expire":"", + "location": "downLibs" + } + ] + }, + { + "name": "P2", + "libs": [ + "C:\\Users\\Desktop\\ASLib", + "P2lib", + "P2libs\\libb", + { + "run": "ftp \\192.168.12.145\\MyASLib", + "location": "\\downLibs" + } + ] + } + ] +})"; +#else + std::string file = R"({ + "pgroups": [ + { + "name": "P1", + "libs": [ + "/home/user/ASLib", + "lib", + "libs/lib2/", + "", + { + "run": "ftp /192.168.12.145/MyASLib", + "list": "", + "cache_expire":"", + "location": "downLibs" + } + ] + }, + { + "name": "P2", + "libs": [ + "/home/user/ASLib", + "P2lib", + "P2libs/libb", + { + "run": "ftp /192.168.12.145/MyASLib", + "location": "downLibs" + } + ] + } + ] + })"; +#endif //_WIN32 +}; + +class file_pgm_conf : public file_impl +{ +public: + file_pgm_conf() + : file_impl("proc_grps.json") + {} + + file_uri uri = "test_uri"; + + virtual const file_uri& get_file_name() override { return uri; } + + virtual const std::string& get_text() override { return file; } + + virtual bool update_and_get_bad() override { return false; } + +#if _WIN32 + std::string file = R"({ + "pgms": [ + { + "program": "pgm1", + "pgroup": "P1" + }, + { + "program": "pgms\\*", + "pgroup": "P2" + } + ] +})"; +#else + std::string file = R"({ + "pgms": [ + { + "program": "pgm1", + "pgroup": "P1" + }, + { + "program": "pgms/*", + "pgroup": "P2" + } + ] +})"; +#endif +}; + +class file_manager_proc_grps_test : public file_manager_impl +{ +public: + file_ptr add_file(const file_uri& uri) override + { + if (uri.substr(uri.size() - 14) == "proc_grps.json") + return proc_grps; + else + return pgm_conf; + } + + std::shared_ptr proc_grps = std::make_shared(); + std::shared_ptr pgm_conf = std::make_shared(); + + + // Inherited via file_manager + virtual void did_open_file(const std::string&, version_t, std::string) override {} + virtual void did_change_file(const std::string&, version_t, const document_change*, size_t) override {} + virtual void did_close_file(const std::string&) override {} +}; + +TEST(workspace, load_config_synthetic) +{ + file_manager_proc_grps_test file_manager; + lib_config config; + workspace ws("test_proc_grps_uri", "test_proc_grps_name", file_manager, config); + + ws.open(); + + auto& pg = ws.get_proc_grp("P1"); + EXPECT_EQ("P1", pg.name()); +#ifdef _WIN32 + std::string expected[4] { "C:\\Users\\Desktop\\ASLib\\", + "test_proc_grps_uri\\lib\\", + "test_proc_grps_uri\\libs\\lib2\\", + "test_proc_grps_uri\\" }; +#else + std::string expected[4] { + "/home/user/ASLib/", "test_proc_grps_uri/lib/", "test_proc_grps_uri/libs/lib2/", "test_proc_grps_uri/" + }; +#endif // _WIN32 + EXPECT_EQ(std::size(expected), pg.libraries().size()); + for (size_t i = 0; i < std::min(std::size(expected), pg.libraries().size()); ++i) + { + library_local* libl = dynamic_cast(pg.libraries()[i].get()); + ASSERT_NE(libl, nullptr); + EXPECT_EQ(expected[i], libl->get_lib_path()); + } + + auto& pg2 = ws.get_proc_grp("P2"); + EXPECT_EQ("P2", pg2.name()); +#ifdef _WIN32 + std::string expected2[3] { + "C:\\Users\\Desktop\\ASLib\\", "test_proc_grps_uri\\P2lib\\", "test_proc_grps_uri\\P2libs\\libb\\" + }; +#else + std::string expected2[3] { "/home/user/ASLib/", "test_proc_grps_uri/P2lib/", "test_proc_grps_uri/P2libs/libb/" }; +#endif // _WIN32 + EXPECT_EQ(std::size(expected2), pg2.libraries().size()); + for (size_t i = 0; i < std::min(std::size(expected2), pg2.libraries().size()); ++i) + { + library_local* libl = dynamic_cast(pg2.libraries()[i].get()); + ASSERT_NE(libl, nullptr); + EXPECT_EQ(expected2[i], libl->get_lib_path()); + } + + + // test of pgm_conf and workspace::get_proc_grp_by_program +#ifdef _WIN32 + auto& pg3 = ws.get_proc_grp_by_program("test_proc_grps_uri\\pgm1"); +#else + auto& pg3 = ws.get_proc_grp_by_program("test_proc_grps_uri/pgm1"); +#endif + EXPECT_EQ(pg3.libraries().size(), std::size(expected)); + for (size_t i = 0; i < std::min(std::size(expected), pg3.libraries().size()); ++i) + { + library_local* libl = dynamic_cast(pg3.libraries()[i].get()); + ASSERT_NE(libl, nullptr); + EXPECT_EQ(expected[i], libl->get_lib_path()); + } + + +#ifdef _WIN32 + auto& pg4 = ws.get_proc_grp_by_program("test_proc_grps_uri\\pgms\\anything"); +#else + auto& pg4 = ws.get_proc_grp_by_program("test_proc_grps_uri/pgms/anything"); +#endif + EXPECT_EQ(pg4.libraries().size(), std::size(expected2)); + for (size_t i = 0; i < std::min(std::size(expected2), pg4.libraries().size()); ++i) + { + library_local* libl = dynamic_cast(pg4.libraries()[i].get()); + ASSERT_NE(libl, nullptr); + EXPECT_EQ(expected2[i], libl->get_lib_path()); + } +} + + +TEST(workspace, pgm_conf_malformed) +{ + file_manager_impl fm; + fm.did_open_file(pgm_conf_name, 0, R"({ "pgms": [})"); + fm.did_open_file(proc_grps_name, 0, empty_proc_grps); + + lib_config config; + workspace ws(fm, config); + ws.open(); + + ws.collect_diags(); + ASSERT_EQ(ws.diags().size(), 1U); + EXPECT_EQ(ws.diags()[0].code, "W0003"); +} + +TEST(workspace, proc_grps_malformed) +{ + file_manager_impl fm; + fm.did_open_file(pgm_conf_name, 0, empty_pgm_conf); + fm.did_open_file(proc_grps_name, 0, R"({ "pgroups" []})"); + + lib_config config; + workspace ws(fm, config); + ws.open(); + + ws.collect_diags(); + ASSERT_EQ(ws.diags().size(), 1U); + EXPECT_EQ(ws.diags()[0].code, "W0002"); +} + +TEST(workspace, pgm_conf_missing) +{ + file_manager_impl fm; + fm.did_open_file(proc_grps_name, 0, empty_proc_grps); + + lib_config config; + workspace ws(fm, config); + ws.open(); + + ws.collect_diags(); + ASSERT_EQ(ws.diags().size(), 0U); +} + +TEST(workspace, proc_grps_missing) +{ + file_manager_impl fm; + fm.did_open_file(pgm_conf_name, 0, empty_pgm_conf); + + lib_config config; + workspace ws(fm, config); + ws.open(); + + ws.collect_diags(); + ASSERT_EQ(ws.diags().size(), 0U); +} diff --git a/parser_library/test/workspace/workspace_test.cpp b/parser_library/test/workspace/workspace_test.cpp index 0948915d8..619bee01c 100644 --- a/parser_library/test/workspace/workspace_test.cpp +++ b/parser_library/test/workspace/workspace_test.cpp @@ -13,6 +13,7 @@ */ #include +#include #include #include @@ -58,13 +59,14 @@ class workspace_test : public diagnosable_impl, public testing::Test TEST_F(workspace_test, parse_lib_provider) { + lib_config config; file_manager_impl file_mngr; -#if _WIN32 - workspace ws("test\\library\\test_wks", file_mngr); -#else - workspace ws("test/library/test_wks", file_mngr); -#endif + std::string test_wks_path = (std::filesystem::path("test") / "library" / "test_wks").string(); + + + workspace ws(test_wks_path, file_mngr, config); + ws.open(); collect_diags_from_child(ws); @@ -99,226 +101,7 @@ TEST_F(workspace_test, parse_lib_provider) "not_existing", ctx_2, library_data { processing::processing_kind::MACRO, ctx_1.ids().add("not_existing") }); } -class file_proc_grps : public file_impl -{ -public: - file_proc_grps() - : file_impl("proc_grps.json") - {} - - file_uri uri = "test_uri"; - - virtual const file_uri& get_file_name() override { return uri; } - - virtual const std::string& get_text() override { return file; } - - virtual bool update_and_get_bad() override { return false; } - -#ifdef _WIN32 - std::string file = R"({ - "pgroups": [ - { - "name": "P1", - "libs": [ - "C:\\Users\\Desktop\\ASLib", - "lib", - "libs\\lib2\\", - "", - { - "run": "ftp \\192.168.12.145\\MyASLib", - "list": "", - "cache_expire":"", - "location": "downLibs" - } - ] - }, - { - "name": "P2", - "libs": [ - "C:\\Users\\Desktop\\ASLib", - "P2lib", - "P2libs\\libb", - { - "run": "ftp \\192.168.12.145\\MyASLib", - "location": "\\downLibs" - } - ] - } - ] -})"; -#else - std::string file = R"({ - "pgroups": [ - { - "name": "P1", - "libs": [ - "/home/user/ASLib", - "lib", - "libs/lib2/", - "", - { - "run": "ftp /192.168.12.145/MyASLib", - "list": "", - "cache_expire":"", - "location": "downLibs" - } - ] - }, - { - "name": "P2", - "libs": [ - "/home/user/ASLib", - "P2lib", - "P2libs/libb", - { - "run": "ftp /192.168.12.145/MyASLib", - "location": "downLibs" - } - ] - } - ] - })"; -#endif //_WIN32 -}; - -class file_pgm_conf : public file_impl -{ -public: - file_pgm_conf() - : file_impl("proc_grps.json") - {} - - file_uri uri = "test_uri"; - - virtual const file_uri& get_file_name() override { return uri; } - - virtual const std::string& get_text() override { return file; } - - virtual bool update_and_get_bad() override { return false; } - -#if _WIN32 - std::string file = R"({ - "pgms": [ - { - "program": "pgm1", - "pgroup": "P1" - }, - { - "program": "pgms\\*", - "pgroup": "P2" - } - ] -})"; -#else - std::string file = R"({ - "pgms": [ - { - "program": "pgm1", - "pgroup": "P1" - }, - { - "program": "pgms/*", - "pgroup": "P2" - } - ] -})"; -#endif -}; - -class file_manager_proc_grps_test : public file_manager_impl -{ -public: - file_ptr add_file(const file_uri& uri) override - { - if (uri.substr(uri.size() - 14) == "proc_grps.json") - return proc_grps; - else - return pgm_conf; - } - - std::shared_ptr proc_grps = std::make_shared(); - std::shared_ptr pgm_conf = std::make_shared(); - - - // Inherited via file_manager - virtual void did_open_file(const std::string&, version_t, std::string) override {} - virtual void did_change_file(const std::string&, version_t, const document_change*, size_t) override {} - virtual void did_close_file(const std::string&) override {} -}; - -TEST(workspace, load_config_synthetic) -{ - file_manager_proc_grps_test file_manager; - workspace ws("test_proc_grps_uri", "test_proc_grps_name", file_manager); - - ws.open(); - - auto& pg = ws.get_proc_grp("P1"); - EXPECT_EQ("P1", pg.name()); -#ifdef _WIN32 - std::string expected[4] { "C:\\Users\\Desktop\\ASLib\\", - "test_proc_grps_uri\\lib\\", - "test_proc_grps_uri\\libs\\lib2\\", - "test_proc_grps_uri\\" }; -#else - std::string expected[4] { - "/home/user/ASLib/", "test_proc_grps_uri/lib/", "test_proc_grps_uri/libs/lib2/", "test_proc_grps_uri/" - }; -#endif // _WIN32 - EXPECT_EQ(std::size(expected), pg.libraries().size()); - for (size_t i = 0; i < std::min(std::size(expected), pg.libraries().size()); ++i) - { - library_local* libl = dynamic_cast(pg.libraries()[i].get()); - ASSERT_NE(libl, nullptr); - EXPECT_EQ(expected[i], libl->get_lib_path()); - } - - auto& pg2 = ws.get_proc_grp("P2"); - EXPECT_EQ("P2", pg2.name()); -#ifdef _WIN32 - std::string expected2[3] { - "C:\\Users\\Desktop\\ASLib\\", "test_proc_grps_uri\\P2lib\\", "test_proc_grps_uri\\P2libs\\libb\\" - }; -#else - std::string expected2[3] { "/home/user/ASLib/", "test_proc_grps_uri/P2lib/", "test_proc_grps_uri/P2libs/libb/" }; -#endif // _WIN32 - EXPECT_EQ(std::size(expected2), pg2.libraries().size()); - for (size_t i = 0; i < std::min(std::size(expected2), pg2.libraries().size()); ++i) - { - library_local* libl = dynamic_cast(pg2.libraries()[i].get()); - ASSERT_NE(libl, nullptr); - EXPECT_EQ(expected2[i], libl->get_lib_path()); - } - -// test of pgm_conf and workspace::get_proc_grp_by_program -#ifdef _WIN32 - auto& pg3 = ws.get_proc_grp_by_program("test_proc_grps_uri\\pgm1"); -#else - auto& pg3 = ws.get_proc_grp_by_program("test_proc_grps_uri/pgm1"); -#endif - EXPECT_EQ(pg3.libraries().size(), std::size(expected)); - for (size_t i = 0; i < std::min(std::size(expected), pg3.libraries().size()); ++i) - { - library_local* libl = dynamic_cast(pg3.libraries()[i].get()); - ASSERT_NE(libl, nullptr); - EXPECT_EQ(expected[i], libl->get_lib_path()); - } - -// test of -#ifdef _WIN32 - auto& pg4 = ws.get_proc_grp_by_program("test_proc_grps_uri\\pgms\\anything"); -#else - auto& pg4 = ws.get_proc_grp_by_program("test_proc_grps_uri/pgms/anything"); -#endif - EXPECT_EQ(pg4.libraries().size(), std::size(expected2)); - for (size_t i = 0; i < std::min(std::size(expected2), pg4.libraries().size()); ++i) - { - library_local* libl = dynamic_cast(pg4.libraries()[i].get()); - ASSERT_NE(libl, nullptr); - EXPECT_EQ(expected2[i], libl->get_lib_path()); - } -} std::string pgroups_file = R"({ "pgroups": [ @@ -418,8 +201,9 @@ class file_manager_extended : public file_manager_impl TEST_F(workspace_test, did_close_file) { + lib_config config; file_manager_extended file_manager; - workspace ws("", "workspace_name", file_manager); + workspace ws("", "workspace_name", file_manager, config); ws.open(); // 3 files are open // - open codes source1 and source2 with syntax errors using macro ERROR @@ -459,7 +243,8 @@ TEST_F(workspace_test, did_close_file) TEST_F(workspace_test, did_change_watched_files) { file_manager_extended file_manager; - workspace ws("", "workspace_name", file_manager); + lib_config config; + workspace ws("", "workspace_name", file_manager, config); ws.open(); // no diagnostics with no syntax errors diff --git a/parser_library/test/workspace_manager_test.cpp b/parser_library/test/workspace_manager_test.cpp index efb5a112b..8175d7ed0 100644 --- a/parser_library/test/workspace_manager_test.cpp +++ b/parser_library/test/workspace_manager_test.cpp @@ -12,8 +12,12 @@ * Broadcom, Inc. - initial API and implementation */ +#include + #include "gtest/gtest.h" +#include "lib_config.h" +#include "message_consumer_mock.h" #include "workspace_manager.h" using namespace hlasm_plugin::parser_library; @@ -96,4 +100,27 @@ TEST(workspace_manager, did_change_file) ws_mngr.did_change_file("test/library/test_wks/new_file", 3, changes1.data(), 1); EXPECT_GT(consumer.diags.diagnostics_size(), (size_t)0); -} \ No newline at end of file +} + + +TEST(workspace_manager, set_message_consumer) +{ + workspace_manager mngr; + mngr.configuration_changed(lib_config::load_from_json(R"({"diagnosticsSuppressLimit":0})"_json)); + mngr.add_workspace("ws1", "ws1"); + + message_consumer_mock msg_consumer; + + mngr.set_message_consumer(&msg_consumer); + + std::string error_file_text = "someerror"; + mngr.did_open_file("no_workspace_file", 0, error_file_text.c_str(), error_file_text.size()); + ASSERT_EQ(msg_consumer.messages.size(), 1U); + msg_consumer.messages.clear(); + + mngr.did_open_file((std::filesystem::path("ws1") / "no_workspace_file").string().c_str(), + 0, + error_file_text.c_str(), + error_file_text.size()); + ASSERT_EQ(msg_consumer.messages.size(), 1U); +}