package xlsx
import (
"encoding/xml"
"io"
"os"
"path/filepath"
. "gopkg.in/check.v1"
)
// ReaderAtCounter wraps a ReaderAt and counts the number of bytes that are read out of it
type ReaderAtCounter struct {
readerAt io.ReaderAt
bytesRead int
}
var _ io.ReaderAt = &ReaderAtCounter{}
// NewReaderAtCounter creates a ReaderAtCounter by opening the file name, and provides the size which is needed for
// opening as XLSX.
func NewReaderAtCounter(name string) (*ReaderAtCounter, int64, error) {
f, err := os.Open(name)
if err != nil {
return nil, -1, err
}
fi, err := f.Stat()
if err != nil {
f.Close()
return nil, -1, err
}
readerAtCounter := &ReaderAtCounter{
readerAt: f,
}
return readerAtCounter, fi.Size(), nil
}
func (r *ReaderAtCounter) ReadAt(p []byte, off int64) (n int, err error) {
n, err = r.readerAt.ReadAt(p, off)
r.bytesRead += n
return n, err
}
func (r *ReaderAtCounter) GetBytesRead() int {
return r.bytesRead
}
type FileSuite struct{}
var _ = Suite(&FileSuite{})
// Test we can correctly open a XSLX file and return a xlsx.File
// struct.
func (l *FileSuite) TestOpenFile(c *C) {
var xlsxFile *File
var err error
xlsxFile, err = OpenFile("./testdocs/testfile.xlsx")
c.Assert(err, IsNil)
c.Assert(xlsxFile, NotNil)
}
func (l *FileSuite) TestPartialReadsWithFewSharedStringsOnlyPartiallyReads(c *C) {
// This test verifies that a large file is only partially read when using a small row limit.
// This file is 11,228,530 bytes, but only 14,020 bytes get read out when using a row limit of 10.
// I'm specifying a limit of 20,000 to prevent test flakiness if the bytes read fluctuates with small code changes.
rowLimit := 10
// It is possible that readLimit will need to be increased by a small amount in the future, but do not increase it
// to anywhere near a significant amount of 11 million. We're testing that this number is low, to ensure that partial
// reads are fast.
readLimit := 20 * 1000
reader, size, err := NewReaderAtCounter("testdocs/large_sheet_no_shared_strings_no_dimension_tag.xlsx")
if err != nil {
c.Fatal(err)
}
file, err := OpenReaderAtWithRowLimit(reader, size, rowLimit)
if reader.bytesRead > readLimit {
// If this test begins failing, do not increase readLimit dramatically. Instead investigate why the number of
// bytes read went up and fix this issue.
c.Errorf("Reading %v rows from a sheet with ~31,000 rows and few shared strings read %v bytes, must read less than %v bytes", rowLimit, reader.bytesRead, readLimit)
}
if len(file.Sheets[0].Rows) != rowLimit {
c.Errorf("Expected sheet to have %v rows, but found %v rows", rowLimit, len(file.Sheets[0].Rows))
}
}
func (l *FileSuite) TestPartialReadsWithLargeSharedStringsOnlyPartiallyReads(c *C) {
// This test verifies that a large file is only partially read when using a small row limit.
// This file is 7,055,632 bytes, but only 1,092,839 bytes get read out when using a row limit of 10.
// I'm specifying a limit of 1.2 MB to prevent test flakiness if the bytes read fluctuates with small code changes.
// The reason that this test has a much larger limit than TestPartialReadsWithFewSharedStringsOnlyPartiallyReads
// is that this file has a Shared Strings file that is a little over 1 MB.
rowLimit := 10
// It is possible that readLimit will need to be increased by a small amount in the future, but do not increase it
// to anywhere near a significant amount of 7 million. We're testing that this number is low, to ensure that partial
// reads are fast.
readLimit := int(1.2 * 1000 * 1000)
reader, size, err := NewReaderAtCounter("testdocs/large_sheet_large_sharedstrings_dimension_tag.xlsx")
if err != nil {
c.Fatal(err)
}
file, err := OpenReaderAtWithRowLimit(reader, size, rowLimit)
if reader.bytesRead > readLimit {
// If this test begins failing, do not increase readLimit dramatically. Instead investigate why the number of
// bytes read went up and fix this issue.
c.Errorf("Reading %v rows from a sheet with ~31,000 rows and a large shared strings read %v bytes, must read less than %v bytes", rowLimit, reader.bytesRead, readLimit)
}
// This is testing that the sheet was truncated, but it is also testing that the dimension tag was ignored.
// If the dimension tag is not correctly ignored, there will be 10 rows of the data, plus ~31k empty rows tacked on.
if len(file.Sheets[0].Rows) != rowLimit {
c.Errorf("Expected sheet to have %v rows, but found %v rows", rowLimit, len(file.Sheets[0].Rows))
}
}
func (l *FileSuite) TestPartialReadsWithFewerRowsThanRequested(c *C) {
rowLimit := 10
file, err := OpenFileWithRowLimit("testdocs/testfile.xlsx", rowLimit)
if err != nil {
c.Fatal(err)
}
if len(file.Sheets[0].Rows) != 2 {
c.Errorf("Expected sheet to have %v rows, but found %v rows", 2, len(file.Sheets[0].Rows))
}
}
func (l *FileSuite) TestOpenFileWithoutStyleAndSharedStrings(c *C) {
var xlsxFile *File
var error error
xlsxFile, error = OpenFile("./testdocs/noStylesAndSharedStringsTest.xlsx")
c.Assert(error, IsNil)
c.Assert(xlsxFile, NotNil)
}
func (l *FileSuite) TestOpenFileWithChartsheet(c *C) {
xlsxFile, error := OpenFile("./testdocs/testchartsheet.xlsx")
c.Assert(error, IsNil)
c.Assert(xlsxFile, NotNil)
}
// Test that we can correctly extract a reference table from the
// sharedStrings.xml file embedded in the XLSX file and return a
// reference table of string values from it.
func (l *FileSuite) TestReadSharedStringsFromZipFile(c *C) {
var xlsxFile *File
var err error
xlsxFile, err = OpenFile("./testdocs/testfile.xlsx")
c.Assert(err, IsNil)
c.Assert(xlsxFile.referenceTable, NotNil)
}
// Helper function used to test contents of a given xlsxXf against
// expectations.
func testXf(c *C, result, expected *xlsxXf) {
c.Assert(result.ApplyAlignment, Equals, expected.ApplyAlignment)
c.Assert(result.ApplyBorder, Equals, expected.ApplyBorder)
c.Assert(result.ApplyFont, Equals, expected.ApplyFont)
c.Assert(result.ApplyFill, Equals, expected.ApplyFill)
c.Assert(result.ApplyProtection, Equals, expected.ApplyProtection)
c.Assert(result.BorderId, Equals, expected.BorderId)
c.Assert(result.FillId, Equals, expected.FillId)
c.Assert(result.FontId, Equals, expected.FontId)
c.Assert(result.NumFmtId, Equals, expected.NumFmtId)
}
// We can correctly extract a style table from the style.xml file
// embedded in the XLSX file and return a styles struct from it.
func (l *FileSuite) TestReadStylesFromZipFile(c *C) {
var xlsxFile *File
var err error
var fontCount, fillCount, borderCount, cellStyleXfCount, cellXfCount int
var font xlsxFont
var fill xlsxFill
var border xlsxBorder
var xf xlsxXf
xlsxFile, err = OpenFile("./testdocs/testfile.xlsx")
c.Assert(err, IsNil)
c.Assert(xlsxFile.styles, NotNil)
fontCount = len(xlsxFile.styles.Fonts.Font)
c.Assert(fontCount, Equals, 4)
font = xlsxFile.styles.Fonts.Font[0]
c.Assert(font.Sz.Val, Equals, "11")
c.Assert(font.Name.Val, Equals, "Calibri")
fillCount = xlsxFile.styles.Fills.Count
c.Assert(fillCount, Equals, 3)
fill = xlsxFile.styles.Fills.Fill[2]
c.Assert(fill.PatternFill.PatternType, Equals, "solid")
borderCount = xlsxFile.styles.Borders.Count
c.Assert(borderCount, Equals, 2)
border = xlsxFile.styles.Borders.Border[1]
c.Assert(border.Left.Style, Equals, "thin")
c.Assert(border.Right.Style, Equals, "thin")
c.Assert(border.Top.Style, Equals, "thin")
c.Assert(border.Bottom.Style, Equals, "thin")
cellStyleXfCount = xlsxFile.styles.CellStyleXfs.Count
c.Assert(cellStyleXfCount, Equals, 20)
xf = xlsxFile.styles.CellStyleXfs.Xf[0]
expectedXf := &xlsxXf{
ApplyAlignment: true,
ApplyBorder: true,
ApplyFont: true,
ApplyFill: false,
ApplyProtection: true,
BorderId: 0,
FillId: 0,
FontId: 0,
NumFmtId: 164}
testXf(c, &xf, expectedXf)
c.Assert(xf.Alignment, NotNil)
c.Assert(xf.Alignment.Horizontal, Equals, "general")
c.Assert(xf.Alignment.Indent, Equals, 0)
c.Assert(xf.Alignment.ShrinkToFit, Equals, false)
c.Assert(xf.Alignment.TextRotation, Equals, 0)
c.Assert(xf.Alignment.Vertical, Equals, "bottom")
c.Assert(xf.Alignment.WrapText, Equals, false)
cellXfCount = xlsxFile.styles.CellXfs.Count
c.Assert(cellXfCount, Equals, 3)
xf = xlsxFile.styles.CellXfs.Xf[0]
expectedXf = &xlsxXf{
ApplyAlignment: false,
ApplyBorder: false,
ApplyFont: false,
ApplyFill: false,
ApplyProtection: false,
BorderId: 0,
FillId: 0,
FontId: 0,
NumFmtId: 164}
testXf(c, &xf, expectedXf)
}
// We can correctly extract a map of relationship Ids to the worksheet files in
// which they are contained from the XLSX file.
func (l *FileSuite) TestReadWorkbookRelationsFromZipFile(c *C) {
var xlsxFile *File
var err error
xlsxFile, err = OpenFile("./testdocs/testfile.xlsx")
c.Assert(err, IsNil)
c.Assert(len(xlsxFile.Sheets), Equals, 3)
sheet, ok := xlsxFile.Sheet["Tabelle1"]
c.Assert(ok, Equals, true)
c.Assert(sheet, NotNil)
}
// Style information is correctly extracted from the zipped XLSX file.
func (l *FileSuite) TestGetStyleFromZipFile(c *C) {
var xlsxFile *File
var err error
var style *Style
var val string
xlsxFile, err = OpenFile("./testdocs/testfile.xlsx")
c.Assert(err, IsNil)
sheetCount := len(xlsxFile.Sheets)
c.Assert(sheetCount, Equals, 3)
tabelle1 := xlsxFile.Sheet["Tabelle1"]
row0 := tabelle1.Rows[0]
cellFoo := row0.Cells[0]
style = cellFoo.GetStyle()
if val, err = cellFoo.FormattedValue(); err != nil {
c.Error(err)
}
c.Assert(val, Equals, "Foo")
c.Assert(style.Fill.BgColor, Equals, "FF33CCCC")
c.Assert(style.ApplyFill, Equals, false)
c.Assert(style.ApplyFont, Equals, true)
row1 := tabelle1.Rows[1]
cellQuuk := row1.Cells[1]
style = cellQuuk.GetStyle()
if val, err = cellQuuk.FormattedValue(); err != nil {
c.Error(err)
}
c.Assert(val, Equals, "Quuk")
c.Assert(style.Border.Left, Equals, "thin")
c.Assert(style.ApplyBorder, Equals, true)
cellBar := row0.Cells[1]
if val, err = cellBar.FormattedValue(); err != nil {
c.Error(err)
}
c.Assert(val, Equals, "Bar")
c.Assert(cellBar.GetStyle().Fill.BgColor, Equals, "")
}
// Test we can create a File object from scratch
func (l *FileSuite) TestCreateFile(c *C) {
var xlsxFile *File
xlsxFile = NewFile()
c.Assert(xlsxFile, NotNil)
}
// Test that when we open a real XLSX file we create xlsx.Sheet
// objects for the sheets inside the file and that these sheets are
// themselves correct.
func (l *FileSuite) TestCreateSheet(c *C) {
var xlsxFile *File
var err error
var sheet *Sheet
var row *Row
xlsxFile, err = OpenFile("./testdocs/testfile.xlsx")
c.Assert(err, IsNil)
c.Assert(xlsxFile, NotNil)
sheetLen := len(xlsxFile.Sheets)
c.Assert(sheetLen, Equals, 3)
sheet = xlsxFile.Sheet["Tabelle1"]
rowLen := len(sheet.Rows)
c.Assert(rowLen, Equals, 2)
row = sheet.Rows[0]
c.Assert(len(row.Cells), Equals, 2)
cell := row.Cells[0]
if val, err := cell.FormattedValue(); err != nil {
c.Error(err)
} else {
c.Assert(val, Equals, "Foo")
}
}
// Test that we can add a sheet to a File
func (l *FileSuite) TestAddSheet(c *C) {
var f *File
f = NewFile()
sheet, err := f.AddSheet("MySheet")
c.Assert(err, IsNil)
c.Assert(sheet, NotNil)
c.Assert(len(f.Sheets), Equals, 1)
c.Assert(f.Sheet["MySheet"], Equals, sheet)
}
// Test that AddSheet returns an error if you try to add two sheets with the same name
func (l *FileSuite) TestAddSheetWithDuplicateName(c *C) {
f := NewFile()
_, err := f.AddSheet("MySheet")
c.Assert(err, IsNil)
_, err = f.AddSheet("MySheet")
c.Assert(err, ErrorMatches, "duplicate sheet name 'MySheet'.")
}
// Test that AddSheet returns an error if you try to add sheet with name as empty string
func (l *FileSuite) TestAddSheetWithEmptyName(c *C) {
f := NewFile()
_, err := f.AddSheet("")
c.Assert(err, ErrorMatches, "sheet name must be 31 or fewer characters long. It is currently '0' characters long")
}
// Test that we can append a sheet to a File
func (l *FileSuite) TestAppendSheet(c *C) {
var f *File
f = NewFile()
s := Sheet{}
sheet, err := f.AppendSheet(s, "MySheet")
c.Assert(err, IsNil)
c.Assert(sheet, NotNil)
c.Assert(len(f.Sheets), Equals, 1)
c.Assert(f.Sheet["MySheet"], Equals, sheet)
}
// Test that AppendSheet returns an error if you try to add two sheets with the same name
func (l *FileSuite) TestAppendSheetWithDuplicateName(c *C) {
f := NewFile()
s := Sheet{}
_, err := f.AppendSheet(s, "MySheet")
c.Assert(err, IsNil)
_, err = f.AppendSheet(s, "MySheet")
c.Assert(err, ErrorMatches, "duplicate sheet name 'MySheet'.")
}
// Test that we can read & create a 31 rune sheet name
func (l *FileSuite) TestMaxSheetNameLength(c *C) {
// Open a genuine xlsx created by Microsoft Excel 2007
xlsxFile, err := OpenFile("./testdocs/max_sheet_name_length.xlsx")
c.Assert(err, IsNil)
c.Assert(xlsxFile, NotNil)
c.Assert(xlsxFile.Sheets[0].Name, Equals, "αααααβββββγγγγγδδδδδεεεεεζζζζζη")
// Create a new file with the same sheet name
f := NewFile()
s, err := f.AddSheet(xlsxFile.Sheets[0].Name)
c.Assert(err, IsNil)
c.Assert(s.Name, Equals, "αααααβββββγγγγγδδδδδεεεεεζζζζζη")
}
// Test that we can get the Nth sheet
func (l *FileSuite) TestNthSheet(c *C) {
var f *File
f = NewFile()
sheet, _ := f.AddSheet("MySheet")
sheetByIndex := f.Sheets[0]
sheetByName := f.Sheet["MySheet"]
c.Assert(sheetByIndex, NotNil)
c.Assert(sheetByIndex, Equals, sheet)
c.Assert(sheetByIndex, Equals, sheetByName)
}
// Test invalid sheet name characters
func (l *FileSuite) TestInvalidSheetNameCharacters(c *C) {
f := NewFile()
for _, invalidChar := range []string{":", "\\", "/", "?", "*", "[", "]"} {
_, err := f.AddSheet(invalidChar)
c.Assert(err, NotNil)
}
}
// Test that we can create a Workbook and marshal it to XML.
func (l *FileSuite) TestMarshalWorkbook(c *C) {
var f *File
f = NewFile()
f.AddSheet("MyFirstSheet")
f.AddSheet("MySecondSheet")
workbook := f.makeWorkbook()
workbook.Sheets.Sheet[0] = xlsxSheet{
Name: "MyFirstSheet",
SheetId: "1",
Id: "rId1",
State: "visible"}
workbook.Sheets.Sheet[1] = xlsxSheet{
Name: "MySecondSheet",
SheetId: "2",
Id: "rId2",
State: "visible"}
expectedWorkbook := `
`
output, err := xml.Marshal(workbook)
c.Assert(err, IsNil)
outputStr := replaceRelationshipsNameSpace(string(output))
stringOutput := xml.Header + outputStr
c.Assert(stringOutput, Equals, expectedWorkbook)
}
// Test that we can marshall a File to a collection of xml files
func (l *FileSuite) TestMarshalFile(c *C) {
var f *File
f = NewFile()
sheet1, _ := f.AddSheet("MySheet")
row1 := sheet1.AddRow()
cell1 := row1.AddCell()
cell1.SetString("A cell!")
sheet2, _ := f.AddSheet("AnotherSheet")
row2 := sheet2.AddRow()
cell2 := row2.AddCell()
cell2.SetString("A cell!")
parts, err := f.MarshallParts()
c.Assert(err, IsNil)
c.Assert(len(parts), Equals, 11)
// sheets
expectedSheet1 := `
0
&C&"Times New Roman,Regular"&12&A&C&"Times New Roman,Regular"&12Page &P`
c.Assert(parts["xl/worksheets/sheet1.xml"], Equals, expectedSheet1)
expectedSheet2 := `
0
&C&"Times New Roman,Regular"&12&A&C&"Times New Roman,Regular"&12Page &P`
c.Assert(parts["xl/worksheets/sheet2.xml"], Equals, expectedSheet2)
// .rels.xml
expectedRels := `
`
c.Assert(parts["_rels/.rels"], Equals, expectedRels)
// app.xml
expectedApp := `
0
Go XLSX
`
c.Assert(parts["docProps/app.xml"], Equals, expectedApp)
// core.xml
expectedCore := `
`
c.Assert(parts["docProps/core.xml"], Equals, expectedCore)
// theme1.xml
expectedTheme := `
`
c.Assert(parts["xl/theme/theme1.xml"], Equals, expectedTheme)
// sharedStrings.xml
expectedXLSXSST := `
A cell!`
c.Assert(parts["xl/sharedStrings.xml"], Equals, expectedXLSXSST)
// workbook.xml.rels
expectedXLSXWorkbookRels := `
`
c.Assert(parts["xl/_rels/workbook.xml.rels"], Equals, expectedXLSXWorkbookRels)
// workbook.xml
// Note that the following XML snippet is just pasted in here to correspond to the hack
// added in file.go to support Apple Numbers so the test passes.
// `xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"`
expectedWorkbook := `
`
c.Assert(parts["xl/workbook.xml"], Equals, expectedWorkbook)
// [Content_Types].xml
expectedContentTypes := `
`
c.Assert(parts["[Content_Types].xml"], Equals, expectedContentTypes)
// styles.xml
//
// For now we only allow simple string data in the
// spreadsheet. Style support will follow.
expectedStyles := `
`
c.Assert(parts["xl/styles.xml"], Equals, expectedStyles)
}
// We can save a File as a valid XLSX file at a given path.
func (l *FileSuite) TestSaveFile(c *C) {
var tmpPath string = c.MkDir()
var f *File
f = NewFile()
sheet1, _ := f.AddSheet("MySheet")
row1 := sheet1.AddRow()
cell1 := row1.AddCell()
cell1.Value = "A cell!"
sheet2, _ := f.AddSheet("AnotherSheet")
row2 := sheet2.AddRow()
cell2 := row2.AddCell()
cell2.Value = "A cell!"
xlsxPath := filepath.Join(tmpPath, "TestSaveFile.xlsx")
err := f.Save(xlsxPath)
c.Assert(err, IsNil)
// Let's eat our own dog food
xlsxFile, err := OpenFile(xlsxPath)
c.Assert(err, IsNil)
c.Assert(xlsxFile, NotNil)
c.Assert(len(xlsxFile.Sheets), Equals, 2)
sheet1, ok := xlsxFile.Sheet["MySheet"]
c.Assert(ok, Equals, true)
c.Assert(len(sheet1.Rows), Equals, 1)
row1 = sheet1.Rows[0]
c.Assert(len(row1.Cells), Equals, 1)
cell1 = row1.Cells[0]
c.Assert(cell1.Value, Equals, "A cell!")
}
type SliceReaderSuite struct{}
var _ = Suite(&SliceReaderSuite{})
func (s *SliceReaderSuite) TestFileToSlice(c *C) {
output, err := FileToSlice("./testdocs/testfile.xlsx")
c.Assert(err, IsNil)
fileToSliceCheckOutput(c, output)
}
func (s *SliceReaderSuite) TestFileToSliceMissingCol(c *C) {
// Test xlsx file with the A column removed
_, err := FileToSlice("./testdocs/testFileToSlice.xlsx")
c.Assert(err, IsNil)
}
func (s *SliceReaderSuite) TestFileObjToSlice(c *C) {
f, err := OpenFile("./testdocs/testfile.xlsx")
output, err := f.ToSlice()
c.Assert(err, IsNil)
fileToSliceCheckOutput(c, output)
}
func fileToSliceCheckOutput(c *C, output [][][]string) {
c.Assert(len(output), Equals, 3)
c.Assert(len(output[0]), Equals, 2)
c.Assert(len(output[0][0]), Equals, 2)
c.Assert(output[0][0][0], Equals, "Foo")
c.Assert(output[0][0][1], Equals, "Bar")
c.Assert(len(output[0][1]), Equals, 2)
c.Assert(output[0][1][0], Equals, "Baz")
c.Assert(output[0][1][1], Equals, "Quuk")
c.Assert(len(output[1]), Equals, 0)
c.Assert(len(output[2]), Equals, 0)
}
func (s *SliceReaderSuite) TestFileToSliceUnmerged(c *C) {
output, err := FileToSliceUnmerged("./testdocs/testfile.xlsx")
c.Assert(err, IsNil)
fileToSliceCheckOutput(c, output)
// merged cells
output, err = FileToSliceUnmerged("./testdocs/merged_cells.xlsx")
c.Assert(err, IsNil)
c.Assert(output[0][6][2], Equals, "Happy New Year!")
c.Assert(output[0][6][1], Equals, "Happy New Year!")
c.Assert(output[0][1][0], Equals, "01.01.2016")
c.Assert(output[0][2][0], Equals, "01.01.2016")
}
func (l *FileSuite) TestReadWorkbookWithTypes(c *C) {
var xlsxFile *File
var err error
xlsxFile, err = OpenFile("./testdocs/testcelltypes.xlsx")
c.Assert(err, IsNil)
c.Assert(len(xlsxFile.Sheets), Equals, 1)
sheet := xlsxFile.Sheet["Sheet1"]
c.Assert(len(sheet.Rows), Equals, 8)
c.Assert(len(sheet.Rows[0].Cells), Equals, 2)
// string 1
c.Assert(sheet.Rows[0].Cells[0].Type(), Equals, CellTypeString)
if val, err := sheet.Rows[0].Cells[0].FormattedValue(); err != nil {
c.Error(err)
} else {
c.Assert(val, Equals, "hello world")
}
// string 2
c.Assert(sheet.Rows[1].Cells[0].Type(), Equals, CellTypeString)
if val, err := sheet.Rows[1].Cells[0].FormattedValue(); err != nil {
c.Error(err)
} else {
c.Assert(val, Equals, "日本語")
}
// integer
c.Assert(sheet.Rows[2].Cells[0].Type(), Equals, CellTypeNumeric)
intValue, _ := sheet.Rows[2].Cells[0].Int()
c.Assert(intValue, Equals, 12345)
// float
c.Assert(sheet.Rows[3].Cells[0].Type(), Equals, CellTypeNumeric)
floatValue, _ := sheet.Rows[3].Cells[0].Float()
c.Assert(floatValue, Equals, 1.024)
// Now it can't detect date
c.Assert(sheet.Rows[4].Cells[0].Type(), Equals, CellTypeNumeric)
intValue, _ = sheet.Rows[4].Cells[0].Int()
c.Assert(intValue, Equals, 40543)
// bool
c.Assert(sheet.Rows[5].Cells[0].Type(), Equals, CellTypeBool)
c.Assert(sheet.Rows[5].Cells[0].Bool(), Equals, true)
// formula
c.Assert(sheet.Rows[6].Cells[0].Type(), Equals, CellTypeNumeric)
c.Assert(sheet.Rows[6].Cells[0].Formula(), Equals, "10+20")
c.Assert(sheet.Rows[6].Cells[0].Value, Equals, "30")
// error
c.Assert(sheet.Rows[7].Cells[0].Type(), Equals, CellTypeError)
c.Assert(sheet.Rows[7].Cells[0].Formula(), Equals, "10/0")
c.Assert(sheet.Rows[7].Cells[0].Value, Equals, "#DIV/0!")
}
func (s *SliceReaderSuite) TestFileWithEmptyRows(c *C) {
f, err := OpenFile("./testdocs/empty_rows.xlsx")
c.Assert(err, IsNil)
sheet, ok := f.Sheet["EmptyRows"]
c.Assert(ok, Equals, true)
if val, err := sheet.Cell(0, 0).FormattedValue(); err != nil {
c.Error(err)
} else {
c.Assert(val, Equals, "")
}
if val, err := sheet.Cell(2, 0).FormattedValue(); err != nil {
c.Error(err)
} else {
c.Assert(val, Equals, "A3")
}
}
func (s *SliceReaderSuite) TestFileWithEmptyCols(c *C) {
f, err := OpenFile("./testdocs/empty_rows.xlsx")
c.Assert(err, IsNil)
sheet, ok := f.Sheet["EmptyCols"]
c.Assert(ok, Equals, true)
if val, err := sheet.Cell(0, 0).FormattedValue(); err != nil {
c.Error(err)
} else {
c.Assert(val, Equals, "")
}
if val, err := sheet.Cell(0, 2).FormattedValue(); err != nil {
c.Error(err)
} else {
c.Assert(val, Equals, "C1")
}
}