|
1 | 1 | using System; |
| 2 | +using System.Collections.Generic; |
2 | 3 | using System.IO; |
3 | 4 | using System.IO.Compression; |
4 | 5 | using System.Linq; |
| 6 | +using System.Text; |
5 | 7 | using System.Xml.Linq; |
6 | 8 | using OfficeIMO.Visio; |
7 | 9 | using OfficeIMO.Visio.Fluent; |
@@ -170,6 +172,74 @@ public void CommentsCanBeReviewedUpdatedReopenedAndRemovedInLoadedDocuments() { |
170 | 172 | AssertCommentXml(fluentPath, "Follow-up resolved", done: true, expectedEdited: finalResolvedAt); |
171 | 173 | } |
172 | 174 |
|
| 175 | + [Fact] |
| 176 | + public void LoadRejectsCommentsPartWithTooMuchXml() { |
| 177 | + string filePath = CreateDocumentWithComment(); |
| 178 | + string oversizedCommentText = new('x', checked((int)VisioDocument.MaxCommentsXmlCharacters)); |
| 179 | + ReplaceCommentsXml(filePath, CreateCommentsXml(oversizedCommentText)); |
| 180 | + |
| 181 | + Assert.ThrowsAny<System.Xml.XmlException>(() => VisioDocument.Load(filePath)); |
| 182 | + } |
| 183 | + |
| 184 | + [Fact] |
| 185 | + public void LoadRejectsTooManyNativeComments() { |
| 186 | + string filePath = CreateDocumentWithComment(); |
| 187 | + ReplaceCommentsXml(filePath, CreateCommentsXml(Enumerable.Range(0, VisioDocument.MaxLoadedComments + 1) |
| 188 | + .Select(index => "Comment " + index.ToString()))); |
| 189 | + |
| 190 | + InvalidDataException exception = Assert.Throws<InvalidDataException>(() => VisioDocument.Load(filePath)); |
| 191 | + Assert.Contains(VisioDocument.MaxLoadedComments.ToString(), exception.Message); |
| 192 | + } |
| 193 | + |
| 194 | + [Fact] |
| 195 | + public void LoadRejectsOversizedNativeCommentText() { |
| 196 | + string filePath = CreateDocumentWithComment(); |
| 197 | + string oversizedCommentText = new('x', VisioDocument.MaxCommentTextCharacters + 1); |
| 198 | + ReplaceCommentsXml(filePath, CreateCommentsXml(oversizedCommentText)); |
| 199 | + |
| 200 | + InvalidDataException exception = Assert.Throws<InvalidDataException>(() => VisioDocument.Load(filePath)); |
| 201 | + Assert.Contains(VisioDocument.MaxCommentTextCharacters.ToString(), exception.Message); |
| 202 | + } |
| 203 | + |
| 204 | + [Fact] |
| 205 | + public void SaveRejectsTooManyNativeComments() { |
| 206 | + string filePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".vsdx"); |
| 207 | + VisioDocument document = VisioDocument.Create(filePath); |
| 208 | + VisioPage page = document.AddPage("Review", 11, 8.5); |
| 209 | + for (int index = 0; index <= VisioDocument.MaxLoadedComments; index++) { |
| 210 | + page.Comments.Add(new VisioComment("Comment " + index.ToString()) { |
| 211 | + AuthorName = "Operations", |
| 212 | + AuthorInitials = "OP" |
| 213 | + }); |
| 214 | + } |
| 215 | + |
| 216 | + InvalidDataException exception = Assert.Throws<InvalidDataException>(() => document.Save()); |
| 217 | + Assert.Contains(VisioDocument.MaxLoadedComments.ToString(), exception.Message); |
| 218 | + } |
| 219 | + |
| 220 | + [Fact] |
| 221 | + public void SaveRejectsOversizedNativeCommentText() { |
| 222 | + string filePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".vsdx"); |
| 223 | + VisioDocument document = VisioDocument.Create(filePath); |
| 224 | + VisioPage page = document.AddPage("Review", 11, 8.5); |
| 225 | + page.AddComment(new string('x', VisioDocument.MaxCommentTextCharacters + 1), "Operations", "OP"); |
| 226 | + |
| 227 | + InvalidDataException exception = Assert.Throws<InvalidDataException>(() => document.Save()); |
| 228 | + Assert.Contains(VisioDocument.MaxCommentTextCharacters.ToString(), exception.Message); |
| 229 | + } |
| 230 | + |
| 231 | + [Fact] |
| 232 | + public void SaveRejectsCommentsPartThatExceedsUtf8ByteLimit() { |
| 233 | + string filePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".vsdx"); |
| 234 | + VisioDocument document = VisioDocument.Create(filePath); |
| 235 | + VisioPage page = document.AddPage("Review", 11, 8.5); |
| 236 | + VisioComment comment = page.AddComment("Byte budget", "Operations", "OP"); |
| 237 | + comment.AuthorName = new string('\u20ac', checked((int)(VisioDocument.MaxCommentsPartBytes / 3L + 1024L))); |
| 238 | + |
| 239 | + InvalidDataException exception = Assert.Throws<InvalidDataException>(() => document.Save()); |
| 240 | + Assert.Contains(VisioDocument.MaxCommentsPartBytes.ToString(), exception.Message); |
| 241 | + } |
| 242 | + |
173 | 243 | private static void AssertNativeCommentPackage( |
174 | 244 | string filePath, |
175 | 245 | string expectedText, |
@@ -244,6 +314,49 @@ private static void AssertCommentTextAbsent(string filePath, string text) { |
244 | 314 | Assert.DoesNotContain(comments.Descendants(v + "CommentEntry"), entry => entry.Value == text); |
245 | 315 | } |
246 | 316 |
|
| 317 | + private static string CreateDocumentWithComment() { |
| 318 | + string filePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".vsdx"); |
| 319 | + VisioDocument document = VisioDocument.Create(filePath); |
| 320 | + VisioPage page = document.AddPage("Review", 11, 8.5); |
| 321 | + page.AddComment("Initial comment", "Operations", "OP"); |
| 322 | + document.Save(); |
| 323 | + return filePath; |
| 324 | + } |
| 325 | + |
| 326 | + private static void ReplaceCommentsXml(string filePath, string commentsXml) { |
| 327 | + using ZipArchive archive = ZipFile.Open(filePath, ZipArchiveMode.Update); |
| 328 | + ZipArchiveEntry entry = archive.GetEntry("visio/comments.xml") ?? throw new InvalidOperationException("Missing comments part."); |
| 329 | + entry.Delete(); |
| 330 | + ZipArchiveEntry replacement = archive.CreateEntry("visio/comments.xml", CompressionLevel.Optimal); |
| 331 | + using Stream stream = replacement.Open(); |
| 332 | + using StreamWriter writer = new(stream, new UTF8Encoding(false)); |
| 333 | + writer.Write(commentsXml); |
| 334 | + } |
| 335 | + |
| 336 | + private static string CreateCommentsXml(string commentText) { |
| 337 | + return CreateCommentsXml(new[] { commentText }); |
| 338 | + } |
| 339 | + |
| 340 | + private static string CreateCommentsXml(IEnumerable<string> commentTexts) { |
| 341 | + XNamespace v = "http://schemas.microsoft.com/office/visio/2012/main"; |
| 342 | + int id = 1; |
| 343 | + XDocument comments = new(new XElement(v + "Comments", |
| 344 | + new XAttribute("xmlns", v.NamespaceName), |
| 345 | + new XElement(v + "AuthorList", |
| 346 | + new XElement(v + "AuthorEntry", |
| 347 | + new XAttribute("ID", "1"), |
| 348 | + new XAttribute("Name", "Operations"), |
| 349 | + new XAttribute("Initials", "OP"))), |
| 350 | + new XElement(v + "CommentList", |
| 351 | + commentTexts.Select(text => new XElement(v + "CommentEntry", |
| 352 | + new XAttribute("IX", id++), |
| 353 | + new XAttribute("AuthorID", "1"), |
| 354 | + new XAttribute("PageID", "0"), |
| 355 | + text))))); |
| 356 | + |
| 357 | + return comments.ToString(SaveOptions.DisableFormatting); |
| 358 | + } |
| 359 | + |
247 | 360 | private static string FormatExpectedDate(DateTimeOffset value) { |
248 | 361 | return System.Xml.XmlConvert.ToString(value.UtcDateTime, System.Xml.XmlDateTimeSerializationMode.Utc); |
249 | 362 | } |
|
0 commit comments