file_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. package xlsx
  2. import (
  3. "encoding/xml"
  4. "path/filepath"
  5. . "gopkg.in/check.v1"
  6. )
  7. type FileSuite struct{}
  8. var _ = Suite(&FileSuite{})
  9. // Test we can correctly open a XSLX file and return a xlsx.File
  10. // struct.
  11. func (l *FileSuite) TestOpenFile(c *C) {
  12. var xlsxFile *File
  13. var error error
  14. xlsxFile, error = OpenFile("testfile.xlsx")
  15. c.Assert(error, IsNil)
  16. c.Assert(xlsxFile, NotNil)
  17. }
  18. func (l *FileSuite) TestOpenFileWithoutStyleAndSharedStrings(c *C) {
  19. var xlsxFile *File
  20. var error error
  21. xlsxFile, error = OpenFile("noStylesAndSharedStringsTest.xlsx")
  22. c.Assert(error, IsNil)
  23. c.Assert(xlsxFile, NotNil)
  24. }
  25. // Test that we can correctly extract a reference table from the
  26. // sharedStrings.xml file embedded in the XLSX file and return a
  27. // reference table of string values from it.
  28. func (l *FileSuite) TestReadSharedStringsFromZipFile(c *C) {
  29. var xlsxFile *File
  30. var err error
  31. xlsxFile, err = OpenFile("testfile.xlsx")
  32. c.Assert(err, IsNil)
  33. c.Assert(xlsxFile.referenceTable, NotNil)
  34. }
  35. // Helper function used to test contents of a given xlsxXf against
  36. // expectations.
  37. func testXf(c *C, result, expected *xlsxXf) {
  38. c.Assert(result.ApplyAlignment, Equals, expected.ApplyAlignment)
  39. c.Assert(result.ApplyBorder, Equals, expected.ApplyBorder)
  40. c.Assert(result.ApplyFont, Equals, expected.ApplyFont)
  41. c.Assert(result.ApplyFill, Equals, expected.ApplyFill)
  42. c.Assert(result.ApplyProtection, Equals, expected.ApplyProtection)
  43. c.Assert(result.BorderId, Equals, expected.BorderId)
  44. c.Assert(result.FillId, Equals, expected.FillId)
  45. c.Assert(result.FontId, Equals, expected.FontId)
  46. c.Assert(result.NumFmtId, Equals, expected.NumFmtId)
  47. }
  48. // We can correctly extract a style table from the style.xml file
  49. // embedded in the XLSX file and return a styles struct from it.
  50. func (l *FileSuite) TestReadStylesFromZipFile(c *C) {
  51. var xlsxFile *File
  52. var err error
  53. var fontCount, fillCount, borderCount, cellStyleXfCount, cellXfCount int
  54. var font xlsxFont
  55. var fill xlsxFill
  56. var border xlsxBorder
  57. var xf xlsxXf
  58. xlsxFile, err = OpenFile("testfile.xlsx")
  59. c.Assert(err, IsNil)
  60. c.Assert(xlsxFile.styles, NotNil)
  61. fontCount = len(xlsxFile.styles.Fonts)
  62. c.Assert(fontCount, Equals, 4)
  63. font = xlsxFile.styles.Fonts[0]
  64. c.Assert(font.Sz.Val, Equals, "11")
  65. c.Assert(font.Name.Val, Equals, "Calibri")
  66. fillCount = len(xlsxFile.styles.Fills)
  67. c.Assert(fillCount, Equals, 3)
  68. fill = xlsxFile.styles.Fills[2]
  69. c.Assert(fill.PatternFill.PatternType, Equals, "solid")
  70. borderCount = len(xlsxFile.styles.Borders)
  71. c.Assert(borderCount, Equals, 2)
  72. border = xlsxFile.styles.Borders[1]
  73. c.Assert(border.Left.Style, Equals, "thin")
  74. c.Assert(border.Right.Style, Equals, "thin")
  75. c.Assert(border.Top.Style, Equals, "thin")
  76. c.Assert(border.Bottom.Style, Equals, "thin")
  77. cellStyleXfCount = len(xlsxFile.styles.CellStyleXfs)
  78. c.Assert(cellStyleXfCount, Equals, 20)
  79. xf = xlsxFile.styles.CellStyleXfs[0]
  80. expectedXf := &xlsxXf{
  81. ApplyAlignment: true,
  82. ApplyBorder: true,
  83. ApplyFont: true,
  84. ApplyFill: false,
  85. ApplyProtection: true,
  86. BorderId: 0,
  87. FillId: 0,
  88. FontId: 0,
  89. NumFmtId: 164}
  90. testXf(c, &xf, expectedXf)
  91. cellXfCount = len(xlsxFile.styles.CellXfs)
  92. c.Assert(cellXfCount, Equals, 3)
  93. xf = xlsxFile.styles.CellXfs[0]
  94. expectedXf = &xlsxXf{
  95. ApplyAlignment: false,
  96. ApplyBorder: false,
  97. ApplyFont: false,
  98. ApplyFill: false,
  99. ApplyProtection: false,
  100. BorderId: 0,
  101. FillId: 0,
  102. FontId: 0,
  103. NumFmtId: 164}
  104. testXf(c, &xf, expectedXf)
  105. }
  106. // We can correctly extract a map of relationship Ids to the worksheet files in
  107. // which they are contained from the XLSX file.
  108. func (l *FileSuite) TestReadWorkbookRelationsFromZipFile(c *C) {
  109. var xlsxFile *File
  110. var err error
  111. xlsxFile, err = OpenFile("testfile.xlsx")
  112. c.Assert(err, IsNil)
  113. c.Assert(len(xlsxFile.Sheets), Equals, 3)
  114. sheet, ok := xlsxFile.Sheet["Tabelle1"]
  115. c.Assert(ok, Equals, true)
  116. c.Assert(sheet, NotNil)
  117. }
  118. // +build fudge
  119. func (l *FileSuite) TestGetStyleFromZipFile(c *C) {
  120. var xlsxFile *File
  121. var err error
  122. var style Style
  123. xlsxFile, err = OpenFile("testfile.xlsx")
  124. c.Assert(err, IsNil)
  125. sheetCount := len(xlsxFile.Sheets)
  126. c.Assert(sheetCount, Equals, 3)
  127. tabelle1 := xlsxFile.Sheet["Tabelle1"]
  128. row0 := tabelle1.Rows[0]
  129. cellFoo := row0.Cells[0]
  130. style = cellFoo.GetStyle()
  131. c.Assert(cellFoo.String(), Equals, "Foo")
  132. c.Assert(style.Fill.BgColor, Equals, "FF33CCCC")
  133. row1 := tabelle1.Rows[1]
  134. cellQuuk := row1.Cells[1]
  135. style = cellQuuk.GetStyle()
  136. c.Assert(cellQuuk.String(), Equals, "Quuk")
  137. c.Assert(style.Border.Left, Equals, "thin")
  138. cellBar := row0.Cells[1]
  139. c.Assert(cellBar.String(), Equals, "Bar")
  140. c.Assert(cellBar.GetStyle().Fill.BgColor, Equals, "")
  141. }
  142. // Test we can create a File object from scratch
  143. func (l *FileSuite) TestCreateFile(c *C) {
  144. var xlsxFile *File
  145. xlsxFile = NewFile()
  146. c.Assert(xlsxFile, NotNil)
  147. }
  148. // Test that when we open a real XLSX file we create xlsx.Sheet
  149. // objects for the sheets inside the file and that these sheets are
  150. // themselves correct.
  151. func (l *FileSuite) TestCreateSheet(c *C) {
  152. var xlsxFile *File
  153. var err error
  154. var sheet *Sheet
  155. var row *Row
  156. xlsxFile, err = OpenFile("testfile.xlsx")
  157. c.Assert(err, IsNil)
  158. c.Assert(xlsxFile, NotNil)
  159. sheetLen := len(xlsxFile.Sheets)
  160. c.Assert(sheetLen, Equals, 3)
  161. sheet = xlsxFile.Sheet["Tabelle1"]
  162. rowLen := len(sheet.Rows)
  163. c.Assert(rowLen, Equals, 2)
  164. row = sheet.Rows[0]
  165. c.Assert(len(row.Cells), Equals, 2)
  166. cell := row.Cells[0]
  167. cellstring := cell.String()
  168. c.Assert(cellstring, Equals, "Foo")
  169. }
  170. // Test that we can add a sheet to a File
  171. func (l *FileSuite) TestAddSheet(c *C) {
  172. var f *File
  173. f = NewFile()
  174. sheet := f.AddSheet("MySheet")
  175. c.Assert(sheet, NotNil)
  176. c.Assert(len(f.Sheets), Equals, 1)
  177. c.Assert(f.Sheet["MySheet"], Equals, sheet)
  178. }
  179. // Test that we can get the Nth sheet
  180. func (l *FileSuite) TestNthSheet(c *C) {
  181. var f *File
  182. f = NewFile()
  183. sheet := f.AddSheet("MySheet")
  184. sheetByIndex := f.Sheets[0]
  185. sheetByName := f.Sheet["MySheet"]
  186. c.Assert(sheetByIndex, NotNil)
  187. c.Assert(sheetByIndex, Equals, sheet)
  188. c.Assert(sheetByIndex, Equals, sheetByName)
  189. }
  190. // Test that we can create a Workbook and marshal it to XML.
  191. func (l *FileSuite) TestMarshalWorkbook(c *C) {
  192. var f *File
  193. f = NewFile()
  194. f.AddSheet("MyFirstSheet")
  195. f.AddSheet("MySecondSheet")
  196. workbook := f.makeWorkbook()
  197. workbook.Sheets.Sheet[0] = xlsxSheet{
  198. Name: "MyFirstSheet",
  199. SheetId: "1",
  200. Id: "rId1"}
  201. workbook.Sheets.Sheet[1] = xlsxSheet{
  202. Name: "MySecondSheet",
  203. SheetId: "2",
  204. Id: "rId2"}
  205. expectedWorkbook := `<?xml version="1.0" encoding="UTF-8"?>
  206. <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  207. <fileVersion appName="Go XLSX"></fileVersion>
  208. <workbookPr date1904="false"></workbookPr>
  209. <bookViews>
  210. <workbookView></workbookView>
  211. </bookViews>
  212. <sheets>
  213. <sheet name="MyFirstSheet" sheetId="1" xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="rId1"></sheet>
  214. <sheet name="MySecondSheet" sheetId="2" xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="rId2"></sheet>
  215. </sheets>
  216. <definedNames></definedNames>
  217. <calcPr></calcPr>
  218. </workbook>`
  219. output, err := xml.MarshalIndent(workbook, " ", " ")
  220. c.Assert(err, IsNil)
  221. stringOutput := xml.Header + string(output)
  222. c.Assert(stringOutput, Equals, expectedWorkbook)
  223. }
  224. // Test that we can marshall a File to a collection of xml files
  225. func (l *FileSuite) TestMarshalFile(c *C) {
  226. var f *File
  227. f = NewFile()
  228. sheet1 := f.AddSheet("MySheet")
  229. row1 := sheet1.AddRow()
  230. cell1 := row1.AddCell()
  231. cell1.Value = "A cell!"
  232. sheet2 := f.AddSheet("AnotherSheet")
  233. row2 := sheet2.AddRow()
  234. cell2 := row2.AddCell()
  235. cell2.Value = "A cell!"
  236. parts, err := f.MarshallParts()
  237. c.Assert(err, IsNil)
  238. c.Assert(len(parts), Equals, 10)
  239. // sheets
  240. expectedSheet := `<?xml version="1.0" encoding="UTF-8"?>
  241. <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  242. <dimension ref="A1:A1"></dimension>
  243. <cols></cols>
  244. <sheetData>
  245. <row r="1">
  246. <c r="A1" t="s">
  247. <v>0</v>
  248. </c>
  249. </row>
  250. </sheetData>
  251. </worksheet>`
  252. c.Assert(parts["xl/worksheets/sheet1.xml"], Equals, expectedSheet)
  253. c.Assert(parts["xl/worksheets/sheet2.xml"], Equals, expectedSheet)
  254. // .rels.xml
  255. expectedRels := `<?xml version="1.0" encoding="UTF-8"?>
  256. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  257. <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
  258. <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
  259. <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
  260. </Relationships>`
  261. c.Assert(parts["_rels/.rels"], Equals, expectedRels)
  262. // app.xml
  263. expectedApp := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  264. <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
  265. <TotalTime>0</TotalTime>
  266. <Application>Go XLSX</Application>
  267. </Properties>`
  268. c.Assert(parts["docProps/app.xml"], Equals, expectedApp)
  269. // core.xml
  270. expectedCore := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  271. <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></cp:coreProperties>`
  272. c.Assert(parts["docProps/core.xml"], Equals, expectedCore)
  273. // sharedStrings.xml
  274. expectedXLSXSST := `<?xml version="1.0" encoding="UTF-8"?>
  275. <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="1" uniqueCount="1">
  276. <si>
  277. <t>A cell!</t>
  278. </si>
  279. </sst>`
  280. c.Assert(parts["xl/sharedStrings.xml"], Equals, expectedXLSXSST)
  281. // workbook.xml.rels
  282. expectedXLSXWorkbookRels := `<?xml version="1.0" encoding="UTF-8"?>
  283. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  284. <Relationship Id="rId1" Target="worksheets/sheet1.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"></Relationship>
  285. <Relationship Id="rId2" Target="worksheets/sheet2.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"></Relationship>
  286. <Relationship Id="rId3" Target="sharedStrings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"></Relationship>
  287. <Relationship Id="rId4" Target="styles.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"></Relationship>
  288. </Relationships>`
  289. c.Assert(parts["xl/_rels/workbook.xml.rels"], Equals, expectedXLSXWorkbookRels)
  290. // workbook.xml
  291. expectedWorkbook := `<?xml version="1.0" encoding="UTF-8"?>
  292. <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  293. <fileVersion appName="Go XLSX"></fileVersion>
  294. <workbookPr date1904="false"></workbookPr>
  295. <bookViews>
  296. <workbookView></workbookView>
  297. </bookViews>
  298. <sheets>
  299. <sheet name="MySheet" sheetId="1" xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="rId1"></sheet>
  300. <sheet name="AnotherSheet" sheetId="2" xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="rId2"></sheet>
  301. </sheets>
  302. <definedNames></definedNames>
  303. <calcPr></calcPr>
  304. </workbook>`
  305. c.Assert(parts["xl/workbook.xml"], Equals, expectedWorkbook)
  306. // [Content_Types].xml
  307. expectedContentTypes := `<?xml version="1.0" encoding="UTF-8"?>
  308. <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  309. <Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"></Override>
  310. <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"></Override>
  311. <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"></Override>
  312. <Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"></Override>
  313. <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"></Override>
  314. <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"></Override>
  315. <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"></Override>
  316. <Override PartName="xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"></Override>
  317. <Override PartName="xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"></Override>
  318. <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"></Default>
  319. <Default Extension="xml" ContentType="application/xml"></Default>
  320. </Types>`
  321. c.Assert(parts["[Content_Types].xml"], Equals, expectedContentTypes)
  322. // styles.xml
  323. //
  324. // For now we only allow simple string data in the
  325. // spreadsheet. Style support will follow.
  326. expectedStyles := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  327. <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  328. </styleSheet>`
  329. c.Assert(parts["xl/styles.xml"], Equals, expectedStyles)
  330. }
  331. // We can save a File as a valid XLSX file at a given path.
  332. func (l *FileSuite) TestSaveFile(c *C) {
  333. var tmpPath string = c.MkDir()
  334. var f *File
  335. f = NewFile()
  336. sheet1 := f.AddSheet("MySheet")
  337. row1 := sheet1.AddRow()
  338. cell1 := row1.AddCell()
  339. cell1.Value = "A cell!"
  340. sheet2 := f.AddSheet("AnotherSheet")
  341. row2 := sheet2.AddRow()
  342. cell2 := row2.AddCell()
  343. cell2.Value = "A cell!"
  344. xlsxPath := filepath.Join(tmpPath, "TestSaveFile.xlsx")
  345. err := f.Save(xlsxPath)
  346. c.Assert(err, IsNil)
  347. // Let's eat our own dog food
  348. xlsxFile, err := OpenFile(xlsxPath)
  349. c.Assert(err, IsNil)
  350. c.Assert(xlsxFile, NotNil)
  351. c.Assert(len(xlsxFile.Sheets), Equals, 2)
  352. sheet1, ok := xlsxFile.Sheet["MySheet"]
  353. c.Assert(ok, Equals, true)
  354. c.Assert(len(sheet1.Rows), Equals, 1)
  355. row1 = sheet1.Rows[0]
  356. c.Assert(len(row1.Cells), Equals, 1)
  357. cell1 = row1.Cells[0]
  358. c.Assert(cell1.Value, Equals, "A cell!")
  359. }
  360. type SliceReaderSuite struct{}
  361. var _ = Suite(&SliceReaderSuite{})
  362. func (s *SliceReaderSuite) TestFileToSlice(c *C) {
  363. output, err := FileToSlice("testfile.xlsx")
  364. c.Assert(err, IsNil)
  365. fileToSliceCheckOutput(c, output)
  366. }
  367. func (s *SliceReaderSuite) TestFileObjToSlice(c *C) {
  368. f, err := OpenFile("testfile.xlsx")
  369. output, err := f.ToSlice()
  370. c.Assert(err, IsNil)
  371. fileToSliceCheckOutput(c, output)
  372. }
  373. func fileToSliceCheckOutput(c *C, output [][][]string) {
  374. c.Assert(len(output), Equals, 3)
  375. c.Assert(len(output[0]), Equals, 2)
  376. c.Assert(len(output[0][0]), Equals, 2)
  377. c.Assert(output[0][0][0], Equals, "Foo")
  378. c.Assert(output[0][0][1], Equals, "Bar")
  379. c.Assert(len(output[0][1]), Equals, 2)
  380. c.Assert(output[0][1][0], Equals, "Baz")
  381. c.Assert(output[0][1][1], Equals, "Quuk")
  382. c.Assert(len(output[1]), Equals, 0)
  383. c.Assert(len(output[2]), Equals, 0)
  384. }