How to Interact with Files and with the Environment in Test Scripts

In addition to the test-specific functionality that Squish provides, test scripts can also use the native functionality (including the standard libraries) provided by the scripting languages themselves. In this subsection we will show how to use native functionality to read data from an external file, write data to an external file, check for the existence of an external file, and delete an external file. In addition, we will see how to compare two external files, and also how to read the environment variables that are set when the script is running.

Python-specific

The Python examples don't show any import statements, but these are of course required when non-global functions are used. The import statements ought to be done before the main function is defined, with those shown below being sufficient for the examples shown in this section.

import codecs, filecmp, os, subprocess, sys

Perl-specific

The Perl examples don't show any use statements, but these are of course required when non-global functions are used. The use statements ought to be done before the main function is defined, with those shown below being sufficient for the examples shown in this section.

use File::Basename;
use File::Spec;

How to Interact with External Files in Test Scripts

Here we will see how to read data from an external file, write data to an external file, check for the existence of an external file, and delete an external file.

How to Read Data from an External File

Reading an external file involves getting its complete filename (including path), and then reading it in the standard way that the scripting language supports. For example:

    infile = findFile("testdata", "before.csv")
    infile = infile.replace("/", os.sep)
    test.log("Reading %s" % infile)
    file = codecs.open(infile, "r", "utf-8")
    lines = []
    for line in file:
        lines.append(line)
    file.close()
    test.verify(len(lines) == 13)
    infile = findFile("testdata", "before.csv");
    infile = infile.replace(/[\/]/g, File.separator);
    test.log("Reading " + infile);
    file = File.open(infile, "r");
    var lines = [];
    var i = 0;
    while (true) {
        var line = file.readln();
        if (line == null)
            break;
        lines[i++] = line;
    }
    file.close();
    test.verify(lines.length == 13);
    my $sep = File::Spec->rootdir();

    # Load data from an external file
    my $infile = findFile("testdata", "before.csv");
    $infile =~ s,/,$sep,g;
    test::log("Reading $infile");
    open(FILE, "<:encoding(UTF-8)", $infile) or test::fail("Failed to read $infile");
    my @lines = <FILE>;
    close(FILE);
    test::verify(scalar(@lines) == 13);
    infile = natify(findFile("testdata", "before.csv"))
    Test.log("Reading %s" % infile)
    lines = []
    File.open(infile, "r:utf-8") do |file|
        file.each {|line| lines << line}
    end
    Test.verify(lines.length == 13)
    set infile [file nativename [findFile "testdata" "before.csv"]]
    test log "Reading $infile"
    set fh [open $infile]
    set text [read $fh]
    close $fh
    set text [string trimright $text]
    set lines [split $text "\n"]
    test compare [llength $lines] 13

Here, we read a file called before.csv that is in the suite's (or the test case's) testdata directory. The file is a text file using the UTF-8 encoding. We open the file and read it line by line into a list (or array) of lines or into a string which we then break into lines, depending on the scripting language. And at the end we check that we have got exactly the number of lines we expected.

Squish uses Unix-style path separators internally on all platforms, but because we want to show the path to the user (using the test.log(message) function), we replace these with the path separator that is appropriate for the platform (e.g., "\" on Windows).

JavaScript has no native support for file handling or for operating system interaction, so Squish provides the File Object and the OS Object to fill these gaps.

How to Write Data to an External File

Writing to an external file is simply a matter of creating a filename, opening the file for writing, and writing data to it in the standard way that the scripting language supports. For example:

    outfile = os.path.join(os.getcwd(), os.path.basename(infile) + ".tmp")
    outfile = outfile.replace("/", os.sep)
    test.log("Writing %s" % outfile)
    file = codecs.open(outfile, "w", "utf-8")
    for line in lines:
        file.write(line)
    file.close()
    outfile = infile + ".tmp";
    var i = outfile.lastIndexOf(File.separator);
    if (i > -1)
        outfile = outfile.substr(i + 1);
    outfile = OS.cwd().replace(/[\/]/g, File.separator) + File.separator + outfile;
    test.log("Writing " + outfile);
    file = File.open(outfile, "w")
    for (var i in lines)
        file.write(lines[i] + "\n");
    file.close();
    $outfile =~ s,/,$sep,g;
    test::log("Writing $outfile");
    open(FILE, ">:encoding(UTF-8)", $outfile) or test::fail("Failed to write $outfile");
    print FILE @lines;
    close(FILE);
    outfile = natify(File.join(Dir.getwd, File.basename(infile) + ".tmp"))
    Test.log("Writing %s" % outfile)
    File.open(outfile, "w:utf-8") do |file|
        lines.each {|line| file.write(line)}
    end
    set outfile [file nativename [file join [pwd] [file tail "$infile.tmp"]]]
    test log "Writing $outfile"
    set fh [open $outfile "w"]
    foreach line $lines {
        puts $fh $line
    }
    close $fh

Here, we write a file that has the same basename as the file we read, but with .tmp appended (e.g., before.csv.tmp), and save it into the script's current working directory. Since we write exactly the same data as we read, this file and the original should be identical. (We'll see how to check this in a later subsection.)

Just as we did when reading a file, we replace the Unix-style path separators with the path separator that is appropriate for the platform. This is done purely for the output to the test.log(message) function; if we are not showing the filename to the user we could safely use Unix path separators no matter what the platform.

How to Check the Existence of an External File

Here is an example that checks two files: the first is expected to exist and the second is not expected to exist.

    test.verify(os.path.exists(infile), "infile correctly present")
    test.verify(not os.path.exists(outfile), "outfile sucessfully deleted")
    test.verify(File.exists(infile), "infile correctly present");
    test.verify(!File.exists(outfile), "outfile sucessfully deleted");
    test::verify(-e $infile, "infile correctly present");
    test::verify(!-e $outfile, "outfile sucessfully deleted");
    Test.verify(File.exist?(infile), "infile correctly present")
    Test.verify(!File.exist?(outfile), "outfile sucessfully deleted")
    test verify [file exists $infile] "infile correctly present"
    test verify [expr ![file exists $outfile]] "outfile sucessfully deleted"

We have used the two-argument form of the Boolean test.verify(condition) function to provide more useful detail information than simply "True expression".

How to Remove an External File

Removing an external file is easy, but not reversible.

    os.remove(outfile)
    File.remove(outfile);
    unlink $outfile;
    File.delete(outfile)
    file delete $outfile

Follow this with a call to the Boolean test.verify(condition) function in conjunction with an existence test to check that the file has been removed as expected.

How to Compare External Files inside Test Scripts

To compare two external files, the easiest approach is to use the Comparing Files Squish API, but it is also possible to use the native script language features. It should not be necessary to use an external program like diff or fc.

    # Compare using Squish API:
    test.compareTextFiles(infile, outfile)

    # Compare using Python API:
    test.verify(filecmp.cmp(infile, outfile, False),
        "infile and outfile equal according to filecmp library")
    // Compare two external files:
    test.compareTextFiles(infile, outfile)
    # Compare two external files
    test::compareTextFiles($infile, $outfile);
    # Compare two external files
    Test.compareTextFiles(infile, outfile)
    # Compare two external files
    test compareTextFiles $infile $outfile

We have to make our script account for the fact that we use the fc program on Windows and the diff program on other platforms. Fortunately, both programs exhibit the same behavior: if the two files are the same they return 0 to the operating system (0 is the traditional "success" value for programs) and if the two files differ they return 1. (They may return other values, e.g., 2, if an error in the command line they are given is encountered.)

Note: It is essential that the filenames use the correct path separators for the platform. We also put the filenames in quotes, except for the Tcl example, in case they or their paths contain spaces, which is usual on Windows.

Python-specific

Python programmers can avoid using an external program and also the inefficiency of loading entire files into memory by taking advantage of the Python standard library's filecmp module. This reduces comparing two files to a single statement:

    test.verify(filecmp.cmp(infile, outfile, False),
        "infile and outfile equal according to filecmp library")

The filecmp.cmp function returns a Boolean with True indicating that the files are the same. The third parameter should always be False to avoid any risk of false-positives.

How to Read Environment Variables inside Test Scripts

Here is an example of how to read some specific environment variables that might be set when the test script is running.

    for key in ("HOME", "PATH", "MY_ENV_VAR"):
        test.log("%s = %s" % (key, os.environ.get(key)))
    var keys = ["HOME", "PATH", "MY_ENV_VAR"];
    for (i in keys)
        test.log(keys[i] + " = " + OS.getenv(keys[i]));
    for my $key ("HOME", "PATH", "MY_ENV_VAR") {
        test::log("$key = $ENV{$key}");
    }
    ENV.each {|key, value| Test.log("#{key} = #{value}") }
    global env
    foreach key {"HOME" "PATH" "MY_ENV_VAR"} {
        set value ""
        if {[catch {set value $env($key)}]} {
            # do nothing for missing key: empty default value is fine
        }
        test log "$key = $value"
    }

If you use a shell script or batch file as your AUT you can easily set some test-suite-specific environment variables which you can then access inside your test scripts using the technique shown here. See Shell Scripts and .bat-Files as AUT.) Test scripts do not have access to the AUT's environment variables, such as the ones set in the Test Suite Settings view's Environment section.

For Python, using the dict.get method ensures that we get None as the value of missing keys rather than an exception. Similarly, for Tcl we achieve the same thing by catching the exception that occurs if the looked up key is missing. For JavaScript and Perl, a missing key's value is a harmless empty value which prints as an empty string.

© 2023 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners.
The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation.
Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.