stream_style_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. package xlsx
  2. import (
  3. "bytes"
  4. "fmt"
  5. . "gopkg.in/check.v1"
  6. "io"
  7. "reflect"
  8. "time"
  9. )
  10. const (
  11. StyleStreamTestsShouldMakeRealFiles = false
  12. )
  13. type StreamStyleSuite struct{}
  14. var _ = Suite(&StreamStyleSuite{})
  15. func (s *StreamSuite) TestStreamTestsShouldMakeRealFilesShouldBeFalse(t *C) {
  16. if StyleStreamTestsShouldMakeRealFiles {
  17. t.Fatal("TestsShouldMakeRealFiles should only be true for local debugging. Don't forget to switch back before commiting.")
  18. }
  19. }
  20. func (s *StreamSuite) TestXlsxStreamWriteWithStyle(t *C) {
  21. // When shouldMakeRealFiles is set to true this test will make actual XLSX files in the file system.
  22. // This is useful to ensure files open in Excel, Numbers, Google Docs, etc.
  23. // In case of issues you can use "Open XML SDK 2.5" to diagnose issues in generated XLSX files:
  24. // https://www.microsoft.com/en-us/download/details.aspx?id=30425
  25. testCases := []struct {
  26. testName string
  27. sheetNames []string
  28. workbookData [][][]StreamCell
  29. expectedError error
  30. }{
  31. {
  32. testName: "Style Test",
  33. sheetNames: []string{
  34. "Sheet1",
  35. },
  36. workbookData: [][][]StreamCell{
  37. {
  38. {NewStyledStringStreamCell("1", UnderlinedStrings), NewStyledStringStreamCell("25", ItalicStrings),
  39. NewStyledStringStreamCell("A", BoldStrings), NewStringStreamCell("B")},
  40. {NewIntegerStreamCell(1234), NewStyledIntegerStreamCell(98, BoldIntegers),
  41. NewStyledIntegerStreamCell(34, ItalicIntegers), NewStyledIntegerStreamCell(26, UnderlinedIntegers)},
  42. },
  43. },
  44. },
  45. {
  46. testName: "One Sheet",
  47. sheetNames: []string{
  48. "Sheet1",
  49. },
  50. workbookData: [][][]StreamCell{
  51. {
  52. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  53. NewStringStreamCell("Price"), NewStringStreamCell("SKU")},
  54. {NewIntegerStreamCell(123), NewStringStreamCell("Taco"),
  55. NewIntegerStreamCell(300), NewIntegerStreamCell(123)},
  56. },
  57. },
  58. },
  59. {
  60. testName: "One Column",
  61. sheetNames: []string{
  62. "Sheet1",
  63. },
  64. workbookData: [][][]StreamCell{
  65. {
  66. {NewStringStreamCell("Token")},
  67. {NewIntegerStreamCell(123)},
  68. },
  69. },
  70. },
  71. {
  72. testName: "Several Sheets, with different numbers of columns and rows",
  73. sheetNames: []string{
  74. "Sheet 1", "Sheet 2", "Sheet3",
  75. },
  76. workbookData: [][][]StreamCell{
  77. {
  78. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  79. NewStringStreamCell("Price"), NewStringStreamCell("SKU")},
  80. {NewIntegerStreamCell(123), NewStringStreamCell("Taco"),
  81. NewIntegerStreamCell(300), NewIntegerStreamCell(123)},
  82. },
  83. {
  84. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  85. NewStringStreamCell("Price"), NewStringStreamCell("SKU"),
  86. NewStringStreamCell("Stock")},
  87. {NewIntegerStreamCell(456), NewStringStreamCell("Salsa"),
  88. NewIntegerStreamCell(200), NewIntegerStreamCell(346),
  89. NewIntegerStreamCell(1)},
  90. {NewIntegerStreamCell(789), NewStringStreamCell("Burritos"),
  91. NewIntegerStreamCell(400), NewIntegerStreamCell(754),
  92. NewIntegerStreamCell(3)},
  93. },
  94. {
  95. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  96. NewStringStreamCell("Price")},
  97. {NewIntegerStreamCell(9853), NewStringStreamCell("Guacamole"),
  98. NewIntegerStreamCell(500)},
  99. {NewIntegerStreamCell(2357), NewStringStreamCell("Margarita"),
  100. NewIntegerStreamCell(700)},
  101. },
  102. },
  103. },
  104. {
  105. testName: "Two Sheets with same the name",
  106. sheetNames: []string{
  107. "Sheet 1", "Sheet 1",
  108. },
  109. workbookData: [][][]StreamCell{
  110. {
  111. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  112. NewStringStreamCell("Price"), NewStringStreamCell("SKU")},
  113. {NewIntegerStreamCell(123), NewStringStreamCell("Taco"),
  114. NewIntegerStreamCell(300), NewIntegerStreamCell(123)},
  115. },
  116. {
  117. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  118. NewStringStreamCell("Price"), NewStringStreamCell("SKU"),
  119. NewStringStreamCell("Stock")},
  120. {NewIntegerStreamCell(456), NewStringStreamCell("Salsa"),
  121. NewIntegerStreamCell(200), NewIntegerStreamCell(346),
  122. NewIntegerStreamCell(1)},
  123. {NewIntegerStreamCell(789), NewStringStreamCell("Burritos"),
  124. NewIntegerStreamCell(400), NewIntegerStreamCell(754),
  125. NewIntegerStreamCell(3)},
  126. },
  127. },
  128. expectedError: fmt.Errorf("duplicate sheet name '%s'.", "Sheet 1"),
  129. },
  130. {
  131. testName: "One Sheet Registered, tries to write to two",
  132. sheetNames: []string{
  133. "Sheet 1",
  134. },
  135. workbookData: [][][]StreamCell{
  136. {
  137. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  138. NewStringStreamCell("Price"), NewStringStreamCell("SKU")},
  139. {NewIntegerStreamCell(123), NewStringStreamCell("Taco"),
  140. NewIntegerStreamCell(300), NewIntegerStreamCell(123)},
  141. },
  142. {
  143. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  144. NewStringStreamCell("Price"), NewStringStreamCell("SKU")},
  145. {NewIntegerStreamCell(456), NewStringStreamCell("Salsa"),
  146. NewIntegerStreamCell(200), NewIntegerStreamCell(346)},
  147. },
  148. },
  149. expectedError: AlreadyOnLastSheetError,
  150. },
  151. {
  152. testName: "One Sheet, too many columns in row 1",
  153. sheetNames: []string{
  154. "Sheet 1",
  155. },
  156. workbookData: [][][]StreamCell{
  157. {
  158. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  159. NewStringStreamCell("Price"), NewStringStreamCell("SKU")},
  160. {NewIntegerStreamCell(123), NewStringStreamCell("Taco"),
  161. NewIntegerStreamCell(300), NewIntegerStreamCell(123),
  162. NewStringStreamCell("asdf")},
  163. },
  164. },
  165. expectedError: WrongNumberOfRowsError,
  166. },
  167. {
  168. testName: "One Sheet, too few columns in row 1",
  169. sheetNames: []string{
  170. "Sheet 1",
  171. },
  172. workbookData: [][][]StreamCell{
  173. {
  174. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  175. NewStringStreamCell("Price"), NewStringStreamCell("SKU")},
  176. {NewIntegerStreamCell(123), NewStringStreamCell("Taco"),
  177. NewIntegerStreamCell(300)},
  178. },
  179. },
  180. expectedError: WrongNumberOfRowsError,
  181. },
  182. {
  183. testName: "Lots of Sheets, only writes rows to one, only writes headers to one, should not error and should still create a valid file",
  184. sheetNames: []string{
  185. "Sheet 1", "Sheet 2", "Sheet 3", "Sheet 4", "Sheet 5", "Sheet 6",
  186. },
  187. workbookData: [][][]StreamCell{
  188. {
  189. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  190. NewStringStreamCell("Price"), NewStringStreamCell("SKU")},
  191. {NewIntegerStreamCell(123), NewStringStreamCell("Taco"),
  192. NewIntegerStreamCell(300), NewIntegerStreamCell(123)},
  193. },
  194. {{}},
  195. {{NewStringStreamCell("Id"), NewStringStreamCell("Unit Cost")}},
  196. {{}},
  197. {{}},
  198. {{}},
  199. },
  200. },
  201. {
  202. testName: "Two Sheets, only writes to one, should not error and should still create a valid file",
  203. sheetNames: []string{
  204. "Sheet 1", "Sheet 2",
  205. },
  206. workbookData: [][][]StreamCell{
  207. {
  208. {NewStringStreamCell("Token"), NewStringStreamCell("Name"),
  209. NewStringStreamCell("Price"), NewStringStreamCell("SKU")},
  210. {NewIntegerStreamCell(123), NewStringStreamCell("Taco"),
  211. NewIntegerStreamCell(300), NewIntegerStreamCell(123)},
  212. },
  213. {{}},
  214. },
  215. },
  216. {
  217. testName: "UTF-8 Characters. This XLSX File loads correctly with Excel, Numbers, and Google Docs. It also passes Microsoft's Office File Format Validator.",
  218. sheetNames: []string{
  219. "Sheet1",
  220. },
  221. workbookData: [][][]StreamCell{
  222. {
  223. // String courtesy of https://github.com/minimaxir/big-list-of-naughty-strings/
  224. // Header row contains the tags that I am filtering on
  225. {NewStringStreamCell("Token"), NewStringStreamCell(endSheetDataTag),
  226. NewStringStreamCell("Price"), NewStringStreamCell(fmt.Sprintf(dimensionTag, "A1:D1"))},
  227. // Japanese and emojis
  228. {NewIntegerStreamCell(123), NewStringStreamCell("パーティーへ行かないか"),
  229. NewIntegerStreamCell(300), NewStringStreamCell("🍕🐵 🙈 🙉 🙊")},
  230. // XML encoder/parser test strings
  231. {NewIntegerStreamCell(123), NewStringStreamCell(`<?xml version="1.0" encoding="ISO-8859-1"?>`),
  232. NewIntegerStreamCell(300), NewStringStreamCell(`<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [ <!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>`)},
  233. // Upside down text and Right to Left Arabic text
  234. {NewIntegerStreamCell(123), NewStringStreamCell(`˙ɐnbᴉlɐ ɐuƃɐɯ ǝɹolop ʇǝ ǝɹoqɐl ʇn ʇunpᴉpᴉɔuᴉ ɹodɯǝʇ poɯsnᴉǝ op pǝs 'ʇᴉlǝ ƃuᴉɔsᴉdᴉpɐ ɹnʇǝʇɔǝsuoɔ 'ʇǝɯɐ ʇᴉs ɹolop ɯnsdᴉ ɯǝɹo˥
  235. 00˙Ɩ$-`), NewIntegerStreamCell(300), NewStringStreamCell(`ﷺ`)},
  236. {NewIntegerStreamCell(123), NewStringStreamCell("Taco"),
  237. NewIntegerStreamCell(300), NewIntegerStreamCell(123)},
  238. },
  239. },
  240. },
  241. }
  242. for i, testCase := range testCases {
  243. var filePath string
  244. var buffer bytes.Buffer
  245. if StyleStreamTestsShouldMakeRealFiles {
  246. filePath = fmt.Sprintf("WorkbookWithStyle%d.xlsx", i)
  247. }
  248. err := writeStreamFileWithStyle(filePath, &buffer, testCase.sheetNames, testCase.workbookData, StyleStreamTestsShouldMakeRealFiles, []StreamStyle{})
  249. if err != testCase.expectedError && err.Error() != testCase.expectedError.Error() {
  250. t.Fatalf("Error differs from expected error. Error: %v, Expected Error: %v ", err, testCase.expectedError)
  251. }
  252. if testCase.expectedError != nil {
  253. return
  254. }
  255. // read the file back with the xlsx package
  256. var bufReader *bytes.Reader
  257. var size int64
  258. if !StyleStreamTestsShouldMakeRealFiles {
  259. bufReader = bytes.NewReader(buffer.Bytes())
  260. size = bufReader.Size()
  261. }
  262. actualSheetNames, actualWorkbookData := readXLSXFile(t, filePath, bufReader, size, StyleStreamTestsShouldMakeRealFiles)
  263. // check if data was able to be read correctly
  264. if !reflect.DeepEqual(actualSheetNames, testCase.sheetNames) {
  265. t.Fatal("Expected sheet names to be equal")
  266. }
  267. expectedWorkbookDataStrings := [][][]string{}
  268. for j, _ := range testCase.workbookData {
  269. expectedWorkbookDataStrings = append(expectedWorkbookDataStrings, [][]string{})
  270. for k, _ := range testCase.workbookData[j] {
  271. expectedWorkbookDataStrings[j] = append(expectedWorkbookDataStrings[j], []string{})
  272. for _, cell := range testCase.workbookData[j][k] {
  273. expectedWorkbookDataStrings[j][k] = append(expectedWorkbookDataStrings[j][k], cell.cellData)
  274. }
  275. }
  276. }
  277. if !reflect.DeepEqual(actualWorkbookData, expectedWorkbookDataStrings) {
  278. t.Fatal("Expected workbook data to be equal")
  279. }
  280. }
  281. }
  282. // writeStreamFile will write the file using this stream package
  283. func writeStreamFileWithStyle(filePath string, fileBuffer io.Writer, sheetNames []string, workbookData [][][]StreamCell,
  284. shouldMakeRealFiles bool, customStyles []StreamStyle) error {
  285. var file *StreamFileBuilder
  286. var err error
  287. if shouldMakeRealFiles {
  288. file, err = NewStreamFileBuilderForPath(filePath)
  289. if err != nil {
  290. return err
  291. }
  292. } else {
  293. file = NewStreamFileBuilder(fileBuffer)
  294. }
  295. defaultStyles := []StreamStyle{Strings, BoldStrings, ItalicStrings, UnderlinedStrings,
  296. Integers, BoldIntegers, ItalicIntegers, UnderlinedIntegers,
  297. Dates}
  298. allStylesToBeAdded := append(defaultStyles, customStyles...)
  299. err = file.AddStreamStyleList(allStylesToBeAdded)
  300. if err != nil {
  301. return err
  302. }
  303. for i, sheetName := range sheetNames {
  304. colStyles := []StreamStyle{}
  305. for range workbookData[i][0] {
  306. colStyles = append(colStyles, Strings)
  307. }
  308. err := file.AddSheetS(sheetName, colStyles)
  309. if err != nil {
  310. return err
  311. }
  312. }
  313. streamFile, err := file.Build()
  314. if err != nil {
  315. return err
  316. }
  317. for i, sheetData := range workbookData {
  318. if i != 0 {
  319. err = streamFile.NextSheet()
  320. if err != nil {
  321. return err
  322. }
  323. }
  324. for _, row := range sheetData {
  325. //if i == 0 {
  326. // continue
  327. //}
  328. err = streamFile.WriteS(row)
  329. if err != nil {
  330. return err
  331. }
  332. }
  333. }
  334. err = streamFile.Close()
  335. if err != nil {
  336. return err
  337. }
  338. return nil
  339. }
  340. func (s *StreamSuite) TestDates(t *C) {
  341. var filePath string
  342. var buffer bytes.Buffer
  343. if StyleStreamTestsShouldMakeRealFiles {
  344. filePath = fmt.Sprintf("Workbook_Date_test.xlsx")
  345. }
  346. sheetNames := []string{"Sheet1"}
  347. workbookData := [][][]StreamCell{
  348. {
  349. {NewStringStreamCell("Date:")},
  350. {NewDateStreamCell(time.Now())},
  351. },
  352. }
  353. err := writeStreamFileWithStyle(filePath, &buffer, sheetNames, workbookData, StyleStreamTestsShouldMakeRealFiles, []StreamStyle{})
  354. if err != nil {
  355. t.Fatal("Error during writing")
  356. }
  357. }
  358. func (s *StreamSuite) TestMakeNewStylesAndUseIt(t *C) {
  359. var filePath string
  360. var buffer bytes.Buffer
  361. if StyleStreamTestsShouldMakeRealFiles {
  362. filePath = fmt.Sprintf("Workbook_newStyle.xlsx")
  363. }
  364. timesNewRoman12 := NewFont(12, TimesNewRoman)
  365. timesNewRoman12.Color = RGB_Dard_Green
  366. courier12 := NewFont(12, Courier)
  367. courier12.Color = RGB_Dark_Red
  368. greenFill := NewFill(Solid_Cell_Fill, RGB_Light_Green, RGB_White)
  369. redFill := NewFill(Solid_Cell_Fill, RGB_Light_Red, RGB_White)
  370. greenStyle := MakeStyle(0, timesNewRoman12, greenFill, DefaultAlignment(), DefaultBorder())
  371. redStyle := MakeStyle(0, courier12, redFill, DefaultAlignment(), DefaultBorder())
  372. sheetNames := []string{"Sheet1"}
  373. workbookData := [][][]StreamCell{
  374. {
  375. {NewStringStreamCell("Header1"), NewStringStreamCell("Header2")},
  376. {NewStyledStringStreamCell("Good", greenStyle), NewStyledStringStreamCell("Bad", redStyle)},
  377. },
  378. }
  379. err := writeStreamFileWithStyle(filePath, &buffer, sheetNames, workbookData, StyleStreamTestsShouldMakeRealFiles, []StreamStyle{greenStyle, redStyle})
  380. if err != nil {
  381. t.Fatal("Error during writing")
  382. }
  383. // read the file back with the xlsx package
  384. var bufReader *bytes.Reader
  385. var size int64
  386. if !StyleStreamTestsShouldMakeRealFiles {
  387. bufReader = bytes.NewReader(buffer.Bytes())
  388. size = bufReader.Size()
  389. }
  390. actualSheetNames, actualWorkbookData := readXLSXFile(t, filePath, bufReader, size, StyleStreamTestsShouldMakeRealFiles)
  391. // check if data was able to be read correctly
  392. if !reflect.DeepEqual(actualSheetNames, sheetNames) {
  393. t.Fatal("Expected sheet names to be equal")
  394. }
  395. expectedWorkbookDataStrings := [][][]string{}
  396. for j, _ := range workbookData {
  397. expectedWorkbookDataStrings = append(expectedWorkbookDataStrings, [][]string{})
  398. for k, _ := range workbookData[j] {
  399. expectedWorkbookDataStrings[j] = append(expectedWorkbookDataStrings[j], []string{})
  400. for _, cell := range workbookData[j][k] {
  401. expectedWorkbookDataStrings[j][k] = append(expectedWorkbookDataStrings[j][k], cell.cellData)
  402. }
  403. }
  404. }
  405. if !reflect.DeepEqual(actualWorkbookData, expectedWorkbookDataStrings) {
  406. t.Fatal("Expected workbook data to be equal")
  407. }
  408. }
  409. func (s *StreamSuite) TestCloseWithNothingWrittenToSheetsWithStyle(t *C) {
  410. buffer := bytes.NewBuffer(nil)
  411. file := NewStreamFileBuilder(buffer)
  412. sheetNames := []string{"Sheet1", "Sheet2"}
  413. workbookData := [][][]StreamCell{
  414. {{NewStringStreamCell("Header1"), NewStringStreamCell("Header2")}},
  415. {{NewStringStreamCell("Header3"), NewStringStreamCell("Header4")}},
  416. }
  417. defaultStyles := []StreamStyle{Strings, BoldStrings, ItalicIntegers, UnderlinedStrings,
  418. Integers, BoldIntegers, ItalicIntegers, UnderlinedIntegers,
  419. Dates}
  420. err := file.AddStreamStyleList(defaultStyles)
  421. if err != nil {
  422. t.Fatal(err)
  423. }
  424. colStyles0 := []StreamStyle{}
  425. for range workbookData[0][0] {
  426. colStyles0 = append(colStyles0, Strings)
  427. }
  428. colStyles1 := []StreamStyle{}
  429. for range workbookData[1][0] {
  430. colStyles1 = append(colStyles1, Strings)
  431. }
  432. err = file.AddSheetS(sheetNames[0], colStyles0)
  433. if err != nil {
  434. t.Fatal(err)
  435. }
  436. err = file.AddSheetS(sheetNames[1], colStyles1)
  437. if err != nil {
  438. t.Fatal(err)
  439. }
  440. stream, err := file.Build()
  441. if err != nil {
  442. t.Fatal(err)
  443. }
  444. err = stream.Close()
  445. if err != nil {
  446. t.Fatal(err)
  447. }
  448. bufReader := bytes.NewReader(buffer.Bytes())
  449. size := bufReader.Size()
  450. actualSheetNames, actualWorkbookData := readXLSXFile(t, "", bufReader, size, false)
  451. // check if data was able to be read correctly
  452. if !reflect.DeepEqual(actualSheetNames, sheetNames) {
  453. t.Fatal("Expected sheet names to be equal")
  454. }
  455. expectedWorkbookDataStrings := [][][]string{}
  456. for range workbookData {
  457. expectedWorkbookDataStrings = append(expectedWorkbookDataStrings, [][]string{})
  458. }
  459. if !reflect.DeepEqual(actualWorkbookData, expectedWorkbookDataStrings) {
  460. t.Fatal("Expected workbook data to be equal")
  461. }
  462. }
  463. func (s *StreamSuite) TestBuildErrorsAfterBuildWithStyle(t *C) {
  464. file := NewStreamFileBuilder(bytes.NewBuffer(nil))
  465. defaultStyles := []StreamStyle{Strings, BoldStrings, ItalicIntegers, UnderlinedStrings,
  466. Integers, BoldIntegers, ItalicIntegers, UnderlinedIntegers,
  467. Dates}
  468. err := file.AddStreamStyleList(defaultStyles)
  469. if err != nil {
  470. t.Fatal(err)
  471. }
  472. err = file.AddSheetS("Sheet1", []StreamStyle{Strings})
  473. if err != nil {
  474. t.Fatal(err)
  475. }
  476. err = file.AddSheetS("Sheet2", []StreamStyle{Strings})
  477. if err != nil {
  478. t.Fatal(err)
  479. }
  480. _, err = file.Build()
  481. if err != nil {
  482. t.Fatal(err)
  483. }
  484. _, err = file.Build()
  485. if err != BuiltStreamFileBuilderError {
  486. t.Fatal(err)
  487. }
  488. }
  489. func (s *StreamSuite) TestAddSheetWithStyleErrorsAfterBuild(t *C) {
  490. file := NewStreamFileBuilder(bytes.NewBuffer(nil))
  491. defaultStyles := []StreamStyle{Strings, BoldStrings, ItalicIntegers, UnderlinedStrings,
  492. Integers, BoldIntegers, ItalicIntegers, UnderlinedIntegers,
  493. Dates}
  494. err := file.AddStreamStyleList(defaultStyles)
  495. if err != nil {
  496. t.Fatal(err)
  497. }
  498. err = file.AddSheetS("Sheet1", []StreamStyle{Strings})
  499. if err != nil {
  500. t.Fatal(err)
  501. }
  502. err = file.AddSheetS("Sheet2", []StreamStyle{Strings})
  503. if err != nil {
  504. t.Fatal(err)
  505. }
  506. _, err = file.Build()
  507. if err != nil {
  508. t.Fatal(err)
  509. }
  510. err = file.AddSheetS("Sheet3", []StreamStyle{Strings})
  511. if err != BuiltStreamFileBuilderError {
  512. t.Fatal(err)
  513. }
  514. }