'ERROR FOUserAgent:103 - Image not found. URI:
I am trying to generate PDF Report with apache fop dependency, using fop version 2.6, the PDF is created and the screenshot is actually taken but not added to the PDF with following exception:
2022-01-24 08:46:14 ERROR FOUserAgent:103 - Image not found. URI: D:\Users\
But the image is in the path specified
Java version 11
org.apache.xmlgraphics
fop
2.6
PDF Report generator class:
Package com.element.fleet.testing.util;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.FileUtils;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.FopFactoryBuilder;
import org.apache.fop.apps.MimeConstants;
import org.apache.xmlgraphics.io.URIResolverAdapter;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.testng.IReporter;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.xml.XmlSuite;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.ge.capital.rainbow.common.utils.UnzipJar;
import com.ge.capital.rainbow.testng.Documentation;
import com.ge.capital.rainbow.testng.FailureMessage;
import com.ge.capital.rainbow.webdriver.BaseWebDriver;
import com.ge.capital.rainbow.webdriver.WebDriverAccess;
import com.ge.capital.rainbow.webdriver.WebDriverPerformance;
import com.google.common.base.Splitter;
import com.ge.capital.rainbow.database.statustracker.StatusTrackerInterface;
import com.ge.capital.rainbow.common.utils.StringUtils;
public class PDFReporterLocal implements ITestListener, IReporter {
private static final Logger logger = Logger.getLogger(PDFReporterLocal.class.getName());
private String screenshotPath;
private String reportPath;
private String suitName="";
private String useApplicationPDF=null;
private String triggerEmail=null;
private String browser = null;
private int responseLines = 100;
private List<Integer> verbosity = null;
protected static List<BufferedImage> extraScreenshotImage = new ArrayList<BufferedImage>();
protected static DateFormat dfTime = new SimpleDateFormat("hh:mm:ss a");
protected static DateFormat dfDateTime = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss a");
protected static Calendar calendar = Calendar.getInstance();
private static String suitHeader;
private static HashMap<String, String> extraParameters;
private static String testDescription;
public PDFReporterLocal(final String screenshotPath, final String reportPath) throws Throwable {
setScreenshotPath(screenshotPath);
setReportPath(reportPath);
extraParameters = new HashMap<String, String>();
}
public PDFReporterLocal() throws Throwable{
this(null, null);
}
public String getScreenshotPath() {
return this.screenshotPath;
}
public void setScreenshotPath(final String path) throws Throwable {
if(path == null) {this.screenshotPath = System.getProperty("user.dir").toString() + "\\target";}
else {this.screenshotPath = path;}
}
public static void takeExtraScreenshot() throws Throwable {
try {
final Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
final Rectangle screenBounds = new Rectangle(0, 0, screenDim.width, screenDim.height);
final Robot robot = new Robot();
if(BaseWebDriver.headless) {
File screen = ((TakesScreenshot) WebDriverAccess.getDriver()).getScreenshotAs(OutputType.FILE);
extraScreenshotImage.add(ImageIO.read(screen));
}//If condition for headless browser
else
extraScreenshotImage.add(robot.createScreenCapture(screenBounds));
} catch (Throwable t) {
System.err.println(t.getStackTrace());
}
}
public static void addParameter(String input)
{
String methodName =Thread.currentThread().getStackTrace()[2].getMethodName();
extraParameters.put(methodName,input);
}
public static void addParameter(String name, String input)
{
extraParameters.put(name,input);
}
public String getReportPath() {
return this.reportPath;
}
public void setReportPath(final String path) throws Throwable {
if(path == null){this.reportPath = System.getProperty("user.dir").toString() + "/target";} // + File.separator + "test-output";}
else {this.reportPath = path;}
System.out.println(">>>REPORT PATH>>>"+this.reportPath);
}
// This method is used to log the output within methods
@SuppressWarnings("unchecked")
protected static void logOutput(final ITestResult result, final String line) {
final Object log = result.getAttribute("log");
final List<String> logList = (null == log) ? new ArrayList<String>() : (List<String>)log;
logList.add(line);
result.setAttribute("log", logList);
}
// Store the verbosity as powers of 2
public void onStart(final ITestContext context) {
final String verbosityValue = context.getSuite().getParameter(ReportVerbosity.PARAMETER);
suitHeader=context.getSuite().getParameter("reportName");
testDescription=context.getSuite().getParameter("testDescription");
useApplicationPDF=context.getSuite().getParameter("useApplicationPDF");
triggerEmail=context.getSuite().getParameter("triggerEmail");
browser=context.getSuite().getParameter("browser");
try {
responseLines= Integer.parseInt(context.getSuite().getParameter("responseLines"));
} catch (Exception e) { }
final String reportPath = context.getSuite().getParameter("reportPath");
if(reportPath==null||reportPath.length()==0 || "".equalsIgnoreCase(reportPath))
{suitName="target/"+context.getSuite().getParameter("reportName")+".pdf";}
else
{suitName = reportPath +context.getSuite().getParameter("reportName")+".pdf";}
if(verbosityValue == null)
{this.verbosity = Arrays.asList(new Integer(0));}
else
{this.verbosity = ReportVerbosity.computePowersOfTwoPartition(ReportVerbosity.validateVerbosity(verbosityValue));}
}
// Makes a screenshot if verbosity is set to make screenshots
public void onTestFailure(final ITestResult result) {
if (extraParameters.isEmpty()) {
if (ReportVerbosity.contains(this.verbosity, ReportVerbosity.SCREENSHOTS_ON_FAILURE.value()) ) {
final String filePath = printScreenshot(result);
if (null != filePath)
{result.setAttribute("screenshot", filePath);}
}
}else {
setRequestResponse(result);
}
setDocumentationValues(result);
setFailureMessage(result);
setPerformance(result);
}
// Makes a screenshot if verbosity is set to make screenshots always
public void onTestSuccess(final ITestResult result) {
if (extraParameters.isEmpty()) {
if (ReportVerbosity.contains(this.verbosity, ReportVerbosity.SCREENSHOTS_ON_SUCCESS.value())) {
final String filePath = printScreenshot(result);
if (null != filePath) {
result.setAttribute("screenshot", filePath);
}
}
}else {
setRequestResponse(result);
}
setDocumentationValues(result);
setPerformance(result);
}
private void setPerformance(final ITestResult result)
{
WebDriverPerformance.PerfData data = WebDriverPerformance.getResults();
if (data != null)
{
result.setAttribute("docLoadTime", data.docCompleteTime);
result.setAttribute("docRequests", data.docCompleteRequests);
result.setAttribute("docBytesIn", data.docCompleteBytesIn);
result.setAttribute("fullLoadTime", data.fullyLoadedTime);
result.setAttribute("fullRequests", data.fullyLoadedRequests);
result.setAttribute("fullBytesIn", data.fullyLoadedBytesIn);
result.setAttribute("PerformanceVisuallyComplete", data.visuallycomplete);
result.setAttribute("PerformanceScore", data.speedIndex);
result.setAttribute("Performancedetails", data.url);
result.setAttribute("TransactionName", data.transactionName);
}
}
protected void setFailureMessage(final ITestResult result){
Annotation[] annotations;
final String methodName=result.getMethod().getMethodName();
for(Method method:result.getTestClass().getRealClass().getDeclaredMethods()){
if(method.getName().equals(methodName)){
//System.out.println(method.getName());
annotations=method.getAnnotations();
for(Annotation annotation:annotations){
//System.out.println(annotation.annotationType());
if(annotation instanceof FailureMessage){
final FailureMessage myAnnotation = (FailureMessage) annotation;
result.setAttribute("failureMessage", myAnnotation.value());
System.out.println("<Failure-Message>" + myAnnotation.value() + "</Failure-Message>");
}
}
}
}
}
protected void setDocumentationValues(final ITestResult result){
Annotation[] annotations;
final String methodName=result.getMethod().getMethodName();
for(Method method:result.getTestClass().getRealClass().getDeclaredMethods()){
if(method.getName().equals(methodName)){
//System.out.println(method.getName());
annotations=method.getAnnotations();
for(Annotation annotation:annotations){
//System.out.println(annotation.annotationType());
if(annotation instanceof Documentation){
final Documentation myAnnotation = (Documentation) annotation;
result.setAttribute("expected", myAnnotation.expected());
result.setAttribute("step", myAnnotation.step());
}
}
}
}
}
protected void setRequestResponse(final ITestResult result)
{
for ( Entry<String, String> entry : extraParameters.entrySet()) {
final String[] lines = entry.getValue().split("\n");
final StringBuffer bf = new StringBuffer();
int i = 0;
for (String line : lines) {
if(line.length() < 20)
{bf.append(line).append("\n"); i++;}
else {
Iterable<String> pieces = Splitter.fixedLength(20).split(line);
for (String string : pieces) {
if (i > responseLines) {
break;
}
bf.append(string).append("\n");
i++;
}
}
if (i > responseLines) {
break;
}
}
result.setAttribute(entry.getKey(), bf.toString());
}
}
protected String printScreenshot(final ITestResult tr) {
try {
final String filePath = screenshotPath + File.separator + tr.getMethod().getXmlTest().getName().replace(' ', '_') + "_" + tr.getMethod().getMethodName() + "_"
+ System.currentTimeMillis() + ".PNG";
if (browser == null || !browser.equals("sauce"))
{
final Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
final Rectangle screenBounds = new Rectangle(0, 0, screenDim.width, screenDim.height);
final Robot robot = new Robot();
BufferedImage image= null;
if(BaseWebDriver.headless) {
File screen = ((TakesScreenshot) WebDriverAccess.getDriver()).getScreenshotAs(OutputType.FILE);
image= ImageIO.read(screen);
}//If condition for headless browser
else
image = robot.createScreenCapture(screenBounds);
final File screenshotFile = new File(filePath);
for(int i = extraScreenshotImage.size() ; i>0 ; i--) {
image = combineImages(extraScreenshotImage.get(i-1), image);
}
ImageIO.write(image, "PNG", screenshotFile);
}
else
{
File scrFile = ((TakesScreenshot)(WebDriverAccess.getDriver())).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(scrFile, new File(filePath));
}
return filePath;
} catch (Throwable t) {
System.err.println(t.getStackTrace());
return null;
}
}
private static String translateTestStatus(final int status) {
switch(status) {
case ITestResult.SUCCESS: return "PASS";
case ITestResult.FAILURE: return "FAIL";
case ITestResult.SKIP: return "SKIP";
}
return "FAIL";
}
private static void writeXMLFile(final DOMSource source, final String path) throws Exception {
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
final Transformer transformer = transformerFactory.newTransformer();
final StreamResult result = new StreamResult(new File(path).toURI().toString()/*getAbsolutePath()*/);
transformer.transform(source, result);
}
private static BufferedImage combineImages(BufferedImage img1, BufferedImage img2) {
int offset = 5;
int wid = Math.max(img1.getWidth(), img2.getWidth()) + offset;
int height = img1.getHeight() + img2.getHeight() + offset;
BufferedImage newImage = new BufferedImage(wid, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = newImage.createGraphics();
Color oldColor = g2.getColor();
g2.setPaint(Color.WHITE);
g2.fillRect(0, 0, wid, height);
g2.setColor(oldColor);
g2.drawImage(img1, null, 0, 0);
g2.drawImage(img2, null, 0, img1.getHeight() + offset);
g2.dispose();
return newImage;
}
public void generateReport(final List<XmlSuite> xmlSuites, final List<ISuite> suites, final String outputDirectory) {
try {
// process the root element
final Document doc = DOMBuilder.createEmptyDocument();
final Element rootElement = doc.createElement("test-run-result");
doc.appendChild(rootElement);
/*List<String> reporterOutput = Reporter.getOutput();
if(reporterOutput != null && !reporterOutput.isEmpty()) {
Element outputElement = addElement(doc, rootElement, "reporter-output");
for(String line : reporterOutput) {
Element lineElement = addElement(doc, outputElement, "line");
addCDATA(doc, lineElement, line);
}
}*/
// overall counts
int passedRunCount = 0;
int failedRunCount = 0;
int skippedRunCount = 0;
// overall durations
long passedRunDuration = 0l;
long failedRunDuration = 0l;
long skippedRunDuration = 0l;
long totalRunDuration = 0l;
// overall dates
Date runStartDate = null;
Date runEndDate = null;
String runStatus = "PASS";
// process all suites
for(ISuite suite : suites) {
final Element suiteElement = DOMBuilder.buildSuite(doc, rootElement, suite);
/*if(isSuitName){
suitName="target/"+suiteElement.getAttribute("name")+".pdf";
isSuitName=false;
}*/
passedRunCount += Integer.parseInt(suiteElement.getAttribute("passed"));
skippedRunCount += Integer.parseInt(suiteElement.getAttribute("skipped"));
failedRunCount += Integer.parseInt(suiteElement.getAttribute("failed"));
passedRunDuration += Long.parseLong(suiteElement.getAttribute("passed-duration-ms"));
skippedRunDuration += Long.parseLong(suiteElement.getAttribute("skipped-duration-ms"));
failedRunDuration += Long.parseLong(suiteElement.getAttribute("failed-duration-ms"));
totalRunDuration += Long.parseLong(suiteElement.getAttribute("duration-ms"));
final long startTime = dfDateTime.parse(suiteElement.getAttribute("started-at")).getTime();
final long endTime = dfDateTime.parse(suiteElement.getAttribute("finished-at")).getTime();
if(runStartDate == null || runStartDate.getTime() > startTime) {
calendar.setTimeInMillis(startTime);
runStartDate = calendar.getTime();
}
if(runEndDate == null || runEndDate.getTime() < endTime) {
calendar.setTimeInMillis(endTime);
runEndDate = calendar.getTime();
}
final String suiteStatus = suiteElement.getAttribute("status");
if("PASS".equals(runStatus)) {runStatus = suiteStatus;}
else if("SKIP".equals(runStatus) && "FAIL".equals(suiteStatus)){ runStatus = "FAIL";}
}
DOMBuilder.addAttribute(doc, rootElement, "passed", String.valueOf(passedRunCount));
DOMBuilder.addAttribute(doc, rootElement, "skipped", String.valueOf(skippedRunCount));
DOMBuilder.addAttribute(doc, rootElement, "failed", String.valueOf(failedRunCount));
DOMBuilder.addAttribute(doc, rootElement, "total", String.valueOf(passedRunCount + skippedRunCount + failedRunCount));
DOMBuilder.addAttribute(doc, rootElement, "passed-duration-ms", String.valueOf(passedRunDuration));
DOMBuilder.addAttribute(doc, rootElement, "skipped-duration-ms", String.valueOf(skippedRunDuration));
DOMBuilder.addAttribute(doc, rootElement, "failed-duration-ms", String.valueOf(failedRunDuration));
DOMBuilder.addAttribute(doc, rootElement, "duration-ms", String.valueOf(totalRunDuration));
DOMBuilder.addAttribute(doc, rootElement, "started-at", dfDateTime.format(runStartDate));
DOMBuilder.addAttribute(doc, rootElement, "finished-at", dfDateTime.format(runEndDate));
DOMBuilder.addAttribute(doc, rootElement, "status", runStatus);
//if (null != suite.getParameter("almAllRunsUnderBrowserDateCombo"))
if ((useApplicationPDF!=null) && (true == useApplicationPDF.equalsIgnoreCase("Yes")))
{
//no need to extract from the jar or copy from project because the project has its own templates for use
System.out.println("Using PDFtempletes folder from application");
}
else
{
if (PDFReporterLocal.class.getResource("PDFReporterLocal.class").toString().startsWith("jar:") == true)
{
//unpack the templates from the jar for use
UnzipJar.unzipJar("resources/pdftemplates", "rainbow-testng", "FROMPOM", "pdftemplates");
System.out.println("Unpacked pdftemplates folder from rainbow-testng jar file");
}
else
{
if (System.getProperty("os.name").toLowerCase().contains("win") == false)
{
try
{
File sourceFile = new File(System.getProperty("user.dir").toString().substring(0,System.getProperty("user.dir").toString().lastIndexOf("/")) + "/rainbow/rainbow-testng/src/main/resources/pdftemplates");
File destFile = new File("resources/pdftemplates");
FileUtils.copyDirectory(sourceFile,destFile);
destFile.setExecutable(true);
System.out.println("Copied pdftemplates to target folder ");
}
catch (Exception ex)
{
System.out.println("EXCEPTION IN PDF Reporter: " + ex.getMessage().toString());
}
}
else
{
// try
// {
String PDFReporterFilePath = PDFReporterLocal.class.getResource("PDFReporterLocal.class").toString();
// System.out.println("PDFTemplate path : "+PDFReporterFilePath.substring(6, PDFReporterFilePath.indexOf("rainbow"))+"rainbow\\rainbow-testng\\src\\main\\resources\\pdftemplates");
// File sourceFile = new File(PDFReporterFilePath.substring(6, PDFReporterFilePath.indexOf("rainbow"))+"rainbow\\rainbow-testng\\src\\main\\resources\\pdftemplates");
File destFile = new File("resources/pdftemplates");
// FileUtils.copyDirectory(sourceFile,destFile);
destFile.setExecutable(true);
System.out.println("Copied pdftemplates to target folder ");
// }
// catch (Exception ex)
// {
// System.out.println("EXCEPTION IN PDF Reporter: " + ex.getMessage().toString());
// }
}
}
}
final DOMSource dom = new DOMSource(doc);
writeXMLFile(dom, "target/test-run-result.xml");
writeCSVFile("resources/pdftemplates/csvreport.xsl", suitName);
writeXMLFile(dom, suitName.replace(".pdf", "") + "_test-run-result.xml");
//Put in this work around so fleet gets a special variation, rather then having two different PDF listeners.
String jenkinsInstance;
try{
jenkinsInstance = System.getenv("BUILD_URL").toString();
}
catch (Exception ex)
{
//do nothing, value probably does not exist. so just set the instance to blank
jenkinsInstance = "";
}
if (jenkinsInstance.contains("8086")|| jenkinsInstance.contains("8082")) {
if ((failedRunCount > 0 || skippedRunCount > 0) && (suitName.lastIndexOf("_FAILED") == -1)) {
suitName = suitName.substring(0, suitName.indexOf(".pdf")) + "_FAILED" + ".pdf";
}
}
writePDFFile(dom, "resources/pdftemplates/pdfreport.xsl", suitName);
//writePDFFile(dom, "../rainbow/rainbow-testng/target/classes/pdftemplates/pdfreport.xsl", suitName);
//delete the test-run-result file since we really only want to use the ones iwth the suite name in them
//the base one is used only in pdf generation
new File("target/test-run-result.xml").delete();
if ((triggerEmail!=null) && (true == triggerEmail.equalsIgnoreCase("Yes")))
{
triggerMail();
}
}
catch(Exception e) {
System.out.println("Error in Generate Report:");
e.printStackTrace();
}
}
private static void writeCSVFile(final String templatePath, final String reportPath) throws Exception {
try{
final File xsltFile = new File(templatePath); // templatePath
File xmlsource=new File ("target/test-run-result.xml");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document resultDoc = builder.parse(xmlsource);
Source resultSrc = new DOMSource(resultDoc);
final StreamSource transformSource = new StreamSource(xsltFile);
Transformer transformer = TransformerFactory.newInstance().newTransformer(transformSource);
final String csvName=reportPath.replace(".pdf", "")+".csv";
Result outputTarget = new StreamResult(new File(csvName).toURI().toString()/*getAbsolutePath()*/);
transformer.transform(resultSrc, outputTarget);
System.out.println("writing CSV File SUCCESS");
}
catch (Exception ex)
{
System.out.println("Exception in writing CSV File: " + ex.getMessage());
}
}
private static void writePDFFile(final DOMSource dom, final String templatePath, final String reportPath) throws Exception {
FileOutputStream fileStream = null;
ByteArrayOutputStream outStream = null;
// try
// {
// URIResolverAdapter uriResolverAdapter = new URIResolverAdapter(new UserAgentUriResolver());
// FopFactoryBuilder builder = new FopFactoryBuilder(URI.create("."), uriResolverAdapter);
// final FopFactory fopFactory = FopFactory.
final File xsltFile = new File(templatePath); // templatePath pass
final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
System.out.println("fopFactory"+fopFactory);
final FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
System.out.println("foUserAgent"+foUserAgent);
outStream = new ByteArrayOutputStream();
final StreamSource transformSource = new StreamSource(xsltFile.toURI().toString());
final Transformer xslfoTransformer = new net.sf.saxon.TransformerFactoryImpl().newTransformer(transformSource);
final Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, outStream);
final Result res = new SAXResult(fop.getDefaultHandler());
xslfoTransformer.transform(dom, res);
final File pdfFile = new File(reportPath); // reportPath
fileStream = new FileOutputStream(pdfFile);
fileStream.write(outStream.toByteArray());
fileStream.close();
outStream.close();
System.out.println("writing PDF File SUCCESS");
// }
// catch (Exception ex)
// {
// System.out.println("Exception in writing PDF File: " + ex.getMessage());
// ex.printStackTrace();
//try to close the filestream and outstream just in case, because we dont want to deal with files that are left open
//open files hang the archiver
try
{
fileStream.close();
}
catch (Exception ex1)
{
//dont care, just making sure the file is closed
logger.log(Level.SEVERE, ex1.getMessage());
}
try
{
outStream.close();
}
catch (Exception ex2)
{
//dont care, just making sure the outstream is closed
logger.log(Level.SEVERE, ex2.getMessage());
}
// }
}
enum ReportVerbosity {
NONE(0), SUITESUMMARY(1), TESTSUMMARY(2), STEPS(4), DETAILS(8), LOG(16), SCREENSHOTS_ON_FAILURE(32), SCREENSHOTS_ON_SUCCESS(64);
public static String PARAMETER = "reportDetailLevel";
public static String SCREENSHOT = "makeScreenshots";
public static String BROWSER = "browser";
public static int MAX = 127;
private int verbosity;
ReportVerbosity(final int value) {
this.verbosity = value;
}
public void onTestStart(final ITestResult arg0) {
// TODO Auto-generated method stub
extraScreenshotImage.clear();
extraParameters.clear();
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
