Improves "save as Excel" functionality (#2266)

This commit is contained in:
2023-10-11 19:32:04 -04:00
committed by GitHub
parent a3b42e43bd
commit ad0aedb0a8
15 changed files with 969 additions and 121 deletions

View File

@@ -16,14 +16,14 @@ using NUnit.Framework;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
{
public partial class SaveAsExcelFileStreamWriterHelperTests : IDisposable
public partial class SaveAsExcelFileStreamWriterHelperTests
{
private Stream _stream;
public SaveAsExcelFileStreamWriterHelperTests()
[Test]
public void DefaultSheet()
{
_stream = new MemoryStream();
using (var helper = new SaveAsExcelFileStreamWriterHelper(_stream, true))
using (var sheet = helper.AddSheet())
var stream = new MemoryStream();
using (var helper = new SaveAsExcelFileStreamWriterHelper(stream, true))
using (var sheet = helper.AddSheet(null, 7))
{
var value = new DbCellValue();
sheet.AddRow();
@@ -70,64 +70,431 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
value.RawObject = new TimeSpan(24, 00, 00);
sheet.AddCell(value);
}
ContentMatch(stream, "[Content_Types].xml");
ContentMatch(stream, "_rels/.rels");
ContentMatch(stream, "xl/_rels/workbook.xml.rels");
ContentMatch(stream, "xl/styles.xml");
ContentMatch(stream, "xl/workbook.xml");
ContentMatch(stream, "xl/worksheets/sheet1.xml");
stream.Dispose();
}
[Test]
public void SheetWithFrozenHeaderRow()
{
var stream = new MemoryStream();
using (var helper = new SaveAsExcelFileStreamWriterHelper(stream, true))
using (var sheet = helper.AddSheet(null, 7))
{
sheet.FreezeHeaderRow();
var value = new DbCellValue();
sheet.AddRow();
value.IsNull = true;
sheet.AddCell(value);
value.IsNull = false;
value.RawObject = "";
sheet.AddCell(value);
value.RawObject = "test string";
sheet.AddCell(value);
value.RawObject = 3;
sheet.AddCell(value);
value.RawObject = 3.5;
sheet.AddCell(value);
value.RawObject = false;
sheet.AddCell(value);
value.RawObject = true;
sheet.AddCell(value);
sheet.AddRow();
value.RawObject = new DateTime(1900, 2, 28);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new DateTime(1, 1, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(24, 00, 00);
sheet.AddCell(value);
}
ContentMatch(stream, "[Content_Types].xml");
ContentMatch(stream, "_rels/.rels");
ContentMatch(stream, "xl/_rels/workbook.xml.rels");
ContentMatch(stream, "xl/styles.xml");
ContentMatch(stream, "xl/workbook.xml");
ContentMatch(stream, "xl/worksheets/sheet1.xml", "xl/worksheets/sheet1-headerRowFrozen.xml");
stream.Dispose();
}
[Test]
public void SheetWithFrozenHeaderRowCalledTooLate()
{
var expectedException = new InvalidOperationException("Must be called before calling AddRow");
var actualException = new Exception();
var stream = new MemoryStream();
using (var helper = new SaveAsExcelFileStreamWriterHelper(stream, true))
using (var sheet = helper.AddSheet(null, 7))
{
sheet.AddRow();
try
{
sheet.FreezeHeaderRow();
Assert.Fail("Did not throw an exception when calling FreezeHeaderRow too late");
}
catch (Exception e)
{
actualException = e;
}
}
Assert.AreEqual(expectedException.GetType(), actualException.GetType());
Assert.AreEqual(expectedException.Message, actualException.Message);
stream.Dispose();
}
[Test]
public void SheetWithBoldHeaderRow()
{
var stream = new MemoryStream();
using (var helper = new SaveAsExcelFileStreamWriterHelper(stream, true))
using (var sheet = helper.AddSheet(null, 7))
{
var value = new DbCellValue();
sheet.AddRow();
value.IsNull = true;
sheet.AddCell(value);
value.IsNull = false;
value.RawObject = "";
sheet.AddCell(value);
value.RawObject = "test string";
sheet.AddCell(value);
value.RawObject = 3;
sheet.AddCell(value);
value.RawObject = 3.5;
sheet.AddCell(value);
value.RawObject = false;
sheet.AddCell(value);
value.RawObject = true;
sheet.AddCell(value);
sheet.AddRow();
value.RawObject = new DateTime(1900, 2, 28);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new DateTime(1, 1, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(24, 00, 00);
sheet.AddCell(value);
}
ContentMatch(stream, "[Content_Types].xml");
ContentMatch(stream, "_rels/.rels");
ContentMatch(stream, "xl/_rels/workbook.xml.rels");
ContentMatch(stream, "xl/styles.xml");
ContentMatch(stream, "xl/workbook.xml");
ContentMatch(stream, "xl/worksheets/sheet1.xml", "xl/worksheets/sheet1-headerRowBold.xml");
stream.Dispose();
}
[Test]
public void SheetWithAutoFilterEnabled()
{
var stream = new MemoryStream();
using (var helper = new SaveAsExcelFileStreamWriterHelper(stream, true))
using (var sheet = helper.AddSheet(null, 7))
{
sheet.EnableAutoFilter();
var value = new DbCellValue();
sheet.AddRow();
value.IsNull = true;
sheet.AddCell(value);
value.IsNull = false;
value.RawObject = "";
sheet.AddCell(value);
value.RawObject = "test string";
sheet.AddCell(value);
value.RawObject = 3;
sheet.AddCell(value);
value.RawObject = 3.5;
sheet.AddCell(value);
value.RawObject = false;
sheet.AddCell(value);
value.RawObject = true;
sheet.AddCell(value);
sheet.AddRow();
value.RawObject = new DateTime(1900, 2, 28);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new DateTime(1, 1, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(24, 00, 00);
sheet.AddCell(value);
}
ContentMatch(stream, "[Content_Types].xml");
ContentMatch(stream, "_rels/.rels");
ContentMatch(stream, "xl/_rels/workbook.xml.rels");
ContentMatch(stream, "xl/styles.xml");
ContentMatch(stream, "xl/workbook.xml");
ContentMatch(stream, "xl/worksheets/sheet1.xml", "xl/worksheets/sheet1-autoFilterEnabled.xml");
stream.Dispose();
}
[Test]
public void SheetWriteColumnInformationCalledTooLate()
{
var expectedException = new InvalidOperationException("Must be called before calling AddRow");
var actualException = new Exception();
var stream = new MemoryStream();
using (var helper = new SaveAsExcelFileStreamWriterHelper(stream, true))
using (var sheet = helper.AddSheet(null, 7))
{
sheet.AddRow();
try
{
sheet.WriteColumnInformation(new []{ 1F, 2F, 3F, 4F, 5F, 6F, 7F });
Assert.Fail("Did not throw an exception when calling WriteColumnInformation too late");
}
catch (Exception e)
{
actualException = e;
}
}
Assert.AreEqual(expectedException.GetType(), actualException.GetType());
Assert.AreEqual(expectedException.Message, actualException.Message);
stream.Dispose();
}
[Test]
public void SheetWriteColumnInformationWithWrongAmount()
{
var expectedException = new InvalidOperationException("Column count mismatch");
var actualException = new Exception();
var stream = new MemoryStream();
using (var helper = new SaveAsExcelFileStreamWriterHelper(stream, true))
using (var sheet = helper.AddSheet(null, 7))
{
try
{
sheet.WriteColumnInformation(new[] { 1F, 2F, 3F, 4F, 5F });
Assert.Fail("Did not throw an exception when calling WriteColumnInformation with the wrong number of columns");
}
catch (Exception e)
{
actualException = e;
}
var value = new DbCellValue();
sheet.AddRow();
value.IsNull = true;
sheet.AddCell(value);
value.IsNull = false;
value.RawObject = "";
sheet.AddCell(value);
value.RawObject = "test string";
sheet.AddCell(value);
value.RawObject = 3;
sheet.AddCell(value);
value.RawObject = 3.5;
sheet.AddCell(value);
value.RawObject = false;
sheet.AddCell(value);
value.RawObject = true;
sheet.AddCell(value);
sheet.AddRow();
value.RawObject = new DateTime(1900, 2, 28);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new DateTime(1, 1, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(24, 00, 00);
sheet.AddCell(value);
}
Assert.AreEqual(expectedException.GetType(), actualException.GetType());
Assert.AreEqual(expectedException.Message, actualException.Message);
stream.Dispose();
}
[Test]
public void SheetWriteColumnInformation()
{
var stream = new MemoryStream();
using (var helper = new SaveAsExcelFileStreamWriterHelper(stream, true))
using (var sheet = helper.AddSheet(null, 7))
{
sheet.WriteColumnInformation(new[] { 1F, 2F, 3F, 4F, 5F, 6F, 7F });
var value = new DbCellValue();
sheet.AddRow();
value.IsNull = true;
sheet.AddCell(value);
value.IsNull = false;
value.RawObject = "";
sheet.AddCell(value);
value.RawObject = "test string";
sheet.AddCell(value);
value.RawObject = 3;
sheet.AddCell(value);
value.RawObject = 3.5;
sheet.AddCell(value);
value.RawObject = false;
sheet.AddCell(value);
value.RawObject = true;
sheet.AddCell(value);
sheet.AddRow();
value.RawObject = new DateTime(1900, 2, 28);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1);
sheet.AddCell(value);
value.RawObject = new DateTime(1900, 3, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new DateTime(1, 1, 1, 15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(15, 00, 00);
sheet.AddCell(value);
value.RawObject = new TimeSpan(24, 00, 00);
sheet.AddCell(value);
}
ContentMatch(stream, "[Content_Types].xml");
ContentMatch(stream, "_rels/.rels");
ContentMatch(stream, "xl/_rels/workbook.xml.rels");
ContentMatch(stream, "xl/styles.xml");
ContentMatch(stream, "xl/workbook.xml");
ContentMatch(stream, "xl/worksheets/sheet1.xml", "xl/worksheets/sheet1-withColumns.xml");
stream.Dispose();
}
[GeneratedRegex("\\r?\\n\\s*")]
private static partial Regex GetContentRemoveLinebreakLeadingSpaceRegex();
private void ContentMatch(string fileName)
private void ContentMatch(Stream stream, string fileName)
{
ContentMatch(stream, fileName, fileName);
}
private void ContentMatch(Stream stream, string realFileName, string referenceFileName)
{
string referencePath = Path.Combine(RunEnvironmentInfo.GetTestDataLocation(),
"DataStorage",
"SaveAsExcelFileStreamWriterHelperTests",
fileName);
referenceFileName);
string referenceContent = File.ReadAllText(referencePath);
referenceContent = GetContentRemoveLinebreakLeadingSpaceRegex().Replace(referenceContent, "");
using (var zip = new ZipArchive(_stream, ZipArchiveMode.Read, true))
using (var zip = new ZipArchive(stream, ZipArchiveMode.Read, true))
{
using (var reader = new StreamReader(zip?.GetEntry(fileName)?.Open()!))
using (var reader = new StreamReader(zip?.GetEntry(realFileName)?.Open()!))
{
string realContent = reader.ReadToEnd();
Assert.AreEqual(referenceContent, realContent);
}
}
}
[Test]
public void CheckContentType()
{
ContentMatch("[Content_Types].xml");
}
[Test]
public void CheckTopRels()
{
ContentMatch("_rels/.rels");
}
[Test]
public void CheckWorkbookRels()
{
ContentMatch("xl/_rels/workbook.xml.rels");
}
[Test]
public void CheckStyles()
{
ContentMatch("xl/styles.xml");
}
[Test]
public void CheckWorkbook()
{
ContentMatch("xl/workbook.xml");
}
[Test]
public void CheckSheet1()
{
ContentMatch("xl/worksheets/sheet1.xml");
}
public void Dispose()
{
_stream.Dispose();
}
}
public class SaveAsExcelFileStreamWriterHelperReferenceManagerTests