Skip to content

Commit c272226

Browse files
committed
Retrigger CI
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
1 parent de671ce commit c272226

6 files changed

Lines changed: 132 additions & 8 deletions

File tree

.github/workflows/functional-tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ jobs:
157157
run: |
158158
SET PATH=C:\Program Files\VFS for Git;%PATH%
159159
SET GIT_TRACE2_PERF=C:\temp\git-trace2.log
160-
ft\GVFS.FunctionalTests.exe /result:TestResult.xml --ci --slice=${{ matrix.nr }},10
160+
ft\GVFS.FunctionalTests.exe /result:TestResult.xml --ci --slice=${{ matrix.nr }},10 --workers=1
161161
162162
- name: Upload functional test results
163163
if: always() && steps.skip.outputs.result != 'true'

GVFS/GVFS.Common/Git/GitRepo.cs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ private static bool ReadLooseObjectHeader(Stream input, out long size)
172172
return true;
173173
}
174174

175-
private LooseBlobState GetLooseBlobStateAtPath(string blobPath, Action<Stream, long> writeAction, out long size)
175+
private LooseBlobState GetLooseBlobStateAtPath(string blobPath, Action<Stream, long> writeAction, out long size)
176176
{
177177
bool corruptLooseObject = false;
178178
try
@@ -191,7 +191,21 @@ private LooseBlobState GetLooseBlobStateAtPath(string blobPath, Action<Stream, l
191191
return LooseBlobState.Corrupt;
192192
}
193193

194-
writeAction?.Invoke(deflate, size);
194+
if (writeAction != null)
195+
{
196+
CountingStream counting = new CountingStream(deflate);
197+
writeAction(counting, size);
198+
199+
// .NET 10 DeflateStream silently returns partial data on truncated
200+
// zlib input instead of throwing InvalidDataException. Detect this
201+
// by comparing the bytes actually read to the size in the header.
202+
if (counting.BytesRead < size)
203+
{
204+
corruptLooseObject = true;
205+
return LooseBlobState.Corrupt;
206+
}
207+
}
208+
195209
return LooseBlobState.Exists;
196210
}
197211
}
@@ -278,5 +292,56 @@ private LooseBlobState GetLooseBlobState(string blobSha, Action<Stream, long> wr
278292

279293
return state;
280294
}
295+
296+
/// <summary>
297+
/// A read-only stream wrapper that counts the total bytes read.
298+
/// Used to detect truncated loose objects where DeflateStream returns
299+
/// fewer bytes than the header declares (see GetLooseBlobStateAtPath).
300+
/// </summary>
301+
private sealed class CountingStream : Stream
302+
{
303+
private readonly Stream inner;
304+
private long bytesRead;
305+
306+
public CountingStream(Stream inner)
307+
{
308+
this.inner = inner;
309+
}
310+
311+
public long BytesRead => this.bytesRead;
312+
313+
public override bool CanRead => true;
314+
public override bool CanSeek => false;
315+
public override bool CanWrite => false;
316+
public override long Length => this.inner.Length;
317+
public override long Position
318+
{
319+
get => this.inner.Position;
320+
set => throw new NotSupportedException();
321+
}
322+
323+
public override int Read(byte[] buffer, int offset, int count)
324+
{
325+
int read = this.inner.Read(buffer, offset, count);
326+
this.bytesRead += read;
327+
return read;
328+
}
329+
330+
public override int ReadByte()
331+
{
332+
int b = this.inner.ReadByte();
333+
if (b >= 0)
334+
{
335+
this.bytesRead++;
336+
}
337+
338+
return b;
339+
}
340+
341+
public override void Flush() => this.inner.Flush();
342+
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
343+
public override void SetLength(long value) => throw new NotSupportedException();
344+
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
345+
}
281346
}
282347
}

GVFS/GVFS.FunctionalTests/Program.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ public static void Main(string[] args)
130130
?? Properties.Settings.Default.RepoToClone;
131131

132132
RunBeforeAnyTests();
133+
Console.WriteLine("[CI-DEBUG] RunBeforeAnyTests complete, starting RunTests...");
134+
Console.Out.Flush();
133135
Environment.ExitCode = runner.RunTests(includeCategories, excludeCategories, testSlice);
134136

135137
if (Debugger.IsAttached)
@@ -141,12 +143,19 @@ public static void Main(string[] args)
141143

142144
private static void RunBeforeAnyTests()
143145
{
146+
Console.WriteLine("[CI-DEBUG] RunBeforeAnyTests: starting");
147+
Console.Out.Flush();
148+
144149
if (GVFSTestConfig.ReplaceInboxProjFS)
145150
{
146151
ProjFSFilterInstaller.ReplaceInboxProjFS();
147152
}
148153

154+
Console.WriteLine("[CI-DEBUG] Installing service...");
155+
Console.Out.Flush();
149156
GVFSServiceProcess.InstallService();
157+
Console.WriteLine("[CI-DEBUG] Service installed successfully");
158+
Console.Out.Flush();
150159

151160
string serviceProgramDataDir = GVFSPlatform.Instance.GetSecureDataRootForGVFSComponent(
152161
GVFSConstants.Service.ServiceName);
@@ -159,6 +168,9 @@ private static void RunBeforeAnyTests()
159168
Directory.CreateDirectory(serviceProgramDataDir);
160169
File.WriteAllText(statusCacheVersionTokenPath, string.Empty);
161170
}
171+
172+
Console.WriteLine("[CI-DEBUG] RunBeforeAnyTests: complete");
173+
Console.Out.Flush();
162174
}
163175
}
164176
}

GVFS/GVFS.FunctionalTests/Tools/GVFSFunctionalTestEnlistment.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,11 @@ public void DeleteEnlistment()
175175

176176
public void CloneAndMount(bool skipPrefetch)
177177
{
178+
Console.Error.WriteLine("[CI-DEBUG] CloneAndMount: starting clone of " + this.RepoUrl);
179+
Console.Error.Flush();
178180
this.gvfsProcess.Clone(this.RepoUrl, this.Commitish, skipPrefetch);
181+
Console.Error.WriteLine("[CI-DEBUG] CloneAndMount: clone complete, running git checkout");
182+
Console.Error.Flush();
179183

180184
GitProcess.Invoke(this.RepoRoot, "checkout " + this.Commitish);
181185
GitProcess.Invoke(this.RepoRoot, "branch --unset-upstream");

GVFS/GVFS.FunctionalTests/Tools/GVFSProcess.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ private string CallGVFS(string args, int expectedExitCode = DoNotCheckExitCode,
254254
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
255255
processInfo.UseShellExecute = false;
256256
processInfo.RedirectStandardOutput = true;
257+
processInfo.RedirectStandardError = true;
257258
if (standardInput != null)
258259
{
259260
processInfo.RedirectStandardInput = true;
@@ -264,6 +265,9 @@ private string CallGVFS(string args, int expectedExitCode = DoNotCheckExitCode,
264265
processInfo.EnvironmentVariables["GIT_TRACE"] = trace;
265266
}
266267

268+
Console.Error.WriteLine($"[CI-DEBUG] CallGVFS: {this.pathToGVFS} {processInfo.Arguments}");
269+
Console.Error.Flush();
270+
267271
using (Process process = Process.Start(processInfo))
268272
{
269273
if (standardInput != null)
@@ -272,8 +276,43 @@ private string CallGVFS(string args, int expectedExitCode = DoNotCheckExitCode,
272276
process.StandardInput.Close();
273277
}
274278

275-
string result = process.StandardOutput.ReadToEnd();
276-
process.WaitForExit();
279+
// Stream stderr to console in real-time
280+
process.ErrorDataReceived += (sender, e) =>
281+
{
282+
if (e.Data != null)
283+
{
284+
Console.Error.WriteLine($"[gvfs stderr] {e.Data}");
285+
Console.Error.Flush();
286+
}
287+
};
288+
process.BeginErrorReadLine();
289+
290+
// Stream stdout to console and capture it
291+
System.Text.StringBuilder outputBuilder = new System.Text.StringBuilder();
292+
process.OutputDataReceived += (sender, e) =>
293+
{
294+
if (e.Data != null)
295+
{
296+
outputBuilder.AppendLine(e.Data);
297+
Console.Error.WriteLine($"[gvfs stdout] {e.Data}");
298+
Console.Error.Flush();
299+
}
300+
};
301+
process.BeginOutputReadLine();
302+
303+
bool exited = process.WaitForExit(300000); // 5 minute timeout
304+
if (!exited)
305+
{
306+
Console.Error.WriteLine("[CI-DEBUG] CallGVFS: TIMEOUT after 5 minutes, killing process");
307+
Console.Error.Flush();
308+
process.Kill();
309+
process.WaitForExit(5000);
310+
throw new TimeoutException($"gvfs process timed out after 5 minutes. Args: {args}");
311+
}
312+
313+
string result = outputBuilder.ToString();
314+
Console.Error.WriteLine($"[CI-DEBUG] CallGVFS done: exit={process.ExitCode}");
315+
Console.Error.Flush();
277316

278317
if (expectedExitCode >= SuccessExitCode)
279318
{

GVFS/GVFS.Virtualization/FileSystemCallbacks.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -695,8 +695,12 @@ public void OnPlaceholderFileCreated(string relativePath, string sha, string tri
695695
// Note: Because OnPlaceholderFileCreated is not synchronized on all platforms it is possible that GVFS will double count
696696
// the creation of file placeholders if multiple requests for the same file are received at the same time on different
697697
// threads.
698+
//
699+
// triggeringProcessImageFileName can be null when ProjFS reports a triggering process ID of 0 (e.g. kernel or
700+
// system-level operations). The ProjFS managed API may pass null for the image file name in AOT builds.
701+
// ConcurrentDictionary does not allow null keys, so fall back to a sentinel value.
698702
this.filePlaceHolderCreationCount.AddOrUpdate(
699-
triggeringProcessImageFileName,
703+
triggeringProcessImageFileName ?? string.Empty,
700704
(imageName) => { return new PlaceHolderCreateCounter(); },
701705
(key, oldCount) => { oldCount.Increment(); return oldCount; });
702706
}
@@ -711,7 +715,7 @@ public void OnPlaceholderFolderCreated(string relativePath, string triggeringPro
711715
this.GitIndexProjection.OnPlaceholderFolderCreated(relativePath);
712716

713717
this.folderPlaceHolderCreationCount.AddOrUpdate(
714-
triggeringProcessImageFileName,
718+
triggeringProcessImageFileName ?? string.Empty,
715719
(imageName) => { return new PlaceHolderCreateCounter(); },
716720
(key, oldCount) => { oldCount.Increment(); return oldCount; });
717721
}
@@ -724,7 +728,7 @@ public void OnPlaceholderFolderExpanded(string relativePath)
724728
public void OnPlaceholderFileHydrated(string triggeringProcessImageFileName)
725729
{
726730
this.fileHydrationCount.AddOrUpdate(
727-
triggeringProcessImageFileName,
731+
triggeringProcessImageFileName ?? string.Empty,
728732
(imageName) => { return new PlaceHolderCreateCounter(); },
729733
(key, oldCount) => { oldCount.Increment(); return oldCount; });
730734
}

0 commit comments

Comments
 (0)