How to attach file to result directory


I would like to save the screenshot from an oscilloscope along with the other results.

The structure could look like this:
/Results for the CSV result files
/Logs for the LOG files
/Screenshoots for the screenshots.

CSV and LOG files are published by their respective result listeners. So it seems to me that I should create a special result listener for the screenshot files. This result listener would set the directory path and a prefix for the file both using MacroString.

The oscilloscope method can copy a file from the remote location (in the scope) to a local location (computer).

My first idea is to save the screenshot to a temporary location, pass this location using “Publish” and move the file into the directory specified in the result listener setup.

Any suggestions for a better approach?


@ts111 welcome to OpenTAP!

Your approach sounds like it would work. Another idea, that may be a little more simple would be to just have a “Store Screenshot” Test Step that handled all the logic for actually saving the file. Then in your ResultListener, if you still wanted to store something, you could just store the filepath.

Welcome @ts111. I second @brennen_direnzo’s ideas. Your test step (that captures the image files) can figure out the csv result directory like so:

(using Keysight.OpenTap.Plugins.Csv;)

CsvResultListener csvResultListener = ResultSettings.Current.Where(r => r is CsvResultListener).FirstOrDefault() as CsvResultListener;
string resultDirectory = Path.GetDirectoryName(csvResultListener.FilePath);

then save the image directly to that folder or a subfolder.

1 Like

I assumed you’re using the default OpenTAP CSV result listener, but you ought to be able to take a similar approach with any result listener type.

1 Like

Hi @ts111 and welcome.
I concur with the other guys that your approach is perfectly reasonable, and pretty much identical to how I initially implemented this functionality. However, you’ve touched on a problem that every developer now faces, dreaded ‘reports’. Results viewer does a nice job accessing, drilling and displaying results, but correct if I’m wrong, it sounds like you will be capturing data to display in a report eventually and be looking to develop your report creation tool maybe.

I usually have the step decide where to put the file from the instrument by calling the capture method with the file path, i.e. Oscilloscope.ScreenCapture(string fullPath).

            var filePath =  @"C:\ScreenShots\SomeImage.png";

            var resParams = new ResultParameters
                new ResultParameter("", name: "Param1", "SomeValue1"),
                new ResultParameter("", name: "Param2", "SomeValue2"),
                new ResultParameter("", name: "Param3", "SomeValue3"),
                new ResultParameter("", name: "PathToFile", filePath),
                new ResultParameter("", name: "FileTitle", "SomeTitle"),

           Results.Publish("SomeResult", resParams.Select(x => x.Name).ToList(), resParams.Select(x =>x.Value).ToArray());

I eventually moved to a MongoDB ResultListener that stores everything published, basically just like a CSV file but in a database. All my steps now record data in the same way, whether it’s large data arrays, images or multiple results. It was relatively straightforward from that point to develop another app and write queries to extract the data and produce tabulated results for as .xlsx, .docx or HTML.
Unfortunately, Publish and PublishTable do not yet have the ability to accept other parameters, like a file, so I had to implement some workarounds in the ResultListener to look for PathToFile so it knew what to do with it, which one way you could try.
Hopefully, it would be good to see more advanced publishing methods future though.


@jason.hicks I know you submitted something on this in the past, does this implementation address what you are looking for:

Also you MongoDB plugin may be something useful to contribute back :wink: . If you are interested, let me know.

Related, Keysight does have more of a report ResultListener for HTLM5 it is currently under a commercial license, but we are looking in to changing that.

1 Like

AWESOME!!! When can we have it?


Start with a trial and let us know what you think:

I think this is a very complex subject, and when I try to explain things it just turns into waffle. It deserves a whole topic and discussion itself.
I can’t quite see the reasoning of #577, but it would probably solve some of the issues I’ve faced. Can you post a real-world example of the feature?

I’m not sure if there is a good one currently. It is listed as related to the original issue you submitted (#33)

You can see the tests that were implemented, that may be helpful. I’m not personally aware of anyone that has adopted this.

:crazy_face: Not at all embarrassing. I remember it now, DOH!
I’ll take a look and get back to you at some point.

I’d have loved to create the MongoDB ResultListener, but our result storage paradigms are way too different. It’s something we could talk about but I recall Rolf saying it would be very difficult to get working with ResultViewer and I’d have to get my head around all that SQLish stuff again.

1 Like

Another similar idea would be to create a ResultListener that stores all of these files into a single Zip file, per execution. Such that the zip file would contain a log file, a report file, some screenshot files, etc.

Below is an example of a Zip File Result Listener.



using System;
using System.IO;
using System.IO.Compression;
using OpenTap;

namespace SomeNamespace
    [Display("Zip File", "Save results and logs into a zip file.")]
    public class ZipFileResultListener : ResultListener
        [Display("Report Path", Order: 2, Description: "Path where report files are to be generated")]
        public string ReportPath { get; set; }

        public ZipFileResultListener()
            // Default values
            Name = "Zip";
            ReportPath = @"C:\Temp\Zip";

        public override void OnTestPlanRunCompleted(TestPlanRun planRun, Stream logStream)
            var fileName = string.Format("{0}{1:d2}{2:d2}-{3:d2}{4:d2}{5:d2}_{6}",

            // Create zip file containing html result file, txt log file, etc.
            using (var memoryStream = new MemoryStream())
                // Create zip in memory
                using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
                    // Create log file in memory
                    var logFileName = fileName + ".txt";
                    var logFile = zipArchive.CreateEntry(logFileName, CompressionLevel.Optimal);
                    using (var logContentsEntryStream = logFile.Open())
                        logStream.Seek(0, SeekOrigin.Begin);

                    // Create html results file
                    // ...

                    // Create screenshot file
                    // ...

                // Create directory if it doesn't exist

                // Write zip file to disk
                var zipFullPath = Path.Combine(ReportPath, fileName + ".zip");
                using (var zipFileStream = new FileStream(zipFullPath, FileMode.Create))
                    memoryStream.Seek(0, SeekOrigin.Begin);
                Log.Info("Saved results to {0}", zipFullPath);

Hi everyone,

Thanks for all the suggestions! I tried my original approach and then @brennen_direnzo, @david-wsd 's one.
It turned out the latest is much simpler:

// retrieve csv result directory
            CsvResultListener csvResultListener = ResultSettings.Current.Where(r => r is CsvResultListener).FirstOrDefault() as CsvResultListener;
            string resultDirectory = Path.GetDirectoryName(csvResultListener.FilePath.Expand(PlanRun));
            Log.Info("CSV result dir: {0}", resultDirectory);

            string remote_file_path = Scope.SaveScreenshot(FileName.Expand(PlanRun));
            string local_file_path = Path.Combine(resultDirectory, Path.GetFileName(remote_file_path));

            Scope.ReadToFileFromInstrument(remote_file_path, local_file_path);


I don’t need to create any listener and I don’t have to save the screenshot to a temporary location.
I just had to expand the path and create the directory.

I will also look into the MongoDB approach suggested by jason.hicks. That seems to be a nice solution for the long term.


Hi @jason.hicks , I am new to the forum but have used TAP for quite some time. My team is interested in implement a MongoDB result listener. Would you be open to share your experiences with us?


Hi @carlos.montes. and welcome to the forum. I’ll start a new topic relating to MongoDB rather than hijack this one.