views:

163

answers:

6

Hello,

I have an executable that I need to run some tests on in C++ - and the testing is going to take place on all of Windows, Linux and Mac OSes.

I was hoping for input on:

  • How would I interface with the previously built executable from my code? Is there some kind of command functionality that I can use? Also, since I think the commands change between OSes, I'd need some guidance in figuring out how I could structure for all three OSes.

EDIT - Interface = I need to be able to run the executable with a command line argument from my C++ code.

  • The executable when called from the commandline also ouputs some text onto a console - how would I be able to grab that ouput stream (I'd need to record those outputted values as part of my tests).

Feel free to ask me follow up questios.

Cheers!

+2  A: 

All those OSes support some form of "subprocess" calling technique, where your tester creates a new child process and executes the code under test there. You get to not only pass a command line, but also have the opportunity to attach pipes to the child process' standard input and output streams.

Unfortunately, there is no standard C++ API to create child processes. You'll have to find the appropriate API for each OS. For example, in Windows you could use the CreateProcess function: MSDN: Creating Processes (Windows).

See also Stackoverflow: How do you spawn another process in C?

Richard Walters
+2  A: 

If you use qt to develop your code, you'll find QProcess will allow you to spawn a command line program in a platform-agnostic way.

Essentially:

 QObject *parent;
 QString program = "yourcommandlineprogram";
 QStringList arguments;
 QProcess *myProcess = new QProcess(parent);
 myProcess->start(program, arguments);

You can then read from the process with various function calls such as readAllStandardOutput (), and write to the input of the process with QProcess::write(QString).

Alternatively, if you prefer Boost to Qt, Boost.Process will also let you launch processes. I confess I don't like the syntax as much...

boost::process::command_line cl("yourcommandlineprogram");
cl.argument("someargument");
boost::process::launcher l;
l.set_stdout_behavior(bp::redirect_stream);
l.set_merge_out_err(true);
l.set_work_directory(dir);
boost::process::child c = l.start(cl);

You can then work with your subprocess 'c' by using stream operators << and >> to read and write.

Nicholas M T Elliott
That sounds really good, but involving Qt at this stage of the project is probably not going to happen :(
sparkFinder
I added a note on using boost instead; as a header-only (mostly) library, it might be easier to integrate, and you won't need to reorganize any existing code.
Nicholas M T Elliott
Boost is probably the way to go in the OP case. Boost is a library, so there are no quirks in including it in any way, and it's platform agnostic.
Bruno Brant
A: 

First of all, is it possible that you simply need to want to make your original code reusable? In that case you can build it as library and link it in your new application.

If you really want to communicate with another executable then you can need start it as a subprocess of the main application. I would recommend the Process class of the Poco C++ libraries.

StackedCrooked
+1  A: 

As I understand, you want to:

  1. Spawn a new process with arguments not known at runtime.
  2. Retrieve the information printed to stdout by the new process.

Libraries such as QProcess can spawn processes, however, I would recommend doing it by hand for both Windows and MacOS/Linux as using QProcess for this case is probably overkill.

For MacOS/Linux, here's what I would do:

  1. Set up a pipe in the parent process. Set the read end of the pipe to a new file descriptor in the parent.
  2. fork.
  3. In newly created child process, set stdout (file descriptor #1) to the write end of the pipe.
  4. execvp in the newly created child process and pass the target executable along with what arguments you want to give it.
  5. From the parent process, wait for the child (optional).
  6. From the parent process, read from the file descriptor you indicated in Step 1.
advait
So why is the set of steps you've given me only Mac/Linux specific? If <unistd.h> is built into C++, the operating system shouldn't matter, right?
sparkFinder
I don't know much about Windows, but here's what I can think of off the top of my head. It all has to do with the way Windows creates new processes (a design decision WAY early on in the 80s): they consolidated `fork` and `execvp` into one function so you can't modify file descriptors in the child before it begins to run the new process.Also, I'm not sure how Windows handles pipes (or if it has them at all).
advait
A: 

Sounds like you are only planning to do functional testing at the executable level. That is not enough. If you plane to do thorough testing, you should also write unit tests. For that there is some excellent frameworks. My prefered one (by far) for C++ is BOOST::Testing.

If you control source code there is also common tricks for functional testing beside launching exe from an external process : embed functional tests. You just add an option to your program that execute tests. This is cool because tests are embedded in code and autocheck can easily be launched in any execution environment.

That means that in the test environment, as you call your program with some test dedicated arguments, nothing keeps you from going the full way and redirect the content of stdout and even check the tests results from within the program. It will make the whole testing much easier than calling from an external launcher, then analysing the results from than launcher.

kriss
The level of testing that I need is as of now at the functional level. We don't have too many classes to test, just some core functionality.
sparkFinder
Don't get me wrong. Unit testing is not something you do when you have many classes to test. The less the better. If you just have some core functionality to test, unit test is even more adapted. I still wonder what you plan to test through launching the exe. Those kind of automated tests are usually quite hard to write, running the exe being the easiest step.
kriss
A: 

Looks like a job for popen(), available on Linux, Windows, and OS X

Éric Malenfant
popen seems to be something *nix specific, not language specific - or am I mistaken?
sparkFinder
@sparkFinder: (Assuming that, by "language specific", you mean "part of the standard library") I don't think that process spawning and stream redirection are part of standard C++. But popen is not entirely unix-specific, as it is also available on Windows.
Éric Malenfant