Skip to content
This repository was archived by the owner on Oct 4, 2021. It is now read-only.

Commit f671515

Browse files
committed
[NUnit] Concurrent-safe AbstractResultsStore
This is a tentative fix for a concurrency case on registering unit tests. Based on the stacktrace in the bug report, this seems like the TestRecord is modified concurrently, thus causing issues on getting an item that would've been added/removed (i.e. array resize). Bug 42513 - Test result store seems to be corrupted - internal error when running many tests
1 parent 17959e5 commit f671515

File tree

1 file changed

+91
-78
lines changed

1 file changed

+91
-78
lines changed

main/src/addins/MonoDevelop.UnitTesting/Services/AbstractResultsStore.cs

Lines changed: 91 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -87,44 +87,48 @@ record = ctr;
8787
record.Results.Add (result);
8888
}
8989
}
90-
90+
9191
public UnitTestResult GetNextResult (string configuration, UnitTest test, DateTime date)
9292
{
9393
DateTime currentDate = date;
94-
TestRecord root = GetRootRecord (configuration, currentDate);
95-
if (root == null)
96-
root = GetNextRootRecord (configuration, ref currentDate);
97-
98-
while (root != null) {
99-
TestRecord tr = FindRecord (root, test.StoreRelativeName);
100-
if (tr != null && tr.Results != null) {
101-
foreach (UnitTestResult res in tr.Results) {
102-
if (res.TestDate > date)
103-
return res;
94+
lock (fileCache) {
95+
TestRecord root = GetRootRecord (configuration, currentDate);
96+
if (root == null)
97+
root = GetNextRootRecord (configuration, ref currentDate);
98+
99+
while (root != null) {
100+
TestRecord tr = FindRecord (root, test.StoreRelativeName);
101+
if (tr != null && tr.Results != null) {
102+
foreach (UnitTestResult res in tr.Results) {
103+
if (res.TestDate > date)
104+
return res;
105+
}
104106
}
107+
root = GetNextRootRecord (configuration, ref currentDate);
105108
}
106-
root = GetNextRootRecord (configuration, ref currentDate);
107109
}
108110
return null;
109111
}
110112

111113
public UnitTestResult GetPreviousResult (string configuration, UnitTest test, DateTime date)
112114
{
113115
DateTime currentDate = date;
114-
TestRecord root = GetRootRecord (configuration, currentDate);
115-
if (root == null)
116-
root = GetPreviousRootRecord (configuration, ref currentDate);
117-
118-
while (root != null) {
119-
TestRecord tr = FindRecord (root, test.StoreRelativeName);
120-
if (tr != null && tr.Results != null) {
121-
for (int n = tr.Results.Count - 1; n >= 0; n--) {
122-
UnitTestResult res = (UnitTestResult) tr.Results [n];
123-
if (res.TestDate < date)
124-
return res;
116+
lock (fileCache) {
117+
TestRecord root = GetRootRecord (configuration, currentDate);
118+
if (root == null)
119+
root = GetPreviousRootRecord (configuration, ref currentDate);
120+
121+
while (root != null) {
122+
TestRecord tr = FindRecord (root, test.StoreRelativeName);
123+
if (tr != null && tr.Results != null) {
124+
for (int n = tr.Results.Count - 1; n >= 0; n--) {
125+
UnitTestResult res = (UnitTestResult)tr.Results [n];
126+
if (res.TestDate < date)
127+
return res;
128+
}
125129
}
130+
root = GetPreviousRootRecord (configuration, ref currentDate);
126131
}
127-
root = GetPreviousRootRecord (configuration, ref currentDate);
128132
}
129133
return null;
130134
}
@@ -140,21 +144,23 @@ public UnitTestResult[] GetResults (string configuration, UnitTest test, DateTim
140144
DateTime firstDay = new DateTime (startDate.Year, startDate.Month, startDate.Day);
141145

142146
DateTime[] dates = GetStoreDates (configuration);
143-
144-
foreach (DateTime date in dates) {
145-
if (date < firstDay)
146-
continue;
147-
if (date > endDate)
148-
break;
149-
150-
TestRecord root = GetRootRecord (configuration, date);
151-
if (root == null) continue;
152147

153-
TestRecord tr = FindRecord (root, test.StoreRelativeName);
154-
if (tr != null && tr.Results != null) {
155-
foreach (UnitTestResult res in tr.Results) {
156-
if (res.TestDate >= startDate && res.TestDate <= endDate)
157-
list.Add (res);
148+
lock (fileCache) {
149+
foreach (DateTime date in dates) {
150+
if (date < firstDay)
151+
continue;
152+
if (date > endDate)
153+
break;
154+
155+
TestRecord root = GetRootRecord (configuration, date);
156+
if (root == null) continue;
157+
158+
TestRecord tr = FindRecord (root, test.StoreRelativeName);
159+
if (tr != null && tr.Results != null) {
160+
foreach (UnitTestResult res in tr.Results) {
161+
if (res.TestDate >= startDate && res.TestDate <= endDate)
162+
list.Add (res);
163+
}
158164
}
159165
}
160166
}
@@ -166,20 +172,22 @@ public UnitTestResult[] GetResultsToDate (string configuration, UnitTest test, D
166172
{
167173
ArrayList list = new ArrayList ();
168174
DateTime[] dates = GetStoreDates (configuration);
169-
170-
for (int n = dates.Length - 1; n >= 0 && list.Count < count; n--) {
171-
if (dates [n] > endDate)
172-
continue;
173-
174-
TestRecord root = GetRootRecord (configuration, dates [n]);
175-
if (root == null) continue;
176175

177-
TestRecord tr = FindRecord (root, test.StoreRelativeName);
178-
if (tr != null && tr.Results != null) {
179-
for (int m = tr.Results.Count - 1; m >= 0 && list.Count < count; m--) {
180-
UnitTestResult res = (UnitTestResult) tr.Results [m];
181-
if (res.TestDate <= endDate)
182-
list.Add (res);
176+
lock (fileCache) {
177+
for (int n = dates.Length - 1; n >= 0 && list.Count < count; n--) {
178+
if (dates [n] > endDate)
179+
continue;
180+
181+
TestRecord root = GetRootRecord (configuration, dates [n]);
182+
if (root == null) continue;
183+
184+
TestRecord tr = FindRecord (root, test.StoreRelativeName);
185+
if (tr != null && tr.Results != null) {
186+
for (int m = tr.Results.Count - 1; m >= 0 && list.Count < count; m--) {
187+
UnitTestResult res = (UnitTestResult)tr.Results [m];
188+
if (res.TestDate <= endDate)
189+
list.Add (res);
190+
}
183191
}
184192
}
185193
}
@@ -193,21 +201,24 @@ public void Save ()
193201
{
194202
if (!Directory.Exists (basePath))
195203
Directory.CreateDirectory (basePath);
196-
197-
foreach (DictionaryEntry entry in fileCache) {
198-
TestRecord record = (TestRecord) entry.Value;
199-
if (!record.Modified)
200-
continue;
201-
202-
string filePath = Path.Combine (basePath, (string)entry.Key);
203-
try {
204-
serializer.Serialize (filePath, record);
205-
record.Modified = false;
206-
} catch (Exception ex) {
207-
LoggingService.LogError (ex.ToString ());
204+
205+
lock (fileCache) {
206+
foreach (DictionaryEntry entry in fileCache) {
207+
TestRecord record = (TestRecord)entry.Value;
208+
if (!record.Modified)
209+
continue;
210+
211+
string filePath = Path.Combine (basePath, (string)entry.Key);
212+
try {
213+
serializer.Serialize (filePath, record);
214+
record.Modified = false;
215+
} catch (Exception ex) {
216+
LoggingService.LogError (ex.ToString ());
217+
}
208218
}
209219
}
210-
cachedRootList.Clear ();
220+
lock (cachedRootList)
221+
cachedRootList.Clear ();
211222
}
212223

213224
TestRecord FindRecord (TestRecord root, string aname)
@@ -313,22 +324,24 @@ DateTime[] GetStoreDates (string configuration)
313324
{
314325
if (!Directory.Exists (basePath))
315326
return new DateTime [0];
316-
317-
DateTime[] res = (DateTime[]) cachedRootList [configuration];
318-
if (res != null)
327+
328+
lock (cachedRootList) {
329+
DateTime [] res = (DateTime [])cachedRootList [configuration];
330+
if (res != null)
331+
return res;
332+
333+
var dates = new List<DateTime> ();
334+
var escapedConfiguration = EscapeFilename (configuration);
335+
foreach (string file in Directory.GetFiles (basePath, storeId + "-" + escapedConfiguration + "-*")) {
336+
try {
337+
DateTime t = ParseFileNameDate (escapedConfiguration, Path.GetFileName (file));
338+
dates.Add (t);
339+
} catch { }
340+
}
341+
res = dates.ToArray ();
342+
cachedRootList [configuration] = res;
319343
return res;
320-
321-
var dates = new List<DateTime> ();
322-
var escapedConfiguration = EscapeFilename (configuration);
323-
foreach (string file in Directory.GetFiles (basePath, storeId + "-" + escapedConfiguration + "-*")) {
324-
try {
325-
DateTime t = ParseFileNameDate (escapedConfiguration, Path.GetFileName (file));
326-
dates.Add (t);
327-
} catch { }
328344
}
329-
res = dates.ToArray ();
330-
cachedRootList [configuration] = res;
331-
return res;
332345
}
333346
}
334347

0 commit comments

Comments
 (0)