/* * This source file is part of RmlUi, the HTML/CSS Interface Middleware * * For the latest information, see http://github.com/mikke89/RmlUi * * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd * Copyright (c) 2019 The RmlUi Team, and contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "TestNavigator.h" #include "TestSuite.h" #include "Screenshot.h" #include "TestViewer.h" #include #include #include // When capturing frames it seems we need to wait at least an extra frame for the newly submitted // render to be read back out. If we don't wait, we end up saving a screenshot of the previous test. constexpr int capture_wait_frame_count = 2; TestNavigator::TestNavigator(ShellRenderInterfaceOpenGL* shell_renderer, Rml::Context* context, TestViewer* viewer, TestSuiteList test_suites) : shell_renderer(shell_renderer), context(context), viewer(viewer), test_suites(std::move(test_suites)) { RMLUI_ASSERT(context); RMLUI_ASSERTMSG(!this->test_suites.empty(), "At least one test suite is required."); context->GetRootElement()->AddEventListener(Rml::EventId::Keydown, this); context->GetRootElement()->AddEventListener(Rml::EventId::Textinput, this); LoadActiveTest(); } TestNavigator::~TestNavigator() { context->GetRootElement()->RemoveEventListener(Rml::EventId::Keydown, this); context->GetRootElement()->RemoveEventListener(Rml::EventId::Textinput, this); } void TestNavigator::Update() { if (capture_index >= 0 && capture_wait_frames > 0) { capture_wait_frames -= 1; } else if (capture_index >= 0) { RMLUI_ASSERT(capture_index < CurrentSuite().GetNumTests()); capture_wait_frames += capture_wait_frame_count; if (!CaptureCurrentView()) { StopCaptureFullTestSuite(); return; } capture_index += 1; TestSuite& suite = CurrentSuite(); if (capture_index < suite.GetNumTests()) { suite.SetIndex(capture_index); LoadActiveTest(); } else { StopCaptureFullTestSuite(); } } } void TestNavigator::ProcessEvent(Rml::Event& event) { if (event == Rml::EventId::Keydown) { auto key_identifier = (Rml::Input::KeyIdentifier)event.GetParameter< int >("key_identifier", 0); bool key_ctrl = event.GetParameter< bool >("ctrl_key", false); bool key_shift = event.GetParameter< bool >("shift_key", false); if (key_identifier == Rml::Input::KI_LEFT) { if (CurrentSuite().SetIndex(CurrentSuite().GetIndex() - 1)) { LoadActiveTest(); } } else if (key_identifier == Rml::Input::KI_RIGHT) { if (CurrentSuite().SetIndex(CurrentSuite().GetIndex() + 1)) { LoadActiveTest(); } } else if (key_identifier == Rml::Input::KI_UP) { int new_index = std::max(0, index - 1); if (new_index != index) { index = new_index; LoadActiveTest(); } } else if (key_identifier == Rml::Input::KI_DOWN) { int new_index = std::min((int)test_suites.size() - 1, index + 1); if (new_index != index) { index = new_index; LoadActiveTest(); } } else if (key_identifier == Rml::Input::KI_F7) { if (key_ctrl && key_shift) StartCaptureFullTestSuite(); else CaptureCurrentView(); } else if (key_identifier == Rml::Input::KI_S) { if (source_state == SourceType::None) { source_state = (key_shift ? SourceType::Reference : SourceType::Test); } else { if (key_shift) source_state = (source_state == SourceType::Reference ? SourceType::Test : SourceType::Reference); else source_state = SourceType::None; } viewer->ShowSource(source_state); } else if (key_identifier == Rml::Input::KI_ESCAPE) { if (capture_index >= 0) { StopCaptureFullTestSuite(); } else if (source_state != SourceType::None) { source_state = SourceType::None; viewer->ShowSource(source_state); } else { Shell::RequestExit(); } } else if (key_identifier == Rml::Input::KI_C && key_ctrl) { if (key_shift) Shell::SetClipboardText(CurrentSuite().GetDirectory() + '/' + viewer->GetReferenceFilename()); else Shell::SetClipboardText(CurrentSuite().GetPath()); } else if (key_identifier == Rml::Input::KI_HOME) { CurrentSuite().SetIndex(0); LoadActiveTest(); } else if (key_identifier == Rml::Input::KI_END) { CurrentSuite().SetIndex(CurrentSuite().GetNumTests() - 1); LoadActiveTest(); } else if (goto_index >= 0 && key_identifier == Rml::Input::KI_BACK) { if (goto_index <= 0) { goto_index = -1; viewer->SetGoToText(""); } else { goto_index = goto_index / 10; viewer->SetGoToText(Rml::CreateString(64, "Go To: %d", goto_index)); } } } if (event == Rml::EventId::Textinput) { const Rml::String text = event.GetParameter< Rml::String >("text", ""); for (const char c : text) { if (c >= '0' && c <= '9') { if (goto_index < 0) goto_index = 0; goto_index = goto_index * 10 + int(c - '0'); viewer->SetGoToText(Rml::CreateString(64, "Go To: %d", goto_index)); } else if (goto_index >= 0 && c == '\n') { if (goto_index > 0) { if (CurrentSuite().SetIndex(goto_index)) { LoadActiveTest(); } else { viewer->SetGoToText(Rml::CreateString(64, "Go To out of bounds.", goto_index)); } } goto_index = -1; } } } } void TestNavigator::LoadActiveTest() { const TestSuite& suite = CurrentSuite(); viewer->LoadTest(suite.GetDirectory(), suite.GetFilename(), suite.GetIndex(), suite.GetNumTests(), index, (int)test_suites.size()); viewer->ShowSource(source_state); } bool TestNavigator::CaptureCurrentView() { Rml::String filename = CurrentSuite().GetFilename(); filename = filename.substr(0, filename.rfind('.')) + ".png"; bool result = CaptureScreenshot(shell_renderer, filename, 1060); return result; } void TestNavigator::StopCaptureFullTestSuite() { const Rml::String output_directory = GetOutputDirectory(); TestSuite& suite = CurrentSuite(); const int num_tests = suite.GetNumTests(); if (capture_index == num_tests) { Rml::Log::Message(Rml::Log::LT_INFO, "Successfully captured %d document screenshots to directory: %s", capture_index, output_directory.c_str()); } else { Rml::Log::Message(Rml::Log::LT_ERROR, "Test suite capture aborted after %d of %d test(s). Output directory: %s", capture_index, num_tests, output_directory.c_str()); } suite.SetIndex(capture_initial_index); LoadActiveTest(); capture_index = -1; capture_initial_index = -1; capture_wait_frames = -1; } void TestNavigator::StartCaptureFullTestSuite() { if(capture_index == -1) { source_state = SourceType::None; TestSuite& suite = CurrentSuite(); capture_initial_index = suite.GetIndex(); capture_wait_frames = capture_wait_frame_count; capture_index = 0; suite.SetIndex(capture_index); LoadActiveTest(); } }