ScriptReporter.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include <AzCore/Debug/TraceMessageBus.h>
  10. #include <AzFramework/StringFunc/StringFunc.h>
  11. #include <Atom/Feature/Utils/FrameCaptureBus.h>
  12. #include <Atom/Feature/Utils/FrameCaptureTestBus.h>
  13. #include <Atom/Utils/ImageComparison.h>
  14. #include <Automation/ImageComparisonConfig.h>
  15. #include <Utils/ImGuiMessageBox.h>
  16. #include <Atom/Utils/PngFile.h>
  17. #include <imgui/imgui.h>
  18. namespace AtomSampleViewer
  19. {
  20. struct ImageComparisonToleranceLevel;
  21. //! Collects data about each script run by the ScriptManager.
  22. //! This includes counting errors, checking screenshots, and providing a final report dialog.
  23. class ScriptReporter
  24. {
  25. public:
  26. // currently set to track the ScriptReport index and the ScreenshotTestInfo index.
  27. using ReportIndex = AZStd::pair<size_t, size_t>;
  28. static constexpr const char* TestResultsFolder = "TestResults";
  29. static constexpr const char* UserFolder = "user";
  30. //! Set the list of available tolerance levels, so the report can suggest an alternate level that matches the actual results.
  31. void SetAvailableToleranceLevels(const AZStd::vector<ImageComparisonToleranceLevel>& toleranceLevels);
  32. //! Clears all recorded data.
  33. void Reset();
  34. //! Invalidates the final results when displaying a report to the user. This can be used to highlight
  35. //! local changes that were made, and remind the user that these results should not be considered official.
  36. //! Use an empty string to clear the invalidation.
  37. void SetInvalidationMessage(const AZStd::string& message);
  38. //! Indicates that a new script has started processing.
  39. //! Any subsequent errors will be included as part of this script's report.
  40. void PushScript(const AZStd::string& scriptAssetPath);
  41. //! Indicates that the current script has finished executing.
  42. //! Any subsequent errors will be included as part of the prior script's report.
  43. void PopScript();
  44. //! Returns whether there are active processing scripts (i.e. more PushScript() calls than PopScript() calls)
  45. bool HasActiveScript() const;
  46. //! Indicates that a new screenshot is about to be captured.
  47. bool AddScreenshotTest(const AZStd::string& imageName);
  48. //! Check the latest screenshot using default thresholds.
  49. void CheckLatestScreenshot(const ImageComparisonToleranceLevel* comparisonPreset);
  50. //! Opens the script report dialog.
  51. //! This displays all the collected script reporting data, provides links to tools for analyzing data like
  52. //! viewing screenshot diffs. It can be left open during processing and will update in real-time.
  53. void OpenReportDialog();
  54. void HideReportDialog();
  55. //! Called every frame to update the ImGui dialog
  56. void TickImGui();
  57. //! Returns true if there are any errors or asserts in the script report
  58. bool HasErrorsAssertsInReport() const;
  59. struct ScriptResultsSummary
  60. {
  61. uint32_t m_totalAsserts = 0;
  62. uint32_t m_totalErrors = 0;
  63. uint32_t m_totalWarnings = 0;
  64. uint32_t m_totalScreenshotsCount = 0;
  65. uint32_t m_totalScreenshotsFailed = 0;
  66. uint32_t m_totalScreenshotWarnings = 0;
  67. };
  68. //! Displays the script results summary in ImGui.
  69. void DisplayScriptResultsSummary();
  70. //! Retrieves the current script result summary.
  71. const ScriptResultsSummary& GetScriptResultSummary() const;
  72. struct ImageComparisonResult
  73. {
  74. enum class ResultCode
  75. {
  76. None,
  77. Pass,
  78. FileNotFound,
  79. FileNotLoaded,
  80. WrongSize,
  81. WrongFormat,
  82. NullImageComparisonToleranceLevel,
  83. ThresholdExceeded
  84. };
  85. ResultCode m_resultCode = ResultCode::None;
  86. //! The diff score that was used for comparison..
  87. //! The diff score can be before or after filtering out visually imperceptible differences,
  88. //! depending on the tolerance level settings.
  89. //! See CalcImageDiffRms.
  90. float m_diffScore = 0.0f;
  91. AZStd::string GetSummaryString() const;
  92. };
  93. //! Records all the information about a screenshot comparison test.
  94. struct ScreenshotTestInfo
  95. {
  96. AZStd::string m_screenshotFilePath; //!< The full path where the screenshot will be generated.
  97. AZStd::string m_officialBaselineScreenshotFilePath; //!< The full path to the official baseline image that is checked into source control
  98. AZStd::string m_localBaselineScreenshotFilePath; //!< The full path to a local baseline image that was established by the user
  99. ImageComparisonToleranceLevel m_toleranceLevel; //!< Tolerance for checking against the official baseline image
  100. ImageComparisonResult m_officialComparisonResult; //!< Result of comparing against the official baseline image, for reporting test failure
  101. ImageComparisonResult m_localComparisonResult; //!< Result of comparing against a local baseline, for reporting warnings
  102. ScreenshotTestInfo(const AZStd::string& m_screenshotName);
  103. };
  104. //! Records all the information about a single test script.
  105. struct ScriptReport : public AZ::Debug::TraceMessageBus::Handler
  106. {
  107. ScriptReport()
  108. {
  109. AZ::Debug::TraceMessageBus::Handler::BusConnect();
  110. }
  111. ~ScriptReport()
  112. {
  113. AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
  114. }
  115. bool OnPreAssert(const char* /*fileName*/, int /*line*/, const char* /*func*/, [[maybe_unused]] const char* message) override
  116. {
  117. ++m_assertCount;
  118. return false;
  119. }
  120. bool OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) override
  121. {
  122. if (AZStd::string::npos == AzFramework::StringFunc::Find(message, "Screenshot check failed"))
  123. {
  124. ++m_generalErrorCount;
  125. }
  126. else
  127. {
  128. ++m_screenshotErrorCount;
  129. }
  130. return false;
  131. }
  132. bool OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) override
  133. {
  134. if (AZStd::string::npos == AzFramework::StringFunc::Find(message, "Screenshot does not match the local baseline"))
  135. {
  136. ++m_generalWarningCount;
  137. }
  138. else
  139. {
  140. ++m_screenshotWarningCount;
  141. }
  142. return false;
  143. }
  144. AZStd::string m_scriptAssetPath;
  145. uint32_t m_assertCount = 0;
  146. uint32_t m_generalErrorCount = 0;
  147. uint32_t m_screenshotErrorCount = 0;
  148. uint32_t m_generalWarningCount = 0;
  149. uint32_t m_screenshotWarningCount = 0;
  150. AZStd::vector<ScreenshotTestInfo> m_screenshotTests;
  151. };
  152. const AZStd::vector<ScriptReport>& GetScriptReport() const { return m_scriptReports; }
  153. // For exporting test results
  154. void ExportTestResults();
  155. void ExportImageDiff(const char* filePath, const ScreenshotTestInfo& screenshotTest);
  156. AZStd::string ExportImageDiff(const ScriptReport& scriptReport, const ScreenshotTestInfo& screenshotTest);
  157. void SortScriptReports();
  158. private:
  159. static const ImGuiTreeNodeFlags FlagDefaultOpen = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_DefaultOpen;
  160. static const ImGuiTreeNodeFlags FlagDefaultClosed = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
  161. // Reports a script error using standard formatting that matches ScriptManager
  162. enum class TraceLevel
  163. {
  164. Error,
  165. Warning
  166. };
  167. // Controls which results are shown to the user
  168. // Must match static const char* DiplayOptions in .cpp file
  169. enum DisplayOption : int
  170. {
  171. AllResults,
  172. WarningsAndErrors,
  173. ErrorsOnly
  174. };
  175. // Controls how screenshot reports are sorted
  176. // Must match static const char* DiplayOptions in .cpp file
  177. enum SortOption : int
  178. {
  179. Unsorted,
  180. OfficialBaselineDiffScore,
  181. LocalBaselineDiffScore
  182. };
  183. static void ReportScriptError(const AZStd::string& message);
  184. static void ReportScriptWarning(const AZStd::string& message);
  185. static void ReportScriptIssue(const AZStd::string& message, TraceLevel traceLevel);
  186. static void ReportScreenshotComparisonIssue(const AZStd::string& message, const AZStd::string& expectedImageFilePath, const AZStd::string& actualImageFilePath, TraceLevel traceLevel);
  187. // Copies all captured screenshots to the local baseline folder. These can be used as an alternative to the central baseline for comparison.
  188. void UpdateAllLocalBaselineImages();
  189. // Copies a single captured screenshot to the local baseline folder. This can be used as an alternative to the central baseline for comparison.
  190. bool UpdateLocalBaselineImage(ScreenshotTestInfo& screenshotTest, bool showResultDialog);
  191. // Copies a single captured screenshot to the official baseline source folder.
  192. bool UpdateSourceBaselineImage(ScreenshotTestInfo& screenshotTest, bool showResultDialog);
  193. // Clears comparison result to passing with no errors or warnings
  194. void ClearImageComparisonResult(ImageComparisonResult& comparisonResult);
  195. // Show a message box to let the user know the results of updating local baseline images
  196. void ShowUpdateLocalBaselineResult(int successCount, int failureCount);
  197. const ImageComparisonToleranceLevel* FindBestToleranceLevel(float diffScore, bool filterImperceptibleDiffs) const;
  198. void ShowReportDialog();
  199. void ShowScreenshotTestInfoTreeNode(const AZStd::string& header, ScriptReport& scriptReport, ScreenshotTestInfo& screenshotResult);
  200. void ShowDiffButton(const char* buttonLabel, const AZStd::string& imagePathA, const AZStd::string& imagePathB);
  201. // Generates a path to the exported test results file.
  202. AZStd::string GenerateTimestamp() const;
  203. AZStd::string GenerateAndCreateExportedImageDiffPath(const ScriptReport& scriptReport, const ScreenshotTestInfo& screenshotTest) const;
  204. AZStd::string GenerateAndCreateExportedTestResultsPath() const;
  205. // Generates a diff between two images of the same size.
  206. void GenerateImageDiff(AZStd::span<const uint8_t> img1, AZStd::span<const uint8_t> img2, AZStd::vector<uint8_t>& buffer);
  207. ScriptReport* GetCurrentScriptReport();
  208. AZStd::string SeeConsole(uint32_t issueCount, const char* searchString);
  209. AZStd::string SeeBelow(uint32_t issueCount);
  210. void HighlightTextIf(bool shouldSet, ImVec4 color);
  211. void ResetTextHighlight();
  212. void HighlightTextFailedOrWarning(bool isFailed, bool isWarning);
  213. struct HighlightColorSettings
  214. {
  215. ImVec4 m_highlightPassed;
  216. ImVec4 m_highlightFailed;
  217. ImVec4 m_highlightWarning;
  218. void UpdateColorSettings();
  219. };
  220. using SortedReportIndexMap = AZStd::multimap<float, ReportIndex, AZStd::greater<float>>;
  221. SortedReportIndexMap m_reportsSortedByOfficialBaslineScore;
  222. SortedReportIndexMap m_reportsSortedByLocaBaslineScore;
  223. SortOption m_currentSortOption = SortOption::OfficialBaselineDiffScore;
  224. ImGuiMessageBox m_messageBox;
  225. AZStd::vector<ImageComparisonToleranceLevel> m_availableToleranceLevels;
  226. AZStd::string m_invalidationMessage;
  227. AZStd::vector<ScriptReport> m_scriptReports; //< Tracks errors for the current active script
  228. AZStd::vector<size_t> m_currentScriptIndexStack; //< Tracks which of the scripts in m_scriptReports is currently active
  229. bool m_showReportDialog = false;
  230. bool m_colorHasBeenSet = false;
  231. DisplayOption m_displayOption = DisplayOption::AllResults;
  232. bool m_forceShowUpdateButtons = false; //< By default, the "Update" buttons are visible only for failed screenshots. This forces them to be visible.
  233. bool m_forceShowExportPngDiffButtons = false; //< By default, "Export Png Diff" buttons are visible only for failed screenshots. This forces them to be visible.
  234. AZStd::string m_officialBaselineSourceFolder; //< Used for updating official baseline screenshots
  235. AZStd::string m_exportedTestResultsPath = "Click the 'Export Test Results' button."; //< Path to exported test results file (if exported).
  236. AZStd::string m_uniqueTimestamp;
  237. HighlightColorSettings m_highlightSettings;
  238. ScriptResultsSummary m_resultsSummary;
  239. // Flags set and used by ShowReportDialog()
  240. bool m_showAll;
  241. bool m_showWarnings;
  242. };
  243. } // namespace AtomSampleViewer