| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "Bookmark.h" |
| |
| #include <regex> |
| |
| #include "TestBase.h" |
| #include "utils/gsl.h" |
| #include "wel/UniqueEvtHandle.h" |
| |
| using Bookmark = org::apache::nifi::minifi::processors::Bookmark; |
| using unique_evt_handle = org::apache::nifi::minifi::wel::unique_evt_handle; |
| using IdGenerator = org::apache::nifi::minifi::utils::IdGenerator; |
| |
| namespace { |
| |
| const std::wstring APPLICATION_CHANNEL = L"Application"; |
| |
| constexpr DWORD BOOKMARK_TESTS_OPCODE = 10368; // random opcode hopefully won't clash with something important |
| |
| constexpr DWORD EVT_NEXT_TIMEOUT_MS = 100; |
| |
| std::unique_ptr<Bookmark> createBookmark(TestPlan &test_plan, |
| const std::wstring &channel, |
| const utils::Identifier &uuid = IdGenerator::getIdGenerator()->generate()) { |
| const auto state_manager = test_plan.getStateManagerProvider()->getCoreComponentStateManager(uuid); |
| const auto logger = test_plan.getLogger(); |
| return utils::make_unique<Bookmark>(channel, L"*", "", uuid, false, state_manager, logger); |
| } |
| |
| void reportEvent(const std::wstring& channel, const char* message) { |
| auto event_source = RegisterEventSourceW(nullptr, channel.c_str()); |
| auto event_source_deleter = gsl::finally([&event_source](){ DeregisterEventSource(event_source); }); |
| ReportEventA(event_source, EVENTLOG_INFORMATION_TYPE, 0, |
| BOOKMARK_TESTS_OPCODE, nullptr, 1, 0, &message, nullptr); |
| } |
| |
| std::wstring bookmarkHandleAsXml(EVT_HANDLE event) { |
| REQUIRE(event); |
| constexpr std::size_t BUFFER_SIZE = 1024; |
| std::array<wchar_t, BUFFER_SIZE> buffer = {}; |
| DWORD buffer_used; |
| DWORD property_count; |
| if (!EvtRender(nullptr, event, EvtRenderBookmark, buffer.size(), buffer.data(), &buffer_used, &property_count)) { |
| FAIL("EvtRender() failed; error code: " << GetLastError()); |
| } |
| return std::wstring{buffer.data()}; |
| } |
| |
| std::wstring bookmarkAsXml(const std::unique_ptr<Bookmark>& bookmark) { |
| REQUIRE(bookmark); |
| REQUIRE(*bookmark); |
| return bookmarkHandleAsXml(bookmark->getBookmarkHandleFromXML()); |
| } |
| |
| unique_evt_handle queryEvents() { |
| std::wstring query = L"Event/System/EventID=" + std::to_wstring(BOOKMARK_TESTS_OPCODE); |
| unique_evt_handle results{EvtQuery(NULL, APPLICATION_CHANNEL.c_str(), query.c_str(), EvtQueryChannelPath | EvtQueryReverseDirection)}; |
| if (!results) { |
| FAIL("EvtQuery() failed; error code: " << GetLastError()); |
| } |
| return results; |
| } |
| |
| unique_evt_handle getFirstEventFromResults(const unique_evt_handle& results) { |
| REQUIRE(results); |
| EVT_HANDLE event_raw_handle = 0; |
| DWORD num_results_found = 0; |
| if (!EvtNext(results.get(), 1, &event_raw_handle, EVT_NEXT_TIMEOUT_MS, 0, &num_results_found)) { |
| FAIL("EvtNext() failed; error code: " << GetLastError()); |
| } |
| unique_evt_handle event{event_raw_handle}; |
| REQUIRE(event); |
| REQUIRE(num_results_found == 1); |
| return event; |
| } |
| |
| } // namespace |
| |
| TEST_CASE("Bookmark constructor works", "[create]") { |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| LogTestController::getInstance().setTrace<TestPlan>(); |
| |
| reportEvent(APPLICATION_CHANNEL, "Publish an event to make sure the event log is not empty"); |
| |
| std::unique_ptr<Bookmark> bookmark = createBookmark(*test_plan, APPLICATION_CHANNEL); |
| REQUIRE(bookmark); |
| REQUIRE(*bookmark); |
| |
| std::wregex pattern{L"<BookmarkList Direction='backward'>\r\n" |
| L" <Bookmark Channel='Application' RecordId='\\d+' IsCurrent='true'/>\r\n" |
| L"</BookmarkList>"}; |
| REQUIRE(std::regex_match(bookmarkAsXml(bookmark), pattern)); |
| } |
| |
| TEST_CASE("Bookmark is restored from the state", "[create][state]") { |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| LogTestController::getInstance().setTrace<TestPlan>(); |
| |
| utils::Identifier uuid = IdGenerator::getIdGenerator()->generate(); |
| std::unique_ptr<Bookmark> bookmark_before = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid); |
| std::wstring bookmark_xml_before = bookmarkAsXml(bookmark_before); |
| |
| reportEvent(APPLICATION_CHANNEL, "Something interesting happened"); |
| |
| // same uuid, so the state manager is the same as before |
| std::unique_ptr<Bookmark> bookmark_after = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid); |
| std::wstring bookmark_xml_after = bookmarkAsXml(bookmark_after); |
| |
| REQUIRE(bookmark_xml_before == bookmark_xml_after); |
| } |
| |
| TEST_CASE("Bookmark created after a new event is different", "[create][state]") { |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| LogTestController::getInstance().setTrace<TestPlan>(); |
| |
| utils::Identifier uuid_one = IdGenerator::getIdGenerator()->generate(); |
| std::unique_ptr<Bookmark> bookmark_before = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid_one); |
| |
| reportEvent(APPLICATION_CHANNEL, "Something interesting happened"); |
| |
| utils::Identifier uuid_two = IdGenerator::getIdGenerator()->generate(); |
| // different uuid, so we get a new, empty, state manager |
| std::unique_ptr<Bookmark> bookmark_after = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid_two); |
| |
| REQUIRE(bookmarkAsXml(bookmark_before) != bookmarkAsXml(bookmark_after)); |
| } |
| |
| TEST_CASE("Bookmark::getBookmarkHandleFromXML() returns the same event from a copy", "[handle_from_xml]") { |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| LogTestController::getInstance().setTrace<TestPlan>(); |
| |
| utils::Identifier uuid = IdGenerator::getIdGenerator()->generate(); |
| std::unique_ptr<Bookmark> bookmark_one = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid); |
| std::unique_ptr<Bookmark> bookmark_two = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid); |
| |
| EVT_HANDLE bookmark_handle_one = bookmark_one->getBookmarkHandleFromXML(); |
| EVT_HANDLE bookmark_handle_two = bookmark_two->getBookmarkHandleFromXML(); |
| REQUIRE(bookmarkHandleAsXml(bookmark_handle_one) == bookmarkHandleAsXml(bookmark_handle_two)); |
| } |
| |
| TEST_CASE("Bookmark::getBookmarkHandleFromXML() returns a different event after the XML is changed", "[handle_from_xml]") { |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| LogTestController::getInstance().setTrace<TestPlan>(); |
| |
| GIVEN("We have two different bookmarks") { |
| std::unique_ptr<Bookmark> bookmark_one = createBookmark(*test_plan, APPLICATION_CHANNEL); |
| std::wstring bookmark_one_xml = bookmarkAsXml(bookmark_one); |
| |
| reportEvent(APPLICATION_CHANNEL, "Something interesting happened"); |
| |
| std::unique_ptr<Bookmark> bookmark_two = createBookmark(*test_plan, APPLICATION_CHANNEL); |
| std::wstring bookmark_two_xml = bookmarkAsXml(bookmark_two); |
| |
| REQUIRE(bookmark_one_xml != bookmark_two_xml); |
| |
| WHEN("we set the XML of the first bookmark equal to the XML of the second bookmark") { |
| bookmark_one->saveBookmarkXml(bookmark_two_xml); |
| |
| THEN("getBookmarkHandleFromXML() will return the updated handle") { |
| EVT_HANDLE bookmark_one_handle = bookmark_one->getBookmarkHandleFromXML(); |
| REQUIRE(bookmarkHandleAsXml(bookmark_one_handle) == bookmark_two_xml); |
| } |
| |
| THEN("... and the two bookmarks are now equal.") { |
| REQUIRE(bookmarkAsXml(bookmark_one) == bookmarkAsXml(bookmark_two)); |
| } |
| } |
| } |
| } |
| |
| TEST_CASE("Bookmark::getNewBookmarkXml() updates the bookmark", "[add_event]") { |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| LogTestController::getInstance().setTrace<TestPlan>(); |
| |
| std::unique_ptr<Bookmark> bookmark = createBookmark(*test_plan, APPLICATION_CHANNEL); |
| std::wstring bookmark_xml_before = bookmarkAsXml(bookmark); |
| |
| reportEvent(APPLICATION_CHANNEL, "Something interesting happened"); |
| |
| unique_evt_handle results = queryEvents(); |
| unique_evt_handle event = getFirstEventFromResults(results); |
| |
| std::wstring bookmark_xml_after; |
| REQUIRE(bookmark->getNewBookmarkXml(event.get(), bookmark_xml_after)); |
| |
| REQUIRE(bookmark_xml_before != bookmark_xml_after); |
| } |
| |
| TEST_CASE("Bookmark::saveBookmarkXml() updates the bookmark and saves it to the state manager", "[save_bookmark][state]") { |
| TestController test_controller; |
| std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); |
| LogTestController::getInstance().setTrace<TestPlan>(); |
| |
| GIVEN("We have two different bookmarks with two different state managers") { |
| utils::Identifier uuid_one = IdGenerator::getIdGenerator()->generate(); |
| std::unique_ptr<Bookmark> bookmark_one = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid_one); |
| |
| reportEvent(APPLICATION_CHANNEL, "Something interesting happened"); |
| |
| utils::Identifier uuid_two = IdGenerator::getIdGenerator()->generate(); |
| std::unique_ptr<Bookmark> bookmark_two = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid_two); |
| |
| REQUIRE(bookmarkAsXml(bookmark_one) != bookmarkAsXml(bookmark_two)); |
| |
| WHEN("we create a new bookmark with state manager one") { |
| std::unique_ptr<Bookmark> bookmark_one_same = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid_one); |
| |
| THEN("it will be the same as bookmark one.") { |
| REQUIRE(bookmarkAsXml(bookmark_one_same) == bookmarkAsXml(bookmark_one)); |
| } |
| } |
| |
| WHEN("saveBookmarkXml() is called on bookmark one with the XML of bookmark two, " |
| "and then we create a new bookmark with state manager one") { |
| bookmark_one->saveBookmarkXml(bookmarkAsXml(bookmark_two)); |
| std::unique_ptr<Bookmark> bookmark_one_different = createBookmark(*test_plan, APPLICATION_CHANNEL, uuid_one); |
| |
| THEN("it will be the same as bookmark two.") { |
| REQUIRE(bookmarkAsXml(bookmark_one_different) == bookmarkAsXml(bookmark_two)); |
| } |
| } |
| } |
| } |