Thread overview
unittest which uses a disk file
Jan 16, 2019
Victor Porton
Jan 16, 2019
Victor Porton
Jan 16, 2019
H. S. Teoh
Jan 17, 2019
Victor Porton
Jan 17, 2019
H. S. Teoh
Jan 16, 2019
Neia Neutuladh
January 16, 2019
What is the rule for unittest which uses a file (containing example data for testing) available only in the source distribution, not in binary distribution?

I am writing a library.

The library has also a file main.d which is compiled only in DUB "application" configuration (I use this configuration solely for testing.)

Maybe I should put unittest { } into main.d not in the module which I test?

Also, what is the correct way to locate the file in the filesystem?
January 16, 2019
On Wednesday, 16 January 2019 at 21:07:24 UTC, Victor Porton wrote:
> What is the rule for unittest which uses a file (containing example data for testing) available only in the source distribution, not in binary distribution?
>
> I am writing a library.
>
> The library has also a file main.d which is compiled only in DUB "application" configuration (I use this configuration solely for testing.)
>
> Maybe I should put unittest { } into main.d not in the module which I test?
>
> Also, what is the correct way to locate the file in the filesystem?

Also if I choose to put any tests in main.d, should these tests be within unittest { }? main.d is anyway meant to be compiled only in unittest mode, so I'm unsure.
January 16, 2019
On Wed, Jan 16, 2019 at 09:07:24PM +0000, Victor Porton via Digitalmars-d-learn wrote:
> What is the rule for unittest which uses a file (containing example data for testing) available only in the source distribution, not in binary distribution?
> 
> I am writing a library.
> 
> The library has also a file main.d which is compiled only in DUB "application" configuration (I use this configuration solely for testing.)
> 
> Maybe I should put unittest { } into main.d not in the module which I test?
> 
> Also, what is the correct way to locate the file in the filesystem?

There is no rule about this as far as I know, but generally speaking, my advice is to avoid touching the filesystem from inside a unittest.  This is not always possible, but where possible, I highly recommend templatizing the File type so that you can substitute it with an in-memory-only proxy in your unittest.  For example, instead of:

	auto myFunc(Args...)(File fp, Args args) {
		...
		fp.rawWrite(...);
		fp.rawRead(...);
		... // etc.
	}

	unittest
	{
		auto testfile = File("testfile", "r+"); // <-- ugh
		auto r = myFunc(testfile, ...);
		...
	}

do something like this instead:

	auto myFunc(File = std.stdio.File, Args...)(File fp, Args args) {
		...
		fp.rawWrite(...);
		fp.rawRead(...);
		... // etc.
	}

	unittest
	{
		struct FakeFile {
			string fakedata = "blah blah blah";
			void[] rawRead(...) {
				// copy fakedata into output buffer,
				// etc.
			}
			... // ditto for any other method you might use
		}
		FakeFile testfile; // N.B.: no actual filesystem access
		auto r = myFunc(testfile, ...);
		...
	}

This way, you can verify your code logic using only in-memory test data, and you don't have to worry about polluting the filesystem with temporary files, cleaning up after your unittest is done, setting up paths, and all that messy stuff.

Better yet, separate your code logic so that accessing the filesystem only happens in one place (e.g., in a user-facing API that takes filenames, say), and encapsulate the file data as a generic data source, so that the main logic of your code operates on the generic data source (e.g., an input range of chars or bytes, or whatever other structure most convenient for your logic) without any specific binding to std.stdio.File.  Then it will be easy to pass in test data to your core logic without needing to use File or the FakeFile hack above.

(The FakeFile hack is really only for testing low-level functions that are actually intended to interact directly with the filesystem; generally, I try to structure my code so that the "business logic" is independent of the filesystem. It just operates on whatever abstract data source is most convenient, whether a range, or even just a string buffer, or whatever. This makes it easy to test with non-file data, and avoids importing std.stdio everywhere (which IMO is a code smell).  It also makes it easier to extend to other data sources in the future, like network data.)


T

-- 
Those who don't understand Unix are condemned to reinvent it, poorly.
January 16, 2019
On Wed, 16 Jan 2019 21:07:24 +0000, Victor Porton wrote:
> What is the rule for unittest which uses a file (containing example data for testing) available only in the source distribution, not in binary distribution?
> 
> I am writing a library.

The easy way of doing things is to define a version for your library's unittests. Like I might write:

version (EpubTest) unittest
{
    writeEbookFile("test.epub");
}

And then in dub.sdl, I'd have the test configuration add -version=EpubTest. Dub has support for this sort of thing, but I don't know the details offhand.

If you have extra dependencies or need extra environment setup, you might want to make a second dub package inside the same repository and give it a path-based dependency on your library. Normal people won't touch that second package, so you can do whatever you want there, and it's a good place to add another README.
January 17, 2019
This way I would make data duplication (data files distributed with the source and the same data embedding as strings into my D sources).

Note that the source is multilingual (I am currently working on a multi-language bindings of a C library).
January 16, 2019
On Thu, Jan 17, 2019 at 12:58:20AM +0000, Victor Porton via Digitalmars-d-learn wrote:
> This way I would make data duplication (data files distributed with the source and the same data embedding as strings into my D sources).
[...]

You could use -J and string imports, perhaps?

In any case, if you only ever need to run unittests from within a main() that never gets run by user code, then you could just version your unittests like somebody else has suggested.  Then you could just make assumptions about the current working directory and so on in your tests.


> Note that the source is multilingual (I am currently working on a multi-language bindings of a C library).

Not sure what this has to do with your question.


T

-- 
The two rules of success: 1. Don't tell everything you know. -- YHL