Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix dictionary key collision of test run parameters. #308

Conversation

CodeCyclone
Copy link

When 'Keep Test Execution Engine Running' in VS, the TestExecutionManager is reused and tests that contain TestRunSettings will encounter key collision when being added between runs and bleedover of properties will occur.

When 'Keep Test Execution Engine Running' in VS, the TestExecutionManager is reused and tests that contain TestRunSettings will encounter key collision when being added between runs and bleedover of properties will occur.
@CodeCyclone
Copy link
Author

This is the type of errors this change fixes:
[11/3/2017 9:21:17 AM Informational] ------ Discover test started ------
[11/3/2017 9:21:17 AM Informational] ========== Discover test finished: 1 found (0:00:00.1785022) ==========
[11/3/2017 9:21:17 AM Informational] ------ Run test started ------
[11/3/2017 9:21:17 AM Error] An item with the same key has already been added.
[11/3/2017 9:21:17 AM Informational] ========== Run test finished: 1 run (0:00:00.1540039) ==========

That error can cause VSTS to fail the test step.
There are two existing unit tests to validate this area:
TestExecutionManagerTests.RunTestsForSourceShouldPassInTestRunParametersInformationAsPropertiesToTheTest
TestExecutionManagerTests.RunTestsForTestShouldPassInTestRunParametersInformationAsPropertiesToTheTest

I didn't create a new test as it required a larger change to the TestExecutionManagerTests for capturing the error text and instead validated it manually.

@CodeCyclone
Copy link
Author

image

@@ -67,9 +60,6 @@ public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrame

var isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);

// Placing this after deployment since we need information post deployment that we pass in as properties.
this.CacheSessionParameters(runContext, frameworkHandle);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would want to call CacheSessionParameters() here itself because we want to process the testRunParameters only once for a test run. Placing this call inside ExecuteTestsInSource() would re-evaluate the testRunParameters for each source and hence, regressing the performance.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem we have is because the test execution engine is left running which keeps the TestExecutionManager instance alive, there are key collisions when populating in CacheSessionParameters(). Alternatively we could change CacheSessionParameters() to use this.sessionParameters[kvp.Key] = kvp.Value which then just overwrites the parameters in the event of collision. The downside to this is you might have bleed-over of settings from a previous run which we saw when we renamed a test run parameter and still saw the old name in the TestContext.Properties collection along with the new name. Tests could behave indeterminately which is an trait you don't want in test frameworks.
We decided to load it every run because RunSettingsUtilities.GetTestRunParameters is pulling from an in-memory cache of the runsettings so it should execute very quickly and its the only sure fire way to prevent the bleed-over effect.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as below. You could choose to rename this API to SetSessionParameters if you choose to so it is explicit we reset this data here.

@@ -107,9 +97,6 @@ public void RunTests(IEnumerable<string> sources, IRunContext runContext, IFrame

bool isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);

// Placing this after deployment since we need information post deployment that we pass in as properties.
this.CacheSessionParameters(runContext, frameworkHandle);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

@@ -236,6 +223,8 @@ private void ExecuteTestsInSource(IEnumerable<TestCase> tests, IRunContext runCo

if (!filterHasError)
{
var sessionParameters = this.GetSessionParameters(runContext, testExecutionRecorder);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to CacheSessionParameters() should not be placed here. Same as above.

{
if (!string.IsNullOrEmpty(runContext?.RunSettings?.SettingsXml))
{
try
{
var testRunParameters = RunSettingsUtilities.GetTestRunParameters(runContext.RunSettings.SettingsXml);
if (testRunParameters != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To address the problem of key value already being present in the dictionary, we can clear the sessionParameters object here and keep rest of the flow as it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clearing sessionParameters would happen every time in the event of test run parameters are used, the performance would be the same with the change I propose. The only optimization would be if test run parameters are not used and the collision can't occur.
Alternatively I could do this.sessionParameters[kvp.Key] = kvp.Value instead and rename the function back to CacheSessionParameters(), keeping the original flow. But as I mentioned above you will have bleed-over of properties between runs which could have indeterminately affect the run depending on the order of execution and how the tests are designed. But the performance shouldn't be impacted at all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CodeCyclone : Across sessions it should be ok to re-calculate TestRunParameters. That could potentially even change when the runsettings is changed. But for one session we should just calculate session parameters once as opposed to doing so for every source. For 100 test assemblies in a session we would be recalculating the same data 100 times.
this.sessionParameters[kvp.Key] = kvp.Value as you stated wouldn't be the right thing to do here - the run carries forward data from the previous session that could lead to all sorts of problems.
It would be great if you could clear the data from the previous session and reset it in this API as @jayaranigarg suggested.

@jayaranigarg
Copy link
Member

@CodeCyclone : Thank you for your contribution but I believe there is some confusion in the concept of Caching testsession parameters.
We cache testsession parameters in RunTests() rather than doing it in ExecuteTestsInSource() because these parameters are constant for a run(as read from the settings file) and they do not change for each test/source. Hence, instead of re-reading the test run parameters for each source, we read them once and cache them for future use.
We have suggested some changes along with which you should also add UnitTests related to that change. Feel free to let us know if you need any assistance on this.

Copy link
Contributor

@AbhitejJohn AbhitejJohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Do let me know if you have any concerns with the proposed change.

@jayaranigarg
Copy link
Member

Fixed this issue in another PR. Punting this PR.

singhsarab pushed a commit to singhsarab/testfx that referenced this pull request Apr 8, 2019
* Fix for issue where xlftool.exe is not able to update neutral xlf file if we update any existing resource string

* nitpick

* Localize vsix

* Publish sattelite assembly
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants