From d35e105b7a10e2b996139042e5e114e6644099e3 Mon Sep 17 00:00:00 2001 From: Gerd Aschemann Date: Fri, 13 Dec 2024 19:39:36 +0100 Subject: [PATCH] Add Findings by Throwable in exception contexts as we would like to be able to check more detailed error conditions when the problems occur (#270). --- .../check/BrokenHttpLinksChecker.java | 4 +-- .../htmlsanitycheck/collect/Finding.java | 29 ++++++++++++++----- .../check/BrokenHttpLinksCheckerSpec.groovy | 21 ++++++++++++++ 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/check/BrokenHttpLinksChecker.java b/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/check/BrokenHttpLinksChecker.java index 5ff2ad56..840a90c6 100644 --- a/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/check/BrokenHttpLinksChecker.java +++ b/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/check/BrokenHttpLinksChecker.java @@ -169,10 +169,10 @@ else if (Web.HTTP_REDIRECT_CODES.contains(responseCode)) { firstConnection.disconnect(); } catch (UnknownHostException exception) { - Finding unknownHostFinding = new Finding("Unknown host with href=" + href); + Finding unknownHostFinding = new Finding("Unknown host with href=" + href, exception); getCheckingResults().addFinding(unknownHostFinding); } catch (IOException exception) { - Finding someException = new Finding("exception " + exception + " with href=" + href); + Finding someException = new Finding("exception " + exception + " with href=" + href, exception); getCheckingResults().addFinding(someException); } } diff --git a/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/collect/Finding.java b/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/collect/Finding.java index e65dbeb6..8a26bf44 100644 --- a/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/collect/Finding.java +++ b/htmlSanityCheck-core/src/main/java/org/aim42/htmlsanitycheck/collect/Finding.java @@ -6,6 +6,7 @@ import lombok.Setter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -22,6 +23,7 @@ public class Finding { // suggestions are ordered: getAt(0) yields the best, getAt(1) the second and so forth @Setter List suggestions; + Throwable throwable; public Finding() { this(""); @@ -34,14 +36,18 @@ public Finding() { * @param whatIsTheProblem An explanation of what went wrong (i.e., name of the missing file) */ public Finding(String whatIsTheProblem) { - this(whatIsTheProblem, 1, new ArrayList<>(3)); + this(whatIsTheProblem, 1, new ArrayList<>(3), null); + } + + public Finding(final String whatIsTheProblem, final Throwable throwable) { + this(whatIsTheProblem, 1, new ArrayList<>(1), throwable); } /** * Finding with explanation and several occurrences */ public Finding(String whatIsTheProblem, int nrOfOccurrences) { - this(whatIsTheProblem, nrOfOccurrences, new ArrayList<>(3)); + this(whatIsTheProblem, nrOfOccurrences, new ArrayList<>(3), null); } @@ -49,13 +55,17 @@ public Finding(String whatIsTheProblem, int nrOfOccurrences) { * Most general constructor: * create Finding with explanation and nrOfOccurrences * - * @param whatIsTheProblem An explanation of what went wrong (i.e. name of missing file) + * @param whatIsTheProblem An explanation of what went wrong (i.e., name of the missing file). + * @param nrOfOccurrences The number of occurrences of a specific issue or event. + * @param suggestions A list of suggestions related to resolving or addressing the issue. + * @param throwable The throwable instance representing an exception or error related to this occurrence. */ - public Finding(String whatIsTheProblem, int nrOfOccurrences, List suggestions) { + public Finding(String whatIsTheProblem, int nrOfOccurrences, + List suggestions, Throwable throwable) { this.whatIsTheProblem = whatIsTheProblem; this.nrOfOccurrences = nrOfOccurrences; this.suggestions = suggestions; - + this.throwable = throwable; } /** @@ -65,15 +75,20 @@ public Finding(String whatIsTheProblem, int nrOfOccurrences, List sugges * @param suggestions what could have been meant */ public Finding(String whatIsTheProblem, List suggestions) { - this(whatIsTheProblem, 1, suggestions); + this(whatIsTheProblem, 1, suggestions, null); } @Override public String toString() { String refCount = (nrOfOccurrences > 1) ? String.format(" (reference count: %d)", nrOfOccurrences) : ""; String suggestionStr = (!suggestions.isEmpty()) ? "\n (Suggestions: " + String.join(",", suggestions) + ")" : ""; + String stackTrace = (null != throwable) + ? "\nStackTrace:\n\t" + String.join("\n\t", Arrays.stream(throwable.getStackTrace()) + .map(StackTraceElement::toString) + .toArray(String[]::new)) + : ""; - return whatIsTheProblem + refCount + (suggestionStr.isEmpty() ? "" : suggestionStr); + return whatIsTheProblem + refCount + suggestionStr + stackTrace; } diff --git a/htmlSanityCheck-core/src/test/groovy/org/aim42/htmlsanitycheck/check/BrokenHttpLinksCheckerSpec.groovy b/htmlSanityCheck-core/src/test/groovy/org/aim42/htmlsanitycheck/check/BrokenHttpLinksCheckerSpec.groovy index 0e102db2..41904de7 100644 --- a/htmlSanityCheck-core/src/test/groovy/org/aim42/htmlsanitycheck/check/BrokenHttpLinksCheckerSpec.groovy +++ b/htmlSanityCheck-core/src/test/groovy/org/aim42/htmlsanitycheck/check/BrokenHttpLinksCheckerSpec.groovy @@ -275,6 +275,27 @@ class BrokenHttpLinksCheckerSpec extends Specification { collector?.getFindings()?.first()?.whatIsTheProblem?.contains("suspicious") } + + def "expect an unknown host exception"() { + given: "an HTML page with a link to a non-resolvable hostname" + String unknownHost = "http://nonexistent-host.fake" + String HTML = """$HtmlConst.HTML_HEAD + Broken Link + $HtmlConst.HTML_END """ + + htmlPage = new HtmlPage(HTML) + + when: "the page is checked" + collector = brokenHttpLinksChecker.performCheck(htmlPage) + + then: "the exception is caught and an appropriate problem is recorded" + collector.getFindings()?.size() == 1 + + and: "the problem describes the unknown host exception" + collector.getFindings()?.first()?.whatIsTheProblem?.contains("Unknown host with href=") + collector.getFindings()?.first()?.whatIsTheProblem?.contains("nonexistent-host.fake") + collector.getFindings()?.first()?.throwable instanceof UnknownHostException + } } /************************************************************************