Rodney;3865 wrote:
1. I have troubleshooted this to your process holding the locks because it never really exits and the tests are testing an API that holds on to resources by design until the application that called it finishes. This is because the cost of using these resources is high initially, but to keep them around while the app is running is keeps it fast, and then it will clean up when the app exits. Since the app is YOURS and you never exit it never cleans up, and on the next run there is a chance they will be held by a different process.
Can you share any details about the resources being locked by your code under test, and how these resources are obtained? Perhaps I can suggest approaches that will allow the resources to be kept separate between the test processes so that they don't interfere with each other.
Rodney;3865 wrote:
2. I have been using NCrunch for almost 18 months now and all my tests are written with it in mind and that they will be running in the background and in parallel. However when it just can't be avoided I will disable parallel test execution for that solution. I have verified that it appears that running with parallelism turned off keeps the tests running better within the NCrunch world. However this is a SOLUTION wide setting and brings with it its own problems.
There is a more granular approach to controlling the concurrency between tests, using
ExclusivelyUsesAttribute. This attribute allows you to specify which of your tests can be executed concurrently, and which can not. It's also worth having a look at
SerialAttribute, which is a somewhat clumsier but simpler alternative to ExclusivelyUsesAttribute.
Rodney;3865 wrote:
2a. All tests in a solution because of this are now forced to run asynchronously.
2b. If the NCrunch processes would just expect and start again only when needed and not share anything from one run to another this would not be needed. My tests run in parallel fine, as I have implemented a feature in the API that will not delete the file, however when you switch from 32 bit to 64 bit the file has to change and it is being locked and it is only being locked in parallel mode for some reason and not in asynchronous mode. So if the process would exit like the real world apps the code being tested is expected to run in then all the problems go away.
2c. It would be nice if this could be set at a project level and not just at a solution level, as most projects are fine, and when this occurs it usually just a few tests which we could then isolated into their own test project and then flag to not be parallel if provided also at the project level.
I think there may be a way that this can be done with your present setup. Although NCrunch attributes don't yet work at project level (this is much requested and will be happening soon), you may be able to make use of the
IsolatedAttribute on the tests that need to have their process torn down on completion. Tests marked with this attribute will always be run within their own distinctive process that exists only for the lifetime of the test execution. When combined with the concurrency restrictions (either through ExclusivelyUsesAttribute or the solution-level configuration setting), I think this may solve the problems you've described.
Rodney;3865 wrote:
4. I don't see how ExclusivelyUsesAttribute applies here, READING the files in question is SHARED just fine, it is when I need to DELETE the file that it gets un-happen that another process is still holding a lock on the file. I only have to delete technically if the bit-ness changes between runs.
I guess this depends upon the timing of the operations. If the shared file can deleted anywhere within the scope of a test, then in theory this test cannot be run concurrently with any other test that may read the file. The concurrency control handled by NCrunch is granular only to test level (i.e. you can't restrict concurrency based on certain parts of the test being executed). It may be technically possible to use some kind of O/S wide mutex to control access to the file, but this would be messy and I probably wouldn't recommend it. ExclusivelyUsesAttribute is still your best bet for making sure two (or more) incompatible tests don't run at the same time. The approach that you've taken with engineering your code so that the file is not deleted during a test run is sensible and will give you far more parallelisation capability.
Rodney;3865 wrote:
I am not sure why you are resistant to at least proving a setting at the All Solutions level that would force all the processes to exit between runs, is there some low level cost that is just to expensive or not worth it that makes this impractical?
Basically, because it's expensive to build the test environment. In the processing queue, you'll notice that NCrunch breaks up the tests into separated tasks to be processed in chunks. These chunks each involve a fresh call into the test runner, making them each effectively a small test run. If the process were to exit between each test run, each of these tasks would need to build a whole fresh test environment from scratch. The cost of building the test environment is considerable - it requires JITing all assemblies in the environment, and reinitialising the test framework, along with any other system-related operations involved in building the process. If you were to attribute each of your tests with the IsolatedAttribute, you'll see exactly this behaviour, and full test cycle time increases of 500% or more are not unheard of.
It would be fair to question why the behaviour of the NCrunch engine is to break the tests up into chunks and run them as separated test runs. The reasoning behind this comes back mostly to performance and complexity. For example, it can be very difficult to steer a 3rd party test framework through a dynamic test run that is being built on the fly (as the execution order of tests is non-deterministic during parallel execution). The behaviour is so deeply embedded within the NCrunch engine that there's simply no way to change it this late in the project.
Cheers,
Remco