From f98f79c5d628709e643c523082a3b85d47d4e877 Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Mon, 11 Aug 2025 12:34:12 +0200 Subject: [PATCH 1/5] add unit test for case-insensitive uri comparison and update comparer logic --- .../src/System/IO/Packaging/PackUriHelper.cs | 9 ++++++-- .../tests/PartPieceTests.cs | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs index 2951cdeff40c4c..d8c14bbc4bf75e 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -601,7 +602,7 @@ private static bool IsPartNameEmpty(string partName) /// to reduce the parsing and number of allocations for Strings and Uris /// we cache the results after parsing. /// - #pragma warning disable CA1067 // Override Equals because it implements IEquatable; not overriding to avoid possible regressions in code that's working +#pragma warning disable CA1067 // Override Equals because it implements IEquatable; not overriding to avoid possible regressions in code that's working internal sealed class ValidatedPartUri : Uri, IComparable, IEquatable #pragma warning restore CA1067 { @@ -841,7 +842,11 @@ private int Compare(ValidatedPartUri? otherPartUri) return 1; //Compare the normalized uri strings for the two part uris. - return string.CompareOrdinal(NormalizedPartUriString, otherPartUri.NormalizedPartUriString); + return string.Compare( + NormalizedPartUriString, + otherPartUri.NormalizedPartUriString, + StringComparison.OrdinalIgnoreCase + ); } //------------------------------------------------------ diff --git a/src/libraries/System.IO.Packaging/tests/PartPieceTests.cs b/src/libraries/System.IO.Packaging/tests/PartPieceTests.cs index cd69cd9343ae1a..f64f3d7ded46cb 100644 --- a/src/libraries/System.IO.Packaging/tests/PartPieceTests.cs +++ b/src/libraries/System.IO.Packaging/tests/PartPieceTests.cs @@ -326,6 +326,27 @@ public void UppercaseContentTypePartPieceSequenceIsFound() Assert.NotEmpty(zipPackage.GetParts()); } + [Fact] + public void PartNamesAreCaseInsensitive() + { + using var ms = new MemoryStream(); + + using (var zipPackage = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite)) + { + zipPackage.CreatePart(new Uri("/part", UriKind.Relative), "text/plain"); + } + ms.Position = 0; + using (var zipPackage = Package.Open(ms, FileMode.Open, FileAccess.Read)) + { + var lowerPart = zipPackage.GetPart(new Uri("/part", UriKind.Relative)); + var upperPart = zipPackage.GetPart(new Uri("/PART", UriKind.Relative)); + + Assert.Same(lowerPart, upperPart); + Assert.Equal(lowerPart.Uri, upperPart.Uri); + Assert.Equal(lowerPart.ContentType, upperPart.ContentType); + } + } + [Fact] public void CanCreateAtomicPart() { From bd64339c1dfe177d59f3e10b5324c8615a16baa0 Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Mon, 11 Aug 2025 12:45:11 +0200 Subject: [PATCH 2/5] remove not needed library --- .../System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs index d8c14bbc4bf75e..36f7d27dbc6d0a 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; From 3df470519130d9ab50d1a45de69b639b115add3e Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Mon, 11 Aug 2025 15:28:26 +0200 Subject: [PATCH 3/5] add overrides to GetHashCode and Equals --- .../src/System/IO/Packaging/PackUriHelper.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs index 36f7d27dbc6d0a..7585c09b52a48f 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs @@ -653,6 +653,21 @@ bool IEquatable.Equals(ValidatedPartUri? otherPartUri) #endregion IEquatable Methods + #region Overrides + public override bool Equals(object? obj) + { + if (obj is ValidatedPartUri other) + return Compare(other) == 0; + return false; + } + + public override int GetHashCode() + { + return StringComparer.OrdinalIgnoreCase.GetHashCode(NormalizedPartUriString); + } + + #endregion Object Overrides + #region Internal Properties //------------------------------------------------------ From 88d9e3f0399e8d41f5993fa4bb6023befd925dbd Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Mon, 11 Aug 2025 18:29:16 +0200 Subject: [PATCH 4/5] fix region coding style --- .../src/System/IO/Packaging/PackUriHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs index 7585c09b52a48f..4483608bb13256 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs @@ -654,6 +654,7 @@ bool IEquatable.Equals(ValidatedPartUri? otherPartUri) #endregion IEquatable Methods #region Overrides + public override bool Equals(object? obj) { if (obj is ValidatedPartUri other) @@ -666,7 +667,7 @@ public override int GetHashCode() return StringComparer.OrdinalIgnoreCase.GetHashCode(NormalizedPartUriString); } - #endregion Object Overrides + #endregion Overrides #region Internal Properties From b6af10b88350e2a95e0965b9d1e51d92364ec95f Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Tue, 19 Aug 2025 13:34:37 +0200 Subject: [PATCH 5/5] add unit test for unique parts and remove pragma --- .../src/System/IO/Packaging/PackUriHelper.cs | 2 -- .../tests/PartPieceTests.cs | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs index 4483608bb13256..4764eddffaa51b 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackUriHelper.cs @@ -601,9 +601,7 @@ private static bool IsPartNameEmpty(string partName) /// to reduce the parsing and number of allocations for Strings and Uris /// we cache the results after parsing. /// -#pragma warning disable CA1067 // Override Equals because it implements IEquatable; not overriding to avoid possible regressions in code that's working internal sealed class ValidatedPartUri : Uri, IComparable, IEquatable -#pragma warning restore CA1067 { //------------------------------------------------------ // diff --git a/src/libraries/System.IO.Packaging/tests/PartPieceTests.cs b/src/libraries/System.IO.Packaging/tests/PartPieceTests.cs index f64f3d7ded46cb..36020afba48143 100644 --- a/src/libraries/System.IO.Packaging/tests/PartPieceTests.cs +++ b/src/libraries/System.IO.Packaging/tests/PartPieceTests.cs @@ -15,7 +15,7 @@ namespace System.IO.Packaging.Tests public class PartPieceTests : FileCleanupTestBase { private delegate byte[] FileContentsGenerator(PartConstructionParameters pcp, int totalLength); - private record class PartConstructionParameters (string FullPath, bool CreateAsAtomic, bool CreateAsValidPieceSequence, bool UppercaseFileName, bool ShufflePieces, int[] PieceLengths, FileContentsGenerator PieceGenerator) + private record class PartConstructionParameters(string FullPath, bool CreateAsAtomic, bool CreateAsValidPieceSequence, bool UppercaseFileName, bool ShufflePieces, int[] PieceLengths, FileContentsGenerator PieceGenerator) { } [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] @@ -85,7 +85,7 @@ private byte[] GenerateSequentialBytes(PartConstructionParameters pcp, int total { var bytes = new byte[totalLength]; - for(int i = 0; i < totalLength; i++) + for (int i = 0; i < totalLength; i++) { bytes[i] = (byte)(i % 255); } @@ -209,7 +209,7 @@ public void InvalidPartPiecesAreNotParsable(string partPieceName) Assert.NotNull(s_ZipPackagePartPieceType); Assert.NotNull(s_TryParseZipPackagePartPiece); - Assert.False((bool)s_TryParseZipPackagePartPiece.Invoke(null, [ partPieceEntry, null ])); + Assert.False((bool)s_TryParseZipPackagePartPiece.Invoke(null, [partPieceEntry, null])); } [Theory] @@ -347,6 +347,18 @@ public void PartNamesAreCaseInsensitive() } } + [Fact] + public void DuplicatePartsDifferingOnlyByCaseAreNotAllowed() + { + using var ms = new MemoryStream(); + using (var zipPackage = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite)) + { + zipPackage.CreatePart(new Uri("/part", UriKind.Relative), "text/plain"); + Assert.Throws(() => + zipPackage.CreatePart(new Uri("/PART", UriKind.Relative), "text/plain")); + } + } + [Fact] public void CanCreateAtomicPart() {