From 8891b0a90b551236e3f4df10c2ec2f07578ce4ce Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 20 Oct 2014 23:17:07 +0000 Subject: [PATCH] fpspreadsheet: Add test case for switched decimal and thousand separators in csv file. Fix bugs related to that. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3674 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpscsv.pas | 2 +- components/fpspreadsheet/fpspreadsheet.pas | 26 +++++---- components/fpspreadsheet/fpsutils.pas | 9 ++-- .../fpspreadsheet/tests/formattests.pas | 54 +++++++++++++------ 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/components/fpspreadsheet/fpscsv.pas b/components/fpspreadsheet/fpscsv.pas index 9ed558722..325f85999 100644 --- a/components/fpspreadsheet/fpscsv.pas +++ b/components/fpspreadsheet/fpscsv.pas @@ -561,7 +561,7 @@ begin if CSVParams.NumberFormat <> '' then s := Format(CSVParams.NumberFormat, [AValue], CSVParams.FormatSettings) else - s := FWorksheet.ReadAsUTF8Text(ACell); + s := FWorksheet.ReadAsUTF8Text(ACell, CSVParams.FormatSettings); AppendToStream(AStream, s); end; diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index f901b5a3b..c33c2da79 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -523,6 +523,7 @@ type { Reading of values } function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; overload; function ReadAsUTF8Text(ACell: PCell): ansistring; overload; + function ReadAsUTF8Text(ACell: PCell; AFormatSettings: TFormatSettings): ansistring; overload; function ReadAsNumber(ARow, ACol: Cardinal): Double; overload; function ReadAsNumber(ACell: PCell): Double; overload; function ReadAsDateTime(ARow, ACol: Cardinal; out AResult: TDateTime): Boolean; overload; @@ -2384,23 +2385,26 @@ end; @return The text representation of the cell -------------------------------------------------------------------------------} function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring; +begin + Result := ReadAsUTF8Text(ACell, FWorkbook.FormatSettings); +end; - function FloatToStrNoNaN(const Value: Double; +function TsWorksheet.ReadAsUTF8Text(ACell: PCell; + AFormatSettings: TFormatSettings): ansistring; + + function FloatToStrNoNaN(const AValue: Double; ANumberFormat: TsNumberFormat; ANumberFormatStr: string): ansistring; - var - fs: TFormatSettings; begin - fs := FWorkbook.FormatSettings; - if IsNan(Value) then + if IsNan(AValue) then Result := '' else if (ANumberFormat = nfGeneral) or (ANumberFormatStr = '') then - Result := FloatToStr(Value, fs) + Result := FloatToStr(AValue, AFormatSettings) else if (ANumberFormat = nfPercentage) then - Result := FormatFloat(ANumberFormatStr, Value*100, fs) + Result := FormatFloat(ANumberFormatStr, AValue*100, AFormatSettings) else - Result := FormatFloat(ANumberFormatStr, Value, fs) + Result := FormatFloat(ANumberFormatStr, AValue, AFormatSettings) end; function DateTimeToStrNoNaN(const Value: Double; @@ -2414,15 +2418,15 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring; if (ANumberFormat = nfGeneral) then begin if frac(Value) = 0 then // date only - ANumberFormatStr := Workbook.FormatSettings.ShortDateFormat + ANumberFormatStr := AFormatSettings.ShortDateFormat else if trunc(Value) = 0 then // time only - ANumberFormatStr := Workbook.FormatSettings.LongTimeFormat + ANumberFormatStr := AFormatSettings.LongTimeFormat else ANumberFormatStr := 'cc' end else if ANumberFormatStr = '' then ANumberFormatStr := BuildDateTimeFormatString(ANumberFormat, - Workbook.FormatSettings, ANumberFormatStr); + AFormatSettings, ANumberFormatStr); // Saw strange cases in ods where date/time formats contained pos/neg/zero parts. // Split to be on the safe side. diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index 847046fb0..75f44c4f9 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -119,8 +119,6 @@ function SpecialDateTimeFormat(ACode: String; procedure SplitFormatString(const AFormatString: String; out APositivePart, ANegativePart, AZeroPart: String); - - procedure MakeTimeIntervalMask(Src: String; var Dest: String); // These two functions are copies of fpc trunk until they are available in stable fpc. @@ -1558,10 +1556,11 @@ begin dec(i); while i >= 1 do begin - if not (AText[i] in ['0'..'9', '+', '-']) then + if not (AText[i] in ['0'..'9', '+', '-', '.', ',']) then exit; - // If we find the testSep character again it must be a thousand separator. + // If we find the testSep character again it must be a thousand separator, + // and there are no decimals. if (AText[i] = testSep) then begin // ... but only if there are 3 numerical digits in between @@ -1573,8 +1572,8 @@ begin fs.DecimalSeparator := ',' else fs.DecimalSeparator := '.'; - ADecimalSeparator := fs.DecimalSeparator; AThousandSeparator := fs.ThousandSeparator; + ADecimalSeparator := #0; // this indicates that there are no decimals done := true; i := 0; end else diff --git a/components/fpspreadsheet/tests/formattests.pas b/components/fpspreadsheet/tests/formattests.pas index 3e4818ef5..8e9df87cf 100644 --- a/components/fpspreadsheet/tests/formattests.pas +++ b/components/fpspreadsheet/tests/formattests.pas @@ -65,7 +65,8 @@ type // Test word wrapping procedure TestWriteRead_WordWrap(AFormat: TsSpreadsheetFormat); // Test number formats - procedure TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat); + procedure TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat; + AVariant: Integer = 0); // Repeat with date/times procedure TestWriteRead_DateTimeFormats(AFormat: TsSpreadsheetFormat); // Test merged cells @@ -137,13 +138,14 @@ type procedure TestWriteRead_OOXML_WordWrap; { CSV Tests } - procedure TestWriteRead_CSV_NumberFormats; + procedure TestWriteRead_CSV_NumberFormats_0; + procedure TestWriteRead_CSV_NumberFormats_1; end; implementation uses - TypInfo, fpsutils; + TypInfo, fpsutils, fpscsv; const FmtNumbersSheet = 'NumbersFormat'; //let's distinguish it from the regular numbers sheet @@ -165,14 +167,18 @@ var i: Integer; fs: TFormatSettings; myworkbook: TsWorkbook; + ch: Char; begin // Set up norm - MUST match spreadsheet cells exactly // The workbook uses a slightly modified copy of the DefaultFormatSettings // We create a copy here in order to better define the predicted strings. myWorkbook := TsWorkbook.Create; - fs := MyWorkbook.FormatSettings; - myWorkbook.Free; + try + fs := MyWorkbook.FormatSettings; + finally + myWorkbook.Free; + end; // Numbers SollNumbers[0] := 0.0; @@ -299,21 +305,33 @@ end; { --- Number format tests --- } -procedure TSpreadWriteReadFormatTests.TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat); +procedure TSpreadWriteReadFormatTests.TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat; + AVariant: Integer = 0); var MyWorksheet: TsWorksheet; MyWorkbook: TsWorkbook; ActualString: String; + ExpectedString: String; Row, Col: Integer; TempFile: string; //write xls/xml to this file and read back from it begin - {// Not needed: use workbook.writetofile with overwrite=true - if fileexists(TempFile) then - DeleteFile(TempFile); - } // Write out all test values MyWorkbook := TsWorkbook.Create; try + if (AFormat = sfCSV) then + begin + case AVariant of + 0: begin + CSVParams.FormatSettings.DecimalSeparator := MyWorkbook.FormatSettings.DecimalSeparator; + CSVParams.FormatSettings.ThousandSeparator := MyWorkbook.FormatSettings.ThousandSeparator; + end; + 1: begin // interchanged decimal and thousand separators + CSVParams.FormatSettings.ThousandSeparator := MyWorkbook.FormatSettings.DecimalSeparator; + CSVParams.FormatSettings.DecimalSeparator := MyWorkbook.FormatSettings.ThousandSeparator; + end; + end; + end; + MyWorkSheet:= MyWorkBook.AddWorksheet(FmtNumbersSheet); for Row := Low(SollNumbers) to High(SollNumbers) do for Col := ord(Low(SollNumberFormats)) to ord(High(SollNumberFormats)) do @@ -343,17 +361,18 @@ begin for Col := Low(SollNumberFormats) to High(SollNumberFormats) do begin ActualString := MyWorkSheet.ReadAsUTF8Text(Row,Col); - if (SollNumberStrings[Row,Col] <> ActualString) then + ExpectedString := SollNumberStrings[Row, Col]; + if (ExpectedString <> ActualString) then begin if (AFormat = sfCSV) and (Row=5) and (Col=0) then // CSV has an insignificant difference of tiny numbers in // general format ignore('Ignoring insignificant saved string mismatch, cell ' + CellNotation(MyWorksheet,Row,Col) + - ', expected: <' + SollNumberStrings[Row,Col] + + ', expected: <' + ExpectedString + '> but was: <' + ActualString + '>') else - CheckEquals(SollNumberStrings[Row,Col], ActualString, + CheckEquals(ExpectedString, ActualString, 'Test saved string mismatch, cell '+CellNotation(MyWorkSheet,Row,Col)); end; end; @@ -388,9 +407,14 @@ begin TestWriteRead_NumberFormats(sfOOXML); end; -procedure TSpreadWriteReadFormatTests.TestWriteRead_CSV_NumberFormats; +procedure TSpreadWriteReadFormatTests.TestWriteRead_CSV_NumberFormats_0; begin - TestWriteRead_NumberFormats(sfCSV); + TestWriteRead_NumberFormats(sfCSV, 0); +end; + +procedure TSpreadWriteReadFormatTests.TestWriteRead_CSV_NumberFormats_1; +begin + TestWriteRead_NumberFormats(sfCSV, 1); end;