From ce10c1fed5f1ccd7e41636e6309bb8655cb8eca2 Mon Sep 17 00:00:00 2001 From: Daniel Smrcek Date: Sun, 17 Nov 2019 21:11:20 +0100 Subject: [PATCH 01/10] Server log report extension --- jbehave-support-core/docs/Reporting.md | 10 +- jbehave-support-core/docs/Ssh.md | 7 + .../ServerLogXmlReporterExtension.java | 176 +++++++++++------- .../jbehavesupport/core/ssh/SshHandler.java | 139 +++++++++++++- .../core/ssh/SshReportType.java | 10 + .../org/jbehavesupport/core/ssh/SshSteps.java | 5 + .../jbehavesupport/core/ssh/SshTemplate.java | 8 + .../report-generator/serverLogXmlRep.xslt | 122 ++++++++---- .../test/sample/SampleStoriesIT.java | 1 + .../test/sample/SshReport.story | 32 ++++ .../test/support/TestConfig.java | 28 ++- 11 files changed, 426 insertions(+), 112 deletions(-) create mode 100644 jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java create mode 100644 jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SshReport.story diff --git a/jbehave-support-core/docs/Reporting.md b/jbehave-support-core/docs/Reporting.md index 8593bf96..2934d81d 100644 --- a/jbehave-support-core/docs/Reporting.md +++ b/jbehave-support-core/docs/Reporting.md @@ -37,10 +37,18 @@ There are several extensions already prepared and ready to use: - `JmsXmlReporterExtension` (copies [JMS](Jms.md) message headers, prints out the message if it is `javax.jms.TextMessage`) - `ScreenshotReporterExtension` (prints out screenshots (except error) from [Web testing](Web-testing.md) - if any were generated) - Frequency of screenshot taking can be controlled by property: 'web.screenshot.reporting.mode' - - MANUAL: screenshots from manual step only + - MANUAL: screenshots from a manual step only - WAIT: screenshots after every web wait - STEP: screenshots after every web step - DEBUG: screenshots after every web step and action + - `ServerLogXmlReporterExtension` (copies server logs, or their parts used by [SshSteps](Ssh.md)) + - Content can be controlled by property: ssh.reporting.mode + - FULL: copies server log(s) for each system with configured [SshTemplate](Ssh.md) + - TEMPLATE: copies server log(s) for each system with configured SshTemplate with an attribute reportable = true + - CACHE: copies caches server log(s) used within scenario execution + - Extension contain fail mode, which act like TEMPLATE mode if test fails + - it can be turned on by using property: ssh.reporting.logOnFailure with value "true" + To use these extensions simply register the wanted extension as a bean, e.g.: ``` diff --git a/jbehave-support-core/docs/Ssh.md b/jbehave-support-core/docs/Ssh.md index c4874f78..af3e29d6 100644 --- a/jbehave-support-core/docs/Ssh.md +++ b/jbehave-support-core/docs/Ssh.md @@ -72,3 +72,10 @@ Given log start timestamp is set to [NCAKO_1] Given log end timestamp is set to [NCAKO_2] ``` Afterwards the same principle of searching the logs as described above applies + +#### Ssh reporter mode +Following step allows setting specific server log report extension mode +``` +Given ssh reporter mode is set to [TEMPLATE] +``` + diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java index fa70f38c..114571b3 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java @@ -1,33 +1,70 @@ package org.jbehavesupport.core.report.extension; +import java.io.File; +import java.io.IOException; import java.io.Writer; -import java.util.ArrayList; -import java.util.Arrays; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Date; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import lombok.Getter; import lombok.RequiredArgsConstructor; -import org.jbehavesupport.core.report.ReportContext; -import org.jbehavesupport.core.ssh.SshLog; -import org.jbehavesupport.core.ssh.SshTemplate; - +import lombok.Setter; +import org.apache.commons.collections4.keyvalue.MultiKey; +import org.apache.commons.collections4.map.LRUMap; +import org.apache.commons.collections4.map.MultiKeyMap; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; +import org.apache.commons.io.FileUtils; +import org.jbehavesupport.core.TestContext; +import org.jbehavesupport.core.report.ReportContext; +import org.jbehavesupport.core.internal.FileNameResolver; +import org.jbehavesupport.core.report.ReportRenderingPhase; +import org.jbehavesupport.core.ssh.SshReportType; +import org.springframework.beans.factory.annotation.Value; @RequiredArgsConstructor public class ServerLogXmlReporterExtension extends AbstractXmlReporterExtension { + private final TestContext testContext; + private final FileNameResolver fileNameResolver; + private static final String SSH_XML_REPORTER_EXTENSION = "serverLog"; private static final String LOG = "log"; + private static final String FILE = "file"; + private static final String TEXT = "text"; private static final String SYSTEM = "system"; private static final String FAIL = "fail"; + private static final String FILE_NAME_PATTERN = "LOG_%s_%s.txt"; + + private MultiKeyMap logContents = MultiKeyMap.multiKeyMap(new LRUMap()); + + @Value("${ssh.reporting.maxLogLength:10000}") + private Long maxLength; + + @Getter + @Setter + private SshReportType sshReportType; - private final ConfigurableListableBeanFactory beanFactory; + @Value("${ssh.reporting.logOnFailure:false}") + @Getter + private boolean loggingOnFailure; + + @Value("${web.reporting.directory:./target/reports}") + private String logFileDirectory; + + @Override + public ReportRenderingPhase getReportRenderingPhase() { + return ReportRenderingPhase.AFTER_STORY; + } + + public void registerLogContent(MultiKey multiKey, String logContent) { + if (logContents.containsKey(multiKey)) { + logContent += logContents.get(multiKey); + } + logContents.put(multiKey, logContent); + } @Override public String getName() { @@ -42,75 +79,70 @@ public Long getPriority() { @Override public void print(final Writer writer, final ReportContext reportContext) { - Map> sshTemplates = getSshTemplates(); - for (String qualifier : sshTemplates.keySet()) { - printBegin(writer, SYSTEM, getSshQualifierAttributes(qualifier)); - for (SshTemplate sshTemplate : sshTemplates.get(qualifier)) { - printBegin(writer, LOG, getSshTemplateAttributes(reportContext, sshTemplate)); - try { - SshLog sshLog = sshTemplate.copyLog(reportContext.startExecutionZoned(), reportContext.endExecutionZoned()); - printCData(writer, sshLog.getLogContents()); - } catch (Exception e) { - printBegin(writer, FAIL); - printCData(writer, ExceptionUtils.getStackTrace(e)); - printEnd(writer, FAIL); - } + printContent(writer); + logContents.clear(); + } + + private void printContent(final Writer writer) { + logContents.entrySet().stream().forEach( + entry -> { + final MultiKey key = entry.getKey(); + printBegin(writer, SYSTEM, getSshQualifierAttributes(key.getKey(0).toString())); + printBegin(writer, LOG, getSshAttributesFromKey(key)); + printLogContent(writer, key.getKey(0).toString(), entry.getValue()); printEnd(writer, LOG); + printEnd(writer, SYSTEM); } - printEnd(writer, SYSTEM); - } + ); } - private Map getSshQualifierAttributes(String qualifier) { - Map sshQualifierAttributes = new HashMap<>(); - sshQualifierAttributes.put("id", qualifier); - return sshQualifierAttributes; + private void printLogContent(Writer writer, String qualifier, String sshLog) { + if (sshLog.length() > maxLength) { + printLogFile(writer, sshLog, qualifier); + } else { + printBegin(writer, TEXT); + printCData(writer, sshLog); + printEnd(writer, TEXT); + } } - private Map getSshTemplateAttributes(ReportContext reportContext, SshTemplate sshTemplate) { - Map sshTemplateAttributes = new HashMap<>(); - sshTemplateAttributes.put("startDate", reportContext.startExecutionZoned().toString()); - sshTemplateAttributes.put("endDate", reportContext.endExecutionZoned().toString()); - sshTemplateAttributes.put("host", sshTemplate.getSshSetting().getHostname() + ":" + sshTemplate.getSshSetting().getPort()); - sshTemplateAttributes.put("logPath", sshTemplate.getSshSetting().getLogPath()); - return sshTemplateAttributes; + private void printLogFile(final Writer writer, String logContent, String qualifier) { + File logFile = new File(new Date().getTime() + ".txt"); + try { + prepareDirectory(); + FileUtils.writeStringToFile(logFile, logContent, (String) null); + File destinationFile = fileNameResolver.resolveFilePath(FILE_NAME_PATTERN, logFileDirectory, qualifier).toFile(); + FileUtils.copyFile(logFile, destinationFile); + printBegin(writer, FILE); + printString(writer, destinationFile.getName()); + printEnd(writer, FILE); + } catch (IOException e) { + printBegin(writer, FAIL); + printCData(writer, ExceptionUtils.getStackTrace(e)); + printEnd(writer, FAIL); + } + logFile.delete(); } - private Map> getSshTemplatesForType(Class clazz) { - Map> sshTemplates = new HashMap<>(); - String[] beanNames = beanFactory.getBeanNamesForType(clazz); - for (String beanName : beanNames) { - BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName); - if (bd instanceof RootBeanDefinition) { - Qualifier qualifier = ((RootBeanDefinition) bd).getResolvedFactoryMethod().getAnnotation(Qualifier.class); - if (sshTemplates.get(qualifier.value()) == null) { - sshTemplates.put(qualifier.value(), new ArrayList<>()); - } - sshTemplates.get(qualifier.value()).add((T) beanFactory.getBean(beanName)); - } + private void prepareDirectory() throws IOException { + if (!Paths.get(logFileDirectory).toFile().exists()) { + Files.createDirectory(Paths.get(logFileDirectory)); + testContext.put("logFileDirectory", logFileDirectory); } - return sshTemplates; } - private Map> getSshTemplates() { - Map> sshTemplatesArray = getSshTemplatesForType(SshTemplate[].class); - Map> sshTemplates = getSshTemplatesForType(SshTemplate.class); - - //merge both maps together to simple Map> - sshTemplatesArray.entrySet() - .stream() - .forEach(entry -> { - List flattenedSshTemplatesArray = entry.getValue() - .stream() - .flatMap(Arrays::stream) - .collect(Collectors.toList()); - if (sshTemplates.get(entry.getKey()) != null) { - sshTemplates.get(entry.getKey()).addAll(flattenedSshTemplatesArray); - } else { - sshTemplates.put(entry.getKey(), flattenedSshTemplatesArray); - } - }); - - return sshTemplates; + private Map getSshQualifierAttributes(String qualifier) { + Map sshQualifierAttributes = new HashMap<>(); + sshQualifierAttributes.put(SYSTEM, qualifier); + return sshQualifierAttributes; + } + + private Map getSshAttributesFromKey(MultiKey key) { + Map sshContextAttributes = new HashMap<>(); + sshContextAttributes.put("startDate", key.getKey(1).toString()); + sshContextAttributes.put("endDate", key.getKey(2).toString()); + sshContextAttributes.put("host", key.getKey(3).toString()); + sshContextAttributes.put("logPath", key.getKey(4).toString()); + return sshContextAttributes; } } diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java index 3dfce500..3ca780e1 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java @@ -5,26 +5,36 @@ import org.apache.commons.collections4.keyvalue.MultiKey; import org.apache.commons.collections4.map.LRUMap; import org.apache.commons.collections4.map.MultiKeyMap; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.assertj.core.api.SoftAssertions; +import org.jbehave.core.annotations.AfterScenario; import org.jbehave.core.annotations.BeforeScenario; +import org.jbehave.core.annotations.ScenarioType; import org.jbehave.core.model.ExamplesTable; import org.jbehavesupport.core.TestContext; import org.jbehavesupport.core.expression.ExpressionEvaluatingParameter; import org.jbehavesupport.core.internal.parameterconverters.ExamplesEvaluationTableConverter; +import org.jbehavesupport.core.report.extension.ServerLogXmlReporterExtension; import org.jbehavesupport.core.verification.Verifier; import org.jbehavesupport.core.verification.VerifierResolver; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; import java.io.IOException; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.jbehavesupport.core.internal.ExampleTableConstraints.VERIFIER; @@ -64,11 +74,17 @@ public class SshHandler { private final ExamplesEvaluationTableConverter tableConverter; private final VerifierResolver verifierResolver; + @Autowired(required = false) + ServerLogXmlReporterExtension serverLogXmlReporterExtension; + + @Value("${ssh.reporting.mode:CACHE}") + SshReportType defaultReportType; @Value("${ssh.max.assert.count:10}") private int maxSoftAssertCount; private ZonedDateTime scenarioStart; + private ZonedDateTime scenarioEnd; private ZonedDateTime logStartTime; private ZonedDateTime logEndTime; @@ -77,9 +93,43 @@ public class SshHandler { @BeforeScenario public void init() { scenarioStart = ZonedDateTime.now(); + scenarioEnd = null; logStartTime = null; logEndTime = null; logCache.clear(); + if (serverLogXmlReporterExtension != null) { + serverLogXmlReporterExtension.setSshReportType(defaultReportType); + } + } + + @AfterScenario + public void after() { + scenarioEnd = ZonedDateTime.now(); + if (serverLogXmlReporterExtension != null) { + if (serverLogXmlReporterExtension.getSshReportType() == SshReportType.CACHE) { + logCache.entrySet().stream().forEach(entry -> { + MultiKey newKey = new MultiKey(entry.getKey().getKey(0), entry.getKey().getKey(1), entry.getKey().getKey(2), "N/A", "N/A"); + storeInExtension(newKey, (entry.getValue())); + }); + } else if (serverLogXmlReporterExtension.getSshReportType() == SshReportType.FULL) { + storeTemplatesLogs(getSshTemplates()); + } else if (serverLogXmlReporterExtension.getSshReportType() == SshReportType.TEMPLATE) { + storeTemplatesLogs(getReportableSshTemplates(getSshTemplates())); + } + } + } + + @AfterScenario(uponType = ScenarioType.ANY, uponOutcome = AfterScenario.Outcome.FAILURE) + public void afterFailedScenario() { + if (serverLogXmlReporterExtension != null) { + if (serverLogXmlReporterExtension.getSshReportType() != SshReportType.TEMPLATE && + serverLogXmlReporterExtension.isLoggingOnFailure()) { + if (scenarioEnd == null) { + scenarioEnd = ZonedDateTime.now(); + } + storeTemplatesLogs(getReportableSshTemplates(getSshTemplates())); + } + } } public void markLogTime(String logTimeAlias) { @@ -106,6 +156,14 @@ public void checkLogDataPresence(String systemQualifier, String stringTable, Ver checkDataPresence(systemQualifier, scenarioStart, stringTable, verifier); } + public void setSshReporterMode(ExpressionEvaluatingParameter mode) { + if (serverLogXmlReporterExtension != null) { + serverLogXmlReporterExtension.setSshReportType(SshReportType.valueOf(mode.getValue())); + } else { + log.warn("ServerLogXmlReportExtension is not registered, no logs will be present in report."); + } + } + /** * @deprecated use logContainsData(String systemQualifier, String stringTable) instead * If you set timestamps via separate steps, log reading is more accurate and use cache @@ -122,9 +180,9 @@ public void checkLogDataPresence(String systemQualifier, String startTimeAlias, * @param endTime log search end timestamp */ protected String readLog(String systemQualifier, ZonedDateTime startTime, ZonedDateTime endTime) { - if (logCache.containsKey(startTime, endTime, systemQualifier)) { + if (logCache.containsKey(systemQualifier, startTime, endTime)) { log.info("Log found in cache."); - return logCache.get(startTime, endTime, systemQualifier); + return logCache.get(systemQualifier, startTime, endTime); } StringBuilder fetchedLog = new StringBuilder(); List sshTemplates = resolveSshTemplates(systemQualifier); @@ -136,7 +194,7 @@ protected String readLog(String systemQualifier, ZonedDateTime startTime, ZonedD log.error("error fetching {}({}) log: {}", systemQualifier, sshTemplate.getSshSetting(), ex); } } - logCache.put(new MultiKey(startTime, endTime, systemQualifier), fetchedLog.toString()); + logCache.put(new MultiKey(systemQualifier, startTime, endTime), fetchedLog.toString()); return fetchedLog.toString(); } @@ -216,4 +274,79 @@ protected String getSearchColumnName(ExamplesTable searchData) { throw new IllegalArgumentException("No search column found in example table"); } } + + private void storeInExtension(MultiKey key, String logContent) { + if (serverLogXmlReporterExtension != null) { + serverLogXmlReporterExtension.registerLogContent(key, logContent); + } else { + log.warn("ServerLogXmlReportExtension is not registered, no logs will be present in report."); + } + } + + private void storeTemplatesLogs(Map> sshTemplates) { + sshTemplates.entrySet().stream().forEach(entry -> + entry.getValue().stream().forEach(sshTemplate -> { + MultiKey multiKey = new MultiKey(entry.getKey(), + scenarioStart.toString(), + scenarioEnd.toString(), + sshTemplate.getSshSetting().getHostname() + ":" + sshTemplate.getSshSetting().getPort(), + sshTemplate.getSshSetting().getLogPath()); + try { + String sshLog = sshTemplate.copyLog(scenarioStart, scenarioEnd).getLogContents(); + storeInExtension(multiKey, sshLog); + } catch (Exception e) { + storeInExtension(multiKey, ExceptionUtils.getStackTrace(e)); + } + }) + ); + } + + private Map> getSshTemplatesForType(Class clazz) { + Map> sshTemplates = new HashMap<>(); + String[] beanNames = beanFactory.getBeanNamesForType(clazz); + for (String beanName : beanNames) { + BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName); + if (bd instanceof RootBeanDefinition) { + Qualifier qualifier = ((RootBeanDefinition) bd).getResolvedFactoryMethod().getAnnotation(Qualifier.class); + if (sshTemplates.get(qualifier.value()) == null) { + sshTemplates.put(qualifier.value(), new ArrayList<>()); + } + sshTemplates.get(qualifier.value()).add((T) beanFactory.getBean(beanName)); + } + } + return sshTemplates; + } + + private Map> getSshTemplates() { + Map> sshTemplatesArray = getSshTemplatesForType(SshTemplate[].class); + Map> sshTemplates = getSshTemplatesForType(SshTemplate.class); + + //merge both maps together to simple Map> + sshTemplatesArray.entrySet() + .stream() + .forEach(entry -> { + List flattenedSshTemplatesArray = entry.getValue() + .stream() + .flatMap(Arrays::stream) + .collect(Collectors.toList()); + if (sshTemplates.get(entry.getKey()) != null) { + sshTemplates.get(entry.getKey()).addAll(flattenedSshTemplatesArray); + } else { + sshTemplates.put(entry.getKey(), flattenedSshTemplatesArray); + } + }); + + return sshTemplates; + } + + private Map> getReportableSshTemplates(Map> sshTemplates) { + Map> reportableSshTemplates = new HashMap<>(); + sshTemplates.entrySet().stream().forEach(entry -> { + List result = entry.getValue().stream().filter(template -> template.isReportable()).collect(Collectors.toList()); + if (!result.isEmpty()) { + reportableSshTemplates.put(entry.getKey(), result); + } + }); + return reportableSshTemplates; + } } diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java new file mode 100644 index 00000000..791f7c17 --- /dev/null +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java @@ -0,0 +1,10 @@ +package org.jbehavesupport.core.ssh; + +/** + *Modes for ServerLogXmlReporterExtension + */ +public enum SshReportType { + FULL, + TEMPLATE, + CACHE +} diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java index ed25f217..f68844f6 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java @@ -79,4 +79,9 @@ public void logContainsData(String systemQualifier, String startTimeAlias, Strin public void dataNotInLog(String systemQualifier, String stringTable) { sshHandler.checkLogDataPresence(systemQualifier, stringTable, notContainsVerifier); } + + @Given("ssh reporter mode is set to [$mode]") + public void setSshReporterMode(ExpressionEvaluatingParameter mode) { + sshHandler.setSshReporterMode( mode); + } } diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java index 9f9d984a..806f2ff1 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java @@ -20,6 +20,7 @@ import java.time.format.DateTimeFormatter; import java.util.List; +import lombok.Getter; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.connection.channel.direct.Session; import net.schmizz.sshj.transport.verification.PromiscuousVerifier; @@ -31,6 +32,8 @@ public class SshTemplate { private final String timestampFormat; private final String getLogBetweenTimestampsCommand; private final RollingLogResolver rollingLogResolver; + @Getter + private final boolean reportable; private SSHClient sshClient; private abstract static class Commands { @@ -43,12 +46,17 @@ private abstract static class Constants { } public SshTemplate(SshSetting sshSetting, String timestampFormat, RollingLogResolver rollingLogResolver) { + this(sshSetting, timestampFormat, rollingLogResolver, false); + } + + public SshTemplate(SshSetting sshSetting, String timestampFormat, RollingLogResolver rollingLogResolver, boolean reportable) { isTrue(!isEmpty(sshSetting.getLogPath()), "log path must not be null or empty"); isTrue(!isEmpty(timestampFormat), "timestamp format must not be null or empty"); this.sshSetting = sshSetting; this.timestampFormat = timestampFormat; this.rollingLogResolver = rollingLogResolver; + this.reportable = reportable; try { InputStream logCommandStream = new ClassPathResource("get-log-between-timestamps-template.awk").getInputStream(); this.getLogBetweenTimestampsCommand = copyToString(logCommandStream, defaultCharset()).trim(); diff --git a/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt b/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt index 5cc7c925..4c24919e 100644 --- a/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt +++ b/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt @@ -15,7 +15,12 @@ Collapse
- + + + + + No logs available +

@@ -26,7 +31,7 @@
- + + +
@@ -41,36 +46,58 @@ -
- - - - - - - - - - +
Start log: - -
End log: - -
+ + + + + + + + + + + + - -
Start log: + +
End log: + +
Host:
Log path:
+ + + + + + + +
+

+

+                        
+                    
+

+
+ + + + + + + + + + - -
+ + - + + + + + + +

+ +
+ Show/hide + stacktrace + +

+
+
+ + \ No newline at end of file diff --git a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SampleStoriesIT.java b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SampleStoriesIT.java index 2c050e0f..c5a63f2d 100644 --- a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SampleStoriesIT.java +++ b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SampleStoriesIT.java @@ -25,6 +25,7 @@ protected List storyPaths() { "org/jbehavesupport/test/sample/WebAction.story", "org/jbehavesupport/test/sample/WebNavigation.story", "org/jbehavesupport/test/sample/Ssh.story", + "org/jbehavesupport/test/sample/SshReport.story", "org/jbehavesupport/test/sample/HealthCheck.story", "org/jbehavesupport/test/sample/Jms.story", "org/jbehavesupport/test/sample/Verification.story" diff --git a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SshReport.story b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SshReport.story new file mode 100644 index 00000000..b41af6b7 --- /dev/null +++ b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SshReport.story @@ -0,0 +1,32 @@ +Narrative: +This scenario is sample story for ServerLogXmlReporterExteson +In output we can se: +2X LONG_REPORTABLE (file) created from TEMPLATE and FULL mode +2X TEST created from CACHE and FULL mode + +Scenario: Test TEMPLATE mode of ssh extension + +Given ssh reporter mode is set to [TEMPLATE] + +Then the following data are present in [TEST] log: +| header | +| long | +| unexpected | + +Scenario: Test CACHE mode of ssh extension + +Given ssh reporter mode is set to [CACHE] + +Then the following data are present in [TEST] log: +| header | +| long | +| unexpected | + +Scenario: Test FULL mode of ssh extension + +Given ssh reporter mode is set to [FULL] + +Then the following data are present in [TEST] log: +| header | +| long | +| unexpected | diff --git a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/support/TestConfig.java b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/support/TestConfig.java index d6999aa6..443992b7 100644 --- a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/support/TestConfig.java +++ b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/support/TestConfig.java @@ -3,8 +3,12 @@ import static java.lang.Integer.parseInt; import static java.util.Collections.singletonMap; import static org.jbehavesupport.core.ssh.SshSetting.builder; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.IOException; +import java.time.ZonedDateTime; import javax.jms.ConnectionFactory; import javax.sql.DataSource; @@ -12,6 +16,7 @@ import lombok.RequiredArgsConstructor; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.commons.dbcp2.BasicDataSource; +import org.apache.commons.lang3.RandomStringUtils; import org.jbehave.core.model.ExamplesTable; import org.jbehavesupport.core.TestContext; import org.jbehavesupport.core.healthcheck.HealthCheck; @@ -30,6 +35,7 @@ import org.jbehavesupport.core.rest.RestTemplateConfigurer; import org.jbehavesupport.core.ssh.RollingLogResolver; import org.jbehavesupport.core.ssh.SimpleRollingLogResolver; +import org.jbehavesupport.core.ssh.SshLog; import org.jbehavesupport.core.ssh.SshSetting; import org.jbehavesupport.core.ssh.SshTemplate; import org.jbehavesupport.core.support.YamlPropertiesConfigurer; @@ -114,8 +120,8 @@ public TestContextXmlReporterExtension testContextXmlReporterExtension(TestConte } @Bean - public ServerLogXmlReporterExtension serverLogXmlReporterExtension(ConfigurableListableBeanFactory beanFactory) { - return new ServerLogXmlReporterExtension(beanFactory); + public ServerLogXmlReporterExtension serverLogXmlReporterExtension(ConfigurableListableBeanFactory beanFactory, TestContext testContext, FileNameResolver fileNameResolver) { + return new ServerLogXmlReporterExtension(testContext, fileNameResolver); } @Bean @@ -195,6 +201,24 @@ public SshTemplate[] sshTemplate() throws IOException { return new SshTemplate[]{passwordTemplate, keyTemplate}; } + @Bean + @Qualifier("LONG_REPORTABLE") + SshTemplate mockSshTemplate() throws IOException { + SshSetting sshSetting = builder() + .hostname("fake hostname") + .user("fake user") + .password("asdf5684Daa") + .port(0) + .logPath("fake/log/api.log") + .build(); + SshLog sshLog = new SshLog("Random really long string: " + RandomStringUtils.random(10001, true, true), sshSetting); + SshTemplate sshTemplate = mock(SshTemplate.class); + when(sshTemplate.copyLog(any(ZonedDateTime.class), any(ZonedDateTime.class))).thenReturn(sshLog); + when(sshTemplate.isReportable()).thenReturn(true); + when(sshTemplate.getSshSetting()).thenReturn(sshSetting); + return sshTemplate; + } + @Bean @Qualifier("TEST") HealthCheck realHealthCheck() { From 91f98fc90faa58d55ad25df2318521fcc90e58cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Smr=C4=8Dek?= <50624143+xsmrcek@users.noreply.github.com> Date: Wed, 20 Nov 2019 09:50:21 +0100 Subject: [PATCH 02/10] Update jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java Co-Authored-By: Pavel Jandejsek <43804753+paveljandejsek@users.noreply.github.com> --- .../main/java/org/jbehavesupport/core/ssh/SshReportType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java index 791f7c17..c1b6c100 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java @@ -1,7 +1,7 @@ package org.jbehavesupport.core.ssh; /** - *Modes for ServerLogXmlReporterExtension + * Modes for ServerLogXmlReporterExtension */ public enum SshReportType { FULL, From b9b262b644776dc8ca34fd02ab0517cff6429da9 Mon Sep 17 00:00:00 2001 From: Daniel Smrcek Date: Wed, 20 Nov 2019 09:50:28 +0100 Subject: [PATCH 03/10] Update jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java Co-Authored-By: Pavel Jandejsek <43804753+paveljandejsek@users.noreply.github.com> --- jbehave-support-core/docs/Reporting.md | 7 + jbehave-support-core/docs/Ssh.md | 7 - .../core/report/ReportSteps.java | 22 +++ .../ServerLogXmlReporterExtension.java | 102 +++++++++++-- .../jbehavesupport/core/ssh/SshHandler.java | 137 ++---------------- .../core/ssh/SshReportType.java | 3 + .../org/jbehavesupport/core/ssh/SshSteps.java | 5 - .../jbehavesupport/core/ssh/SshTemplate.java | 5 + .../report-generator/serverLogXmlRep.xslt | 28 ++-- .../test/support/TestConfig.java | 5 +- 10 files changed, 162 insertions(+), 159 deletions(-) create mode 100644 jbehave-support-core/src/main/java/org/jbehavesupport/core/report/ReportSteps.java diff --git a/jbehave-support-core/docs/Reporting.md b/jbehave-support-core/docs/Reporting.md index 2934d81d..bcd1aa21 100644 --- a/jbehave-support-core/docs/Reporting.md +++ b/jbehave-support-core/docs/Reporting.md @@ -58,4 +58,11 @@ public WsXmlReporterExtension wsXmlReporterExtension() { } ``` +#### Report steps +Following step allows setting specific server log report extension mode +Step throws AssertionError when ServerLogXmlReporterExtension isn't registered. +``` +Given ssh reporter mode is set to [TEMPLATE] +``` + --- diff --git a/jbehave-support-core/docs/Ssh.md b/jbehave-support-core/docs/Ssh.md index af3e29d6..c4874f78 100644 --- a/jbehave-support-core/docs/Ssh.md +++ b/jbehave-support-core/docs/Ssh.md @@ -72,10 +72,3 @@ Given log start timestamp is set to [NCAKO_1] Given log end timestamp is set to [NCAKO_2] ``` Afterwards the same principle of searching the logs as described above applies - -#### Ssh reporter mode -Following step allows setting specific server log report extension mode -``` -Given ssh reporter mode is set to [TEMPLATE] -``` - diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/ReportSteps.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/ReportSteps.java new file mode 100644 index 00000000..1a6a0107 --- /dev/null +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/ReportSteps.java @@ -0,0 +1,22 @@ +package org.jbehavesupport.core.report; + +import lombok.extern.slf4j.Slf4j; +import org.jbehave.core.annotations.Given; +import org.jbehavesupport.core.expression.ExpressionEvaluatingParameter; +import org.jbehavesupport.core.report.extension.ServerLogXmlReporterExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import static org.junit.Assert.assertNotNull; + +@Slf4j +@Component +public class ReportSteps { + + @Autowired(required = false) + private ServerLogXmlReporterExtension serverLogXmlReporterExtension; + + @Given("ssh reporter mode is set to [$mode]") + public void setSshReporterMode(ExpressionEvaluatingParameter mode) { + assertNotNull("serverLogXmlReporterExtension is not registered", serverLogXmlReporterExtension); + } +} diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java index 114571b3..be7ebf19 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java @@ -5,11 +5,13 @@ import java.io.Writer; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.Date; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; - -import lombok.Getter; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.Setter; import org.apache.commons.collections4.keyvalue.MultiKey; @@ -17,18 +19,29 @@ import org.apache.commons.collections4.map.MultiKeyMap; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.io.FileUtils; +import org.jbehave.core.annotations.AfterScenario; +import org.jbehave.core.annotations.BeforeScenario; +import org.jbehave.core.annotations.ScenarioType; import org.jbehavesupport.core.TestContext; import org.jbehavesupport.core.report.ReportContext; import org.jbehavesupport.core.internal.FileNameResolver; import org.jbehavesupport.core.report.ReportRenderingPhase; +import org.jbehavesupport.core.ssh.SshHandler; import org.jbehavesupport.core.ssh.SshReportType; +import org.jbehavesupport.core.ssh.SshTemplate; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; @RequiredArgsConstructor public class ServerLogXmlReporterExtension extends AbstractXmlReporterExtension { private final TestContext testContext; private final FileNameResolver fileNameResolver; + private final SshHandler sshHandler; + private final ConfigurableListableBeanFactory beanFactory; private static final String SSH_XML_REPORTER_EXTENSION = "serverLog"; private static final String LOG = "log"; @@ -38,27 +51,59 @@ public class ServerLogXmlReporterExtension extends AbstractXmlReporterExtension private static final String FAIL = "fail"; private static final String FILE_NAME_PATTERN = "LOG_%s_%s.txt"; - private MultiKeyMap logContents = MultiKeyMap.multiKeyMap(new LRUMap()); + private MultiKeyMap logContents = MultiKeyMap.multiKeyMap(new LRUMap()); @Value("${ssh.reporting.maxLogLength:10000}") private Long maxLength; - @Getter @Setter - private SshReportType sshReportType; + private SshReportType sshReportMode; @Value("${ssh.reporting.logOnFailure:false}") - @Getter + private boolean loggingOnFailure; @Value("${web.reporting.directory:./target/reports}") private String logFileDirectory; + @Value("${ssh.reporting.mode:CACHE}") + SshReportType defaultReportType; + + @BeforeScenario + public void init() { + sshReportMode = defaultReportType; + } + + @AfterScenario + public void after() { + if (sshReportMode == SshReportType.CACHE) { + sshHandler.getLogCache().entrySet().forEach(entry -> { + MultiKey newKey = new MultiKey(entry.getKey().getKey(0), entry.getKey().getKey(1), entry.getKey().getKey(2), "N/A", "N/A"); + registerLogContent(newKey, (entry.getValue())); + }); + } else if (sshReportMode == SshReportType.FULL) { + registerLogContent(sshHandler.getTemplateLogs(getSshTemplates())); + } else if (sshReportMode == SshReportType.TEMPLATE) { + registerLogContent(sshHandler.getTemplateLogs(getReportableSshTemplates(getSshTemplates()))); + } + } + + @AfterScenario(uponType = ScenarioType.ANY, uponOutcome = AfterScenario.Outcome.FAILURE) + public void afterFailedScenario() { + if (sshReportMode != SshReportType.TEMPLATE && loggingOnFailure) { + registerLogContent(sshHandler.getTemplateLogs(getReportableSshTemplates(getSshTemplates()))); + } + } + @Override public ReportRenderingPhase getReportRenderingPhase() { return ReportRenderingPhase.AFTER_STORY; } + public void registerLogContent(MultiKeyMap logContents) { + logContents.entrySet().forEach(entry -> registerLogContent(entry.getKey(), entry.getValue())); + } + public void registerLogContent(MultiKey multiKey, String logContent) { if (logContents.containsKey(multiKey)) { logContent += logContents.get(multiKey); @@ -84,7 +129,7 @@ public void print(final Writer writer, final ReportContext reportContext) { } private void printContent(final Writer writer) { - logContents.entrySet().stream().forEach( + logContents.entrySet().forEach( entry -> { final MultiKey key = entry.getKey(); printBegin(writer, SYSTEM, getSshQualifierAttributes(key.getKey(0).toString())); @@ -107,7 +152,7 @@ private void printLogContent(Writer writer, String qualifier, String sshLog) { } private void printLogFile(final Writer writer, String logContent, String qualifier) { - File logFile = new File(new Date().getTime() + ".txt"); + File logFile = new File(LocalTime.now() + ".txt"); try { prepareDirectory(); FileUtils.writeStringToFile(logFile, logContent, (String) null); @@ -145,4 +190,43 @@ private Map getSshAttributesFromKey(MultiKey key) { sshContextAttributes.put("logPath", key.getKey(4).toString()); return sshContextAttributes; } + + private Map> getSshTemplatesForType(Class clazz) { + Map> sshTemplates = new HashMap<>(); + String[] beanNames = beanFactory.getBeanNamesForType(clazz); + for (String beanName : beanNames) { + BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName); + if (bd instanceof RootBeanDefinition) { + Qualifier qualifier = ((RootBeanDefinition) bd).getResolvedFactoryMethod().getAnnotation(Qualifier.class); + if (sshTemplates.get(qualifier.value()) == null) { + sshTemplates.put(qualifier.value(), new ArrayList<>()); + } + sshTemplates.get(qualifier.value()).add((T) beanFactory.getBean(beanName)); + } + } + return sshTemplates; + } + + private Map> getSshTemplates() { + Map> sshTemplatesArray = getSshTemplatesForType(SshTemplate[].class); + Map> sshTemplates = getSshTemplatesForType(SshTemplate.class); + + //merge both maps together to simple Map> + sshTemplatesArray.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream().flatMap(Arrays::stream).collect(Collectors.toList()))) + .forEach((k, v) -> sshTemplates.merge(k, v, (v1, v2) -> { + v2.addAll(v1); + return v2; + })); + + return sshTemplates; + } + + private Map> getReportableSshTemplates(Map> sshTemplates) { + return sshTemplates.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, + e -> e.getValue().stream() + .filter(SshTemplate::isReportable) + .collect(Collectors.toList()))); + } } diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java index 3ca780e1..40114c46 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java @@ -1,5 +1,6 @@ package org.jbehavesupport.core.ssh; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.keyvalue.MultiKey; @@ -7,34 +8,25 @@ import org.apache.commons.collections4.map.MultiKeyMap; import org.apache.commons.lang3.exception.ExceptionUtils; import org.assertj.core.api.SoftAssertions; -import org.jbehave.core.annotations.AfterScenario; import org.jbehave.core.annotations.BeforeScenario; -import org.jbehave.core.annotations.ScenarioType; import org.jbehave.core.model.ExamplesTable; import org.jbehavesupport.core.TestContext; import org.jbehavesupport.core.expression.ExpressionEvaluatingParameter; import org.jbehavesupport.core.internal.parameterconverters.ExamplesEvaluationTableConverter; -import org.jbehavesupport.core.report.extension.ServerLogXmlReporterExtension; import org.jbehavesupport.core.verification.Verifier; import org.jbehavesupport.core.verification.VerifierResolver; import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; import java.io.IOException; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.jbehavesupport.core.internal.ExampleTableConstraints.VERIFIER; @@ -74,62 +66,22 @@ public class SshHandler { private final ExamplesEvaluationTableConverter tableConverter; private final VerifierResolver verifierResolver; - @Autowired(required = false) - ServerLogXmlReporterExtension serverLogXmlReporterExtension; - - @Value("${ssh.reporting.mode:CACHE}") - SshReportType defaultReportType; - @Value("${ssh.max.assert.count:10}") private int maxSoftAssertCount; private ZonedDateTime scenarioStart; - private ZonedDateTime scenarioEnd; private ZonedDateTime logStartTime; private ZonedDateTime logEndTime; + @Getter private MultiKeyMap logCache = MultiKeyMap.multiKeyMap(new LRUMap()); @BeforeScenario public void init() { scenarioStart = ZonedDateTime.now(); - scenarioEnd = null; logStartTime = null; logEndTime = null; logCache.clear(); - if (serverLogXmlReporterExtension != null) { - serverLogXmlReporterExtension.setSshReportType(defaultReportType); - } - } - - @AfterScenario - public void after() { - scenarioEnd = ZonedDateTime.now(); - if (serverLogXmlReporterExtension != null) { - if (serverLogXmlReporterExtension.getSshReportType() == SshReportType.CACHE) { - logCache.entrySet().stream().forEach(entry -> { - MultiKey newKey = new MultiKey(entry.getKey().getKey(0), entry.getKey().getKey(1), entry.getKey().getKey(2), "N/A", "N/A"); - storeInExtension(newKey, (entry.getValue())); - }); - } else if (serverLogXmlReporterExtension.getSshReportType() == SshReportType.FULL) { - storeTemplatesLogs(getSshTemplates()); - } else if (serverLogXmlReporterExtension.getSshReportType() == SshReportType.TEMPLATE) { - storeTemplatesLogs(getReportableSshTemplates(getSshTemplates())); - } - } - } - - @AfterScenario(uponType = ScenarioType.ANY, uponOutcome = AfterScenario.Outcome.FAILURE) - public void afterFailedScenario() { - if (serverLogXmlReporterExtension != null) { - if (serverLogXmlReporterExtension.getSshReportType() != SshReportType.TEMPLATE && - serverLogXmlReporterExtension.isLoggingOnFailure()) { - if (scenarioEnd == null) { - scenarioEnd = ZonedDateTime.now(); - } - storeTemplatesLogs(getReportableSshTemplates(getSshTemplates())); - } - } } public void markLogTime(String logTimeAlias) { @@ -156,18 +108,11 @@ public void checkLogDataPresence(String systemQualifier, String stringTable, Ver checkDataPresence(systemQualifier, scenarioStart, stringTable, verifier); } - public void setSshReporterMode(ExpressionEvaluatingParameter mode) { - if (serverLogXmlReporterExtension != null) { - serverLogXmlReporterExtension.setSshReportType(SshReportType.valueOf(mode.getValue())); - } else { - log.warn("ServerLogXmlReportExtension is not registered, no logs will be present in report."); - } - } - /** * @deprecated use logContainsData(String systemQualifier, String stringTable) instead * If you set timestamps via separate steps, log reading is more accurate and use cache */ + @Deprecated public void checkLogDataPresence(String systemQualifier, String startTimeAlias, String stringTable, Verifier verifier) { checkDataPresence(systemQualifier, testContext.get(startTimeAlias), stringTable, verifier); } @@ -275,78 +220,24 @@ protected String getSearchColumnName(ExamplesTable searchData) { } } - private void storeInExtension(MultiKey key, String logContent) { - if (serverLogXmlReporterExtension != null) { - serverLogXmlReporterExtension.registerLogContent(key, logContent); - } else { - log.warn("ServerLogXmlReportExtension is not registered, no logs will be present in report."); - } - } - - private void storeTemplatesLogs(Map> sshTemplates) { - sshTemplates.entrySet().stream().forEach(entry -> - entry.getValue().stream().forEach(sshTemplate -> { - MultiKey multiKey = new MultiKey(entry.getKey(), - scenarioStart.toString(), - scenarioEnd.toString(), + public MultiKeyMap getTemplateLogs(Map> sshTemplates){ + ZonedDateTime scenarioEnd = ZonedDateTime.now(); + MultiKeyMap templateLogs = MultiKeyMap.multiKeyMap(new LRUMap()); + sshTemplates.entrySet().forEach(entry -> + entry.getValue().forEach(sshTemplate -> { + MultiKey multiKey = new MultiKey(entry.getKey(), + scenarioStart,//.toString(), + scenarioEnd,//.toString(), sshTemplate.getSshSetting().getHostname() + ":" + sshTemplate.getSshSetting().getPort(), sshTemplate.getSshSetting().getLogPath()); try { String sshLog = sshTemplate.copyLog(scenarioStart, scenarioEnd).getLogContents(); - storeInExtension(multiKey, sshLog); + templateLogs.put(multiKey, sshLog); } catch (Exception e) { - storeInExtension(multiKey, ExceptionUtils.getStackTrace(e)); + templateLogs.put(multiKey, ExceptionUtils.getStackTrace(e)); } }) ); - } - - private Map> getSshTemplatesForType(Class clazz) { - Map> sshTemplates = new HashMap<>(); - String[] beanNames = beanFactory.getBeanNamesForType(clazz); - for (String beanName : beanNames) { - BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName); - if (bd instanceof RootBeanDefinition) { - Qualifier qualifier = ((RootBeanDefinition) bd).getResolvedFactoryMethod().getAnnotation(Qualifier.class); - if (sshTemplates.get(qualifier.value()) == null) { - sshTemplates.put(qualifier.value(), new ArrayList<>()); - } - sshTemplates.get(qualifier.value()).add((T) beanFactory.getBean(beanName)); - } - } - return sshTemplates; - } - - private Map> getSshTemplates() { - Map> sshTemplatesArray = getSshTemplatesForType(SshTemplate[].class); - Map> sshTemplates = getSshTemplatesForType(SshTemplate.class); - - //merge both maps together to simple Map> - sshTemplatesArray.entrySet() - .stream() - .forEach(entry -> { - List flattenedSshTemplatesArray = entry.getValue() - .stream() - .flatMap(Arrays::stream) - .collect(Collectors.toList()); - if (sshTemplates.get(entry.getKey()) != null) { - sshTemplates.get(entry.getKey()).addAll(flattenedSshTemplatesArray); - } else { - sshTemplates.put(entry.getKey(), flattenedSshTemplatesArray); - } - }); - - return sshTemplates; - } - - private Map> getReportableSshTemplates(Map> sshTemplates) { - Map> reportableSshTemplates = new HashMap<>(); - sshTemplates.entrySet().stream().forEach(entry -> { - List result = entry.getValue().stream().filter(template -> template.isReportable()).collect(Collectors.toList()); - if (!result.isEmpty()) { - reportableSshTemplates.put(entry.getKey(), result); - } - }); - return reportableSshTemplates; + return templateLogs; } } diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java index c1b6c100..ff773d6c 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java @@ -2,6 +2,9 @@ /** * Modes for ServerLogXmlReporterExtension + * FULL for full log content from all sshTemplates + * TEMPLATE for full log content from reportable sshTemplates + * CACHE from all log parts saved in case during scenario run */ public enum SshReportType { FULL, diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java index f68844f6..ed25f217 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java @@ -79,9 +79,4 @@ public void logContainsData(String systemQualifier, String startTimeAlias, Strin public void dataNotInLog(String systemQualifier, String stringTable) { sshHandler.checkLogDataPresence(systemQualifier, stringTable, notContainsVerifier); } - - @Given("ssh reporter mode is set to [$mode]") - public void setSshReporterMode(ExpressionEvaluatingParameter mode) { - sshHandler.setSshReporterMode( mode); - } } diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java index 806f2ff1..8efc44a9 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java @@ -45,6 +45,11 @@ private abstract static class Constants { static final String CMD_DELIMITER = "; "; } + /** + * @deprecated + * use {@link #SshTemplate(SshSetting, String, RollingLogResolver, boolean)}instead + */ + @Deprecated public SshTemplate(SshSetting sshSetting, String timestampFormat, RollingLogResolver rollingLogResolver) { this(sshSetting, timestampFormat, rollingLogResolver, false); } diff --git a/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt b/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt index 4c24919e..73c64174 100644 --- a/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt +++ b/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt @@ -79,9 +79,15 @@ - - - + + + + + + + + +

@@ -94,10 +100,8 @@ + - - -

Show/hide log @@ -112,10 +116,8 @@ + - - - - + + + + - - -

- + + + + + No logs available +

@@ -26,7 +31,7 @@
- + + +
@@ -41,36 +46,58 @@ -
- - - - - - - - - - +
Start log: - -
End log: - -
+ + + + + + + + + + + + - -
Start log: + +
End log: + +
Host:
Log path:
+ + + + + + + +
+

+

+                        
+                    
+

+
+ + + + + + + + + + - -
+ + - + + + + + + +

+ +
+ Show/hide + stacktrace + +

+
+
+ + \ No newline at end of file diff --git a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SampleStoriesIT.java b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SampleStoriesIT.java index 2c050e0f..c5a63f2d 100644 --- a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SampleStoriesIT.java +++ b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SampleStoriesIT.java @@ -25,6 +25,7 @@ protected List storyPaths() { "org/jbehavesupport/test/sample/WebAction.story", "org/jbehavesupport/test/sample/WebNavigation.story", "org/jbehavesupport/test/sample/Ssh.story", + "org/jbehavesupport/test/sample/SshReport.story", "org/jbehavesupport/test/sample/HealthCheck.story", "org/jbehavesupport/test/sample/Jms.story", "org/jbehavesupport/test/sample/Verification.story" diff --git a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SshReport.story b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SshReport.story new file mode 100644 index 00000000..b41af6b7 --- /dev/null +++ b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/sample/SshReport.story @@ -0,0 +1,32 @@ +Narrative: +This scenario is sample story for ServerLogXmlReporterExteson +In output we can se: +2X LONG_REPORTABLE (file) created from TEMPLATE and FULL mode +2X TEST created from CACHE and FULL mode + +Scenario: Test TEMPLATE mode of ssh extension + +Given ssh reporter mode is set to [TEMPLATE] + +Then the following data are present in [TEST] log: +| header | +| long | +| unexpected | + +Scenario: Test CACHE mode of ssh extension + +Given ssh reporter mode is set to [CACHE] + +Then the following data are present in [TEST] log: +| header | +| long | +| unexpected | + +Scenario: Test FULL mode of ssh extension + +Given ssh reporter mode is set to [FULL] + +Then the following data are present in [TEST] log: +| header | +| long | +| unexpected | diff --git a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/support/TestConfig.java b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/support/TestConfig.java index 00a0dffc..c978e0e8 100644 --- a/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/support/TestConfig.java +++ b/jbehave-support-core/src/test/groovy/org/jbehavesupport/test/support/TestConfig.java @@ -3,10 +3,14 @@ import static java.lang.Integer.parseInt; import static java.util.Collections.singletonMap; import static org.jbehavesupport.core.ssh.SshSetting.builder; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.time.ZonedDateTime; import javax.jms.ConnectionFactory; import javax.sql.DataSource; @@ -15,6 +19,7 @@ import lombok.SneakyThrows; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.commons.dbcp2.BasicDataSource; +import org.apache.commons.lang3.RandomStringUtils; import org.jbehave.core.model.ExamplesTable; import org.jbehavesupport.core.TestContext; import org.jbehavesupport.core.healthcheck.HealthCheck; @@ -33,6 +38,7 @@ import org.jbehavesupport.core.rest.RestTemplateConfigurer; import org.jbehavesupport.core.ssh.RollingLogResolver; import org.jbehavesupport.core.ssh.SimpleRollingLogResolver; +import org.jbehavesupport.core.ssh.SshLog; import org.jbehavesupport.core.ssh.SshSetting; import org.jbehavesupport.core.ssh.SshTemplate; import org.jbehavesupport.core.support.YamlPropertiesConfigurer; @@ -58,12 +64,12 @@ @RequiredArgsConstructor public class TestConfig { - - private final Environment env; public static final String FIREFOX_BROWSERSTACK = "firefox-browserstack"; public static final String SAFARI_BROWSERSTACK = "safari-browserstack"; public static final String CHROME_BROWSERSTACK = "chrome-browserstack"; + private final Environment env; + final ResourceLoader resourceLoader; @Bean @@ -124,8 +130,8 @@ public TestContextXmlReporterExtension testContextXmlReporterExtension(TestConte } @Bean - public ServerLogXmlReporterExtension serverLogXmlReporterExtension(ConfigurableListableBeanFactory beanFactory) { - return new ServerLogXmlReporterExtension(beanFactory); + public ServerLogXmlReporterExtension serverLogXmlReporterExtension(ConfigurableListableBeanFactory beanFactory, TestContext testContext, FileNameResolver fileNameResolver) { + return new ServerLogXmlReporterExtension(testContext, fileNameResolver); } @Bean @@ -205,6 +211,24 @@ public SshTemplate[] sshTemplate() throws IOException { return new SshTemplate[]{passwordTemplate, keyTemplate}; } + @Bean + @Qualifier("LONG_REPORTABLE") + SshTemplate mockSshTemplate() throws IOException { + SshSetting sshSetting = builder() + .hostname("fake hostname") + .user("fake user") + .password("asdf5684Daa") + .port(0) + .logPath("fake/log/api.log") + .build(); + SshLog sshLog = new SshLog("Random really long string: " + RandomStringUtils.random(10001, true, true), sshSetting); + SshTemplate sshTemplate = mock(SshTemplate.class); + when(sshTemplate.copyLog(any(ZonedDateTime.class), any(ZonedDateTime.class))).thenReturn(sshLog); + when(sshTemplate.isReportable()).thenReturn(true); + when(sshTemplate.getSshSetting()).thenReturn(sshSetting); + return sshTemplate; + } + @Bean @Qualifier("TEST") HealthCheck realHealthCheck() { From 0e6f400cddc40740e73475b5ad953e2bdd453672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Smr=C4=8Dek?= <50624143+xsmrcek@users.noreply.github.com> Date: Wed, 20 Nov 2019 09:50:21 +0100 Subject: [PATCH 07/10] Update jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java Co-Authored-By: Pavel Jandejsek <43804753+paveljandejsek@users.noreply.github.com> --- .../main/java/org/jbehavesupport/core/ssh/SshReportType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java index 791f7c17..c1b6c100 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java @@ -1,7 +1,7 @@ package org.jbehavesupport.core.ssh; /** - *Modes for ServerLogXmlReporterExtension + * Modes for ServerLogXmlReporterExtension */ public enum SshReportType { FULL, From 0bc4ef4c89587ebf359069cd9850ad0dbedee4d2 Mon Sep 17 00:00:00 2001 From: Daniel Smrcek Date: Wed, 20 Nov 2019 09:50:28 +0100 Subject: [PATCH 08/10] Update jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java Co-Authored-By: Pavel Jandejsek <43804753+paveljandejsek@users.noreply.github.com> --- jbehave-support-core/docs/Reporting.md | 7 + jbehave-support-core/docs/Ssh.md | 7 - .../core/report/ReportSteps.java | 22 +++ .../ServerLogXmlReporterExtension.java | 102 +++++++++++-- .../jbehavesupport/core/ssh/SshHandler.java | 137 ++---------------- .../core/ssh/SshReportType.java | 3 + .../org/jbehavesupport/core/ssh/SshSteps.java | 5 - .../jbehavesupport/core/ssh/SshTemplate.java | 5 + .../report-generator/serverLogXmlRep.xslt | 28 ++-- .../test/support/TestConfig.java | 5 +- 10 files changed, 162 insertions(+), 159 deletions(-) create mode 100644 jbehave-support-core/src/main/java/org/jbehavesupport/core/report/ReportSteps.java diff --git a/jbehave-support-core/docs/Reporting.md b/jbehave-support-core/docs/Reporting.md index 2934d81d..bcd1aa21 100644 --- a/jbehave-support-core/docs/Reporting.md +++ b/jbehave-support-core/docs/Reporting.md @@ -58,4 +58,11 @@ public WsXmlReporterExtension wsXmlReporterExtension() { } ``` +#### Report steps +Following step allows setting specific server log report extension mode +Step throws AssertionError when ServerLogXmlReporterExtension isn't registered. +``` +Given ssh reporter mode is set to [TEMPLATE] +``` + --- diff --git a/jbehave-support-core/docs/Ssh.md b/jbehave-support-core/docs/Ssh.md index af3e29d6..c4874f78 100644 --- a/jbehave-support-core/docs/Ssh.md +++ b/jbehave-support-core/docs/Ssh.md @@ -72,10 +72,3 @@ Given log start timestamp is set to [NCAKO_1] Given log end timestamp is set to [NCAKO_2] ``` Afterwards the same principle of searching the logs as described above applies - -#### Ssh reporter mode -Following step allows setting specific server log report extension mode -``` -Given ssh reporter mode is set to [TEMPLATE] -``` - diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/ReportSteps.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/ReportSteps.java new file mode 100644 index 00000000..1a6a0107 --- /dev/null +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/ReportSteps.java @@ -0,0 +1,22 @@ +package org.jbehavesupport.core.report; + +import lombok.extern.slf4j.Slf4j; +import org.jbehave.core.annotations.Given; +import org.jbehavesupport.core.expression.ExpressionEvaluatingParameter; +import org.jbehavesupport.core.report.extension.ServerLogXmlReporterExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import static org.junit.Assert.assertNotNull; + +@Slf4j +@Component +public class ReportSteps { + + @Autowired(required = false) + private ServerLogXmlReporterExtension serverLogXmlReporterExtension; + + @Given("ssh reporter mode is set to [$mode]") + public void setSshReporterMode(ExpressionEvaluatingParameter mode) { + assertNotNull("serverLogXmlReporterExtension is not registered", serverLogXmlReporterExtension); + } +} diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java index 114571b3..be7ebf19 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/report/extension/ServerLogXmlReporterExtension.java @@ -5,11 +5,13 @@ import java.io.Writer; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.Date; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; - -import lombok.Getter; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.Setter; import org.apache.commons.collections4.keyvalue.MultiKey; @@ -17,18 +19,29 @@ import org.apache.commons.collections4.map.MultiKeyMap; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.io.FileUtils; +import org.jbehave.core.annotations.AfterScenario; +import org.jbehave.core.annotations.BeforeScenario; +import org.jbehave.core.annotations.ScenarioType; import org.jbehavesupport.core.TestContext; import org.jbehavesupport.core.report.ReportContext; import org.jbehavesupport.core.internal.FileNameResolver; import org.jbehavesupport.core.report.ReportRenderingPhase; +import org.jbehavesupport.core.ssh.SshHandler; import org.jbehavesupport.core.ssh.SshReportType; +import org.jbehavesupport.core.ssh.SshTemplate; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; @RequiredArgsConstructor public class ServerLogXmlReporterExtension extends AbstractXmlReporterExtension { private final TestContext testContext; private final FileNameResolver fileNameResolver; + private final SshHandler sshHandler; + private final ConfigurableListableBeanFactory beanFactory; private static final String SSH_XML_REPORTER_EXTENSION = "serverLog"; private static final String LOG = "log"; @@ -38,27 +51,59 @@ public class ServerLogXmlReporterExtension extends AbstractXmlReporterExtension private static final String FAIL = "fail"; private static final String FILE_NAME_PATTERN = "LOG_%s_%s.txt"; - private MultiKeyMap logContents = MultiKeyMap.multiKeyMap(new LRUMap()); + private MultiKeyMap logContents = MultiKeyMap.multiKeyMap(new LRUMap()); @Value("${ssh.reporting.maxLogLength:10000}") private Long maxLength; - @Getter @Setter - private SshReportType sshReportType; + private SshReportType sshReportMode; @Value("${ssh.reporting.logOnFailure:false}") - @Getter + private boolean loggingOnFailure; @Value("${web.reporting.directory:./target/reports}") private String logFileDirectory; + @Value("${ssh.reporting.mode:CACHE}") + SshReportType defaultReportType; + + @BeforeScenario + public void init() { + sshReportMode = defaultReportType; + } + + @AfterScenario + public void after() { + if (sshReportMode == SshReportType.CACHE) { + sshHandler.getLogCache().entrySet().forEach(entry -> { + MultiKey newKey = new MultiKey(entry.getKey().getKey(0), entry.getKey().getKey(1), entry.getKey().getKey(2), "N/A", "N/A"); + registerLogContent(newKey, (entry.getValue())); + }); + } else if (sshReportMode == SshReportType.FULL) { + registerLogContent(sshHandler.getTemplateLogs(getSshTemplates())); + } else if (sshReportMode == SshReportType.TEMPLATE) { + registerLogContent(sshHandler.getTemplateLogs(getReportableSshTemplates(getSshTemplates()))); + } + } + + @AfterScenario(uponType = ScenarioType.ANY, uponOutcome = AfterScenario.Outcome.FAILURE) + public void afterFailedScenario() { + if (sshReportMode != SshReportType.TEMPLATE && loggingOnFailure) { + registerLogContent(sshHandler.getTemplateLogs(getReportableSshTemplates(getSshTemplates()))); + } + } + @Override public ReportRenderingPhase getReportRenderingPhase() { return ReportRenderingPhase.AFTER_STORY; } + public void registerLogContent(MultiKeyMap logContents) { + logContents.entrySet().forEach(entry -> registerLogContent(entry.getKey(), entry.getValue())); + } + public void registerLogContent(MultiKey multiKey, String logContent) { if (logContents.containsKey(multiKey)) { logContent += logContents.get(multiKey); @@ -84,7 +129,7 @@ public void print(final Writer writer, final ReportContext reportContext) { } private void printContent(final Writer writer) { - logContents.entrySet().stream().forEach( + logContents.entrySet().forEach( entry -> { final MultiKey key = entry.getKey(); printBegin(writer, SYSTEM, getSshQualifierAttributes(key.getKey(0).toString())); @@ -107,7 +152,7 @@ private void printLogContent(Writer writer, String qualifier, String sshLog) { } private void printLogFile(final Writer writer, String logContent, String qualifier) { - File logFile = new File(new Date().getTime() + ".txt"); + File logFile = new File(LocalTime.now() + ".txt"); try { prepareDirectory(); FileUtils.writeStringToFile(logFile, logContent, (String) null); @@ -145,4 +190,43 @@ private Map getSshAttributesFromKey(MultiKey key) { sshContextAttributes.put("logPath", key.getKey(4).toString()); return sshContextAttributes; } + + private Map> getSshTemplatesForType(Class clazz) { + Map> sshTemplates = new HashMap<>(); + String[] beanNames = beanFactory.getBeanNamesForType(clazz); + for (String beanName : beanNames) { + BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName); + if (bd instanceof RootBeanDefinition) { + Qualifier qualifier = ((RootBeanDefinition) bd).getResolvedFactoryMethod().getAnnotation(Qualifier.class); + if (sshTemplates.get(qualifier.value()) == null) { + sshTemplates.put(qualifier.value(), new ArrayList<>()); + } + sshTemplates.get(qualifier.value()).add((T) beanFactory.getBean(beanName)); + } + } + return sshTemplates; + } + + private Map> getSshTemplates() { + Map> sshTemplatesArray = getSshTemplatesForType(SshTemplate[].class); + Map> sshTemplates = getSshTemplatesForType(SshTemplate.class); + + //merge both maps together to simple Map> + sshTemplatesArray.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream().flatMap(Arrays::stream).collect(Collectors.toList()))) + .forEach((k, v) -> sshTemplates.merge(k, v, (v1, v2) -> { + v2.addAll(v1); + return v2; + })); + + return sshTemplates; + } + + private Map> getReportableSshTemplates(Map> sshTemplates) { + return sshTemplates.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, + e -> e.getValue().stream() + .filter(SshTemplate::isReportable) + .collect(Collectors.toList()))); + } } diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java index 3ca780e1..40114c46 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshHandler.java @@ -1,5 +1,6 @@ package org.jbehavesupport.core.ssh; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.keyvalue.MultiKey; @@ -7,34 +8,25 @@ import org.apache.commons.collections4.map.MultiKeyMap; import org.apache.commons.lang3.exception.ExceptionUtils; import org.assertj.core.api.SoftAssertions; -import org.jbehave.core.annotations.AfterScenario; import org.jbehave.core.annotations.BeforeScenario; -import org.jbehave.core.annotations.ScenarioType; import org.jbehave.core.model.ExamplesTable; import org.jbehavesupport.core.TestContext; import org.jbehavesupport.core.expression.ExpressionEvaluatingParameter; import org.jbehavesupport.core.internal.parameterconverters.ExamplesEvaluationTableConverter; -import org.jbehavesupport.core.report.extension.ServerLogXmlReporterExtension; import org.jbehavesupport.core.verification.Verifier; import org.jbehavesupport.core.verification.VerifierResolver; import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; import java.io.IOException; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.jbehavesupport.core.internal.ExampleTableConstraints.VERIFIER; @@ -74,62 +66,22 @@ public class SshHandler { private final ExamplesEvaluationTableConverter tableConverter; private final VerifierResolver verifierResolver; - @Autowired(required = false) - ServerLogXmlReporterExtension serverLogXmlReporterExtension; - - @Value("${ssh.reporting.mode:CACHE}") - SshReportType defaultReportType; - @Value("${ssh.max.assert.count:10}") private int maxSoftAssertCount; private ZonedDateTime scenarioStart; - private ZonedDateTime scenarioEnd; private ZonedDateTime logStartTime; private ZonedDateTime logEndTime; + @Getter private MultiKeyMap logCache = MultiKeyMap.multiKeyMap(new LRUMap()); @BeforeScenario public void init() { scenarioStart = ZonedDateTime.now(); - scenarioEnd = null; logStartTime = null; logEndTime = null; logCache.clear(); - if (serverLogXmlReporterExtension != null) { - serverLogXmlReporterExtension.setSshReportType(defaultReportType); - } - } - - @AfterScenario - public void after() { - scenarioEnd = ZonedDateTime.now(); - if (serverLogXmlReporterExtension != null) { - if (serverLogXmlReporterExtension.getSshReportType() == SshReportType.CACHE) { - logCache.entrySet().stream().forEach(entry -> { - MultiKey newKey = new MultiKey(entry.getKey().getKey(0), entry.getKey().getKey(1), entry.getKey().getKey(2), "N/A", "N/A"); - storeInExtension(newKey, (entry.getValue())); - }); - } else if (serverLogXmlReporterExtension.getSshReportType() == SshReportType.FULL) { - storeTemplatesLogs(getSshTemplates()); - } else if (serverLogXmlReporterExtension.getSshReportType() == SshReportType.TEMPLATE) { - storeTemplatesLogs(getReportableSshTemplates(getSshTemplates())); - } - } - } - - @AfterScenario(uponType = ScenarioType.ANY, uponOutcome = AfterScenario.Outcome.FAILURE) - public void afterFailedScenario() { - if (serverLogXmlReporterExtension != null) { - if (serverLogXmlReporterExtension.getSshReportType() != SshReportType.TEMPLATE && - serverLogXmlReporterExtension.isLoggingOnFailure()) { - if (scenarioEnd == null) { - scenarioEnd = ZonedDateTime.now(); - } - storeTemplatesLogs(getReportableSshTemplates(getSshTemplates())); - } - } } public void markLogTime(String logTimeAlias) { @@ -156,18 +108,11 @@ public void checkLogDataPresence(String systemQualifier, String stringTable, Ver checkDataPresence(systemQualifier, scenarioStart, stringTable, verifier); } - public void setSshReporterMode(ExpressionEvaluatingParameter mode) { - if (serverLogXmlReporterExtension != null) { - serverLogXmlReporterExtension.setSshReportType(SshReportType.valueOf(mode.getValue())); - } else { - log.warn("ServerLogXmlReportExtension is not registered, no logs will be present in report."); - } - } - /** * @deprecated use logContainsData(String systemQualifier, String stringTable) instead * If you set timestamps via separate steps, log reading is more accurate and use cache */ + @Deprecated public void checkLogDataPresence(String systemQualifier, String startTimeAlias, String stringTable, Verifier verifier) { checkDataPresence(systemQualifier, testContext.get(startTimeAlias), stringTable, verifier); } @@ -275,78 +220,24 @@ protected String getSearchColumnName(ExamplesTable searchData) { } } - private void storeInExtension(MultiKey key, String logContent) { - if (serverLogXmlReporterExtension != null) { - serverLogXmlReporterExtension.registerLogContent(key, logContent); - } else { - log.warn("ServerLogXmlReportExtension is not registered, no logs will be present in report."); - } - } - - private void storeTemplatesLogs(Map> sshTemplates) { - sshTemplates.entrySet().stream().forEach(entry -> - entry.getValue().stream().forEach(sshTemplate -> { - MultiKey multiKey = new MultiKey(entry.getKey(), - scenarioStart.toString(), - scenarioEnd.toString(), + public MultiKeyMap getTemplateLogs(Map> sshTemplates){ + ZonedDateTime scenarioEnd = ZonedDateTime.now(); + MultiKeyMap templateLogs = MultiKeyMap.multiKeyMap(new LRUMap()); + sshTemplates.entrySet().forEach(entry -> + entry.getValue().forEach(sshTemplate -> { + MultiKey multiKey = new MultiKey(entry.getKey(), + scenarioStart,//.toString(), + scenarioEnd,//.toString(), sshTemplate.getSshSetting().getHostname() + ":" + sshTemplate.getSshSetting().getPort(), sshTemplate.getSshSetting().getLogPath()); try { String sshLog = sshTemplate.copyLog(scenarioStart, scenarioEnd).getLogContents(); - storeInExtension(multiKey, sshLog); + templateLogs.put(multiKey, sshLog); } catch (Exception e) { - storeInExtension(multiKey, ExceptionUtils.getStackTrace(e)); + templateLogs.put(multiKey, ExceptionUtils.getStackTrace(e)); } }) ); - } - - private Map> getSshTemplatesForType(Class clazz) { - Map> sshTemplates = new HashMap<>(); - String[] beanNames = beanFactory.getBeanNamesForType(clazz); - for (String beanName : beanNames) { - BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName); - if (bd instanceof RootBeanDefinition) { - Qualifier qualifier = ((RootBeanDefinition) bd).getResolvedFactoryMethod().getAnnotation(Qualifier.class); - if (sshTemplates.get(qualifier.value()) == null) { - sshTemplates.put(qualifier.value(), new ArrayList<>()); - } - sshTemplates.get(qualifier.value()).add((T) beanFactory.getBean(beanName)); - } - } - return sshTemplates; - } - - private Map> getSshTemplates() { - Map> sshTemplatesArray = getSshTemplatesForType(SshTemplate[].class); - Map> sshTemplates = getSshTemplatesForType(SshTemplate.class); - - //merge both maps together to simple Map> - sshTemplatesArray.entrySet() - .stream() - .forEach(entry -> { - List flattenedSshTemplatesArray = entry.getValue() - .stream() - .flatMap(Arrays::stream) - .collect(Collectors.toList()); - if (sshTemplates.get(entry.getKey()) != null) { - sshTemplates.get(entry.getKey()).addAll(flattenedSshTemplatesArray); - } else { - sshTemplates.put(entry.getKey(), flattenedSshTemplatesArray); - } - }); - - return sshTemplates; - } - - private Map> getReportableSshTemplates(Map> sshTemplates) { - Map> reportableSshTemplates = new HashMap<>(); - sshTemplates.entrySet().stream().forEach(entry -> { - List result = entry.getValue().stream().filter(template -> template.isReportable()).collect(Collectors.toList()); - if (!result.isEmpty()) { - reportableSshTemplates.put(entry.getKey(), result); - } - }); - return reportableSshTemplates; + return templateLogs; } } diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java index c1b6c100..ff773d6c 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshReportType.java @@ -2,6 +2,9 @@ /** * Modes for ServerLogXmlReporterExtension + * FULL for full log content from all sshTemplates + * TEMPLATE for full log content from reportable sshTemplates + * CACHE from all log parts saved in case during scenario run */ public enum SshReportType { FULL, diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java index f68844f6..ed25f217 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshSteps.java @@ -79,9 +79,4 @@ public void logContainsData(String systemQualifier, String startTimeAlias, Strin public void dataNotInLog(String systemQualifier, String stringTable) { sshHandler.checkLogDataPresence(systemQualifier, stringTable, notContainsVerifier); } - - @Given("ssh reporter mode is set to [$mode]") - public void setSshReporterMode(ExpressionEvaluatingParameter mode) { - sshHandler.setSshReporterMode( mode); - } } diff --git a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java index 806f2ff1..8efc44a9 100644 --- a/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java +++ b/jbehave-support-core/src/main/java/org/jbehavesupport/core/ssh/SshTemplate.java @@ -45,6 +45,11 @@ private abstract static class Constants { static final String CMD_DELIMITER = "; "; } + /** + * @deprecated + * use {@link #SshTemplate(SshSetting, String, RollingLogResolver, boolean)}instead + */ + @Deprecated public SshTemplate(SshSetting sshSetting, String timestampFormat, RollingLogResolver rollingLogResolver) { this(sshSetting, timestampFormat, rollingLogResolver, false); } diff --git a/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt b/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt index 4c24919e..73c64174 100644 --- a/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt +++ b/jbehave-support-core/src/main/resources/report-generator/serverLogXmlRep.xslt @@ -79,9 +79,15 @@ - - - + + + + + + + + +

@@ -94,10 +100,8 @@ + - - -

Show/hide log @@ -112,10 +116,8 @@ + - - - - + + + + - - -