@@ -180,6 +180,55 @@ mod tests {
180180 } ) ,
181181 ) ,
182182 } ,
183+ // Regression test: line without leading spaces and large negative value
184+ TestData {
185+ input: " C0000823 OBJECTRW -401552000.00\n " ,
186+ expected: (
187+ "" ,
188+ Some ( WideLine {
189+ name: "C0000823" ,
190+ first_pair: RowValuePair {
191+ row_name: "OBJECTRW" ,
192+ value: -401552000.0 ,
193+ } ,
194+ second_pair: None ,
195+ } ) ,
196+ ) ,
197+ } ,
198+ // Test case with properly formatted strict field positioning
199+ TestData {
200+ input: " X99 ROW1 -123.0\n " ,
201+ expected: (
202+ "" ,
203+ Some ( WideLine {
204+ name: "X99" ,
205+ first_pair: RowValuePair {
206+ row_name: "ROW1" ,
207+ value: -123.0 ,
208+ } ,
209+ second_pair: None ,
210+ } ) ,
211+ ) ,
212+ } ,
213+ // Test case with two values where sign character falls just before second value field
214+ // This should trigger the sign validation check for second value (line 538-546 in parse.rs)
215+ TestData {
216+ input: " X98 ROW1 100.0 ROW2 -20\n " ,
217+ expected: (
218+ "" ,
219+ Some ( WideLine {
220+ name: "X98" ,
221+ first_pair: RowValuePair {
222+ row_name: "ROW1" ,
223+ value: 100.0 ,
224+ } ,
225+ second_pair: Some ( RowValuePair {
226+ row_name: "ROW2" ,
227+ value: -20.0 ,
228+ } ) ,
229+ } ) ,
230+ ) ,
231+ } ,
183232 ] ;
184233 for case in test_cases {
185234 cfg_if:: cfg_if! {
@@ -578,6 +627,58 @@ mod tests {
578627 } ) ,
579628 ) ,
580629 } ,
630+ // Test FR (Free) bound type - no value field (line 924-932 in parse.rs)
631+ TestData {
632+ input: " FR BND1 XFREE\n " ,
633+ expected: (
634+ "" ,
635+ Some ( BoundsLine {
636+ bound_type: BoundType :: Fr ,
637+ bound_name: "BND1" ,
638+ column_name: "XFREE" ,
639+ value: None ,
640+ } ) ,
641+ ) ,
642+ } ,
643+ // Test PL (Plus infinity) bound type - no value field (line 924-932 in parse.rs)
644+ TestData {
645+ input: " PL BND1 XPLUS\n " ,
646+ expected: (
647+ "" ,
648+ Some ( BoundsLine {
649+ bound_type: BoundType :: Pl ,
650+ bound_name: "BND1" ,
651+ column_name: "XPLUS" ,
652+ value: None ,
653+ } ) ,
654+ ) ,
655+ } ,
656+ // Test FX (Fixed) bound type with value - should trigger sign check (line 934-956 in parse.rs)
657+ TestData {
658+ input: " FX BND1 XFIXED -5\n " ,
659+ expected: (
660+ "" ,
661+ Some ( BoundsLine {
662+ bound_type: BoundType :: Fx ,
663+ bound_name: "BND1" ,
664+ column_name: "XFIXED" ,
665+ value: Some ( -5.0 ) ,
666+ } ) ,
667+ ) ,
668+ } ,
669+ // Test MI (Minus infinity) bound type with value - should trigger sign check (line 934-956 in parse.rs)
670+ TestData {
671+ input: " MI BND1 XMINUS\n " ,
672+ expected: (
673+ "" ,
674+ Some ( BoundsLine {
675+ bound_type: BoundType :: Mi ,
676+ bound_name: "BND1" ,
677+ column_name: "XMINUS" ,
678+ value: None ,
679+ } ) ,
680+ ) ,
681+ } ,
581682 ] ;
582683 for case in test_cases {
583684 cfg_if:: cfg_if! {
@@ -1832,6 +1933,114 @@ ENDATA
18321933 Ok ( ( ) )
18331934 }
18341935
1936+ /// Test sign character validation for columns lines
1937+ /// When a sign character appears just before the expected field position,
1938+ /// the strict parser should detect it and fall back to flexible parsing
1939+ #[ test]
1940+ fn test_columns_sign_validation ( ) -> Result < ( ) > {
1941+ // Test case: sign character at position L4-1 (position 22, just before value field at 23)
1942+ // L4 = 23, so position 22 should have the sign character
1943+ // Format: " COL1 ROW1 -123\n"
1944+ // Positions: 0-3 (spaces), 4-11 (COL1), 12-13 (spaces), 14-21 (ROW1), 22 (-), 23-35 (value field)
1945+ let input = " COL1 ROW1 -123\n " ;
1946+ cfg_if:: cfg_if! {
1947+ if #[ cfg( feature = "trace" ) ] {
1948+ let info = TracableInfo :: new( ) . forward( false ) . backward( false ) ;
1949+ let result = Parser :: <f32 >:: columns_line( LocatedSpan :: new_extra( input, info) ) ;
1950+ } else {
1951+ let result = Parser :: <f32 >:: columns_line( input) ;
1952+ }
1953+ }
1954+ // This should succeed with flexible parsing fallback
1955+ assert ! ( result. is_ok( ) ) ;
1956+
1957+ // Also test the second value field sign validation
1958+ // L6 = 48, so position 47 should have the sign character for second value
1959+ let input2 = " COL2 ROW1 100 ROW2 -200\n " ;
1960+ cfg_if:: cfg_if! {
1961+ if #[ cfg( feature = "trace" ) ] {
1962+ let info = TracableInfo :: new( ) . forward( false ) . backward( false ) ;
1963+ let result2 = Parser :: <f32 >:: columns_line( LocatedSpan :: new_extra( input2, info) ) ;
1964+ } else {
1965+ let result2 = Parser :: <f32 >:: columns_line( input2) ;
1966+ }
1967+ }
1968+ assert ! ( result2. is_ok( ) ) ;
1969+
1970+ Ok ( ( ) )
1971+ }
1972+
1973+ /// Test sign character validation for bounds lines
1974+ /// When a sign character appears just before the value field position,
1975+ /// the strict parser should detect it and fall back to flexible parsing
1976+ #[ test]
1977+ fn test_bounds_sign_validation ( ) -> Result < ( ) > {
1978+ // Test case: sign character at position L4-1 (just before value field)
1979+ // For bound types that require a value (not FR or PL)
1980+ let input = " UP BND1 VAR1-999.99\n " ;
1981+ cfg_if:: cfg_if! {
1982+ if #[ cfg( feature = "trace" ) ] {
1983+ let info = TracableInfo :: new( ) . forward( false ) . backward( false ) ;
1984+ let result = Parser :: <f32 >:: bounds_line( LocatedSpan :: new_extra( input, info) ) ;
1985+ } else {
1986+ let result = Parser :: <f32 >:: bounds_line( input) ;
1987+ }
1988+ }
1989+ // This should succeed with flexible parsing fallback
1990+ assert ! ( result. is_ok( ) ) ;
1991+
1992+ // Additional test: ensure FR and PL bounds (no value) work correctly
1993+ let fr_input = " FR BND1 VAR2\n " ;
1994+ cfg_if:: cfg_if! {
1995+ if #[ cfg( feature = "trace" ) ] {
1996+ let info = TracableInfo :: new( ) . forward( false ) . backward( false ) ;
1997+ let fr_result = Parser :: <f32 >:: bounds_line( LocatedSpan :: new_extra( fr_input, info) ) ;
1998+ } else {
1999+ let fr_result = Parser :: <f32 >:: bounds_line( fr_input) ;
2000+ }
2001+ }
2002+ assert ! ( fr_result. is_ok( ) ) ;
2003+ cfg_if:: cfg_if! {
2004+ if #[ cfg( feature = "trace" ) ] {
2005+ if let Ok ( ( _, Some ( bound) ) ) = fr_result {
2006+ assert_eq!( bound. bound_type, BoundType :: Fr ) ;
2007+ assert_eq!( bound. value, None ) ;
2008+ }
2009+ } else {
2010+ if let Ok ( ( _, Some ( bound) ) ) = fr_result {
2011+ assert_eq!( bound. bound_type, BoundType :: Fr ) ;
2012+ assert_eq!( bound. value, None ) ;
2013+ }
2014+ }
2015+ }
2016+
2017+ let pl_input = " PL BND1 VAR3\n " ;
2018+ cfg_if:: cfg_if! {
2019+ if #[ cfg( feature = "trace" ) ] {
2020+ let info = TracableInfo :: new( ) . forward( false ) . backward( false ) ;
2021+ let pl_result = Parser :: <f32 >:: bounds_line( LocatedSpan :: new_extra( pl_input, info) ) ;
2022+ } else {
2023+ let pl_result = Parser :: <f32 >:: bounds_line( pl_input) ;
2024+ }
2025+ }
2026+ assert ! ( pl_result. is_ok( ) ) ;
2027+ cfg_if:: cfg_if! {
2028+ if #[ cfg( feature = "trace" ) ] {
2029+ if let Ok ( ( _, Some ( bound) ) ) = pl_result {
2030+ assert_eq!( bound. bound_type, BoundType :: Pl ) ;
2031+ assert_eq!( bound. value, None ) ;
2032+ }
2033+ } else {
2034+ if let Ok ( ( _, Some ( bound) ) ) = pl_result {
2035+ assert_eq!( bound. bound_type, BoundType :: Pl ) ;
2036+ assert_eq!( bound. value, None ) ;
2037+ }
2038+ }
2039+ }
2040+
2041+ Ok ( ( ) )
2042+ }
2043+
18352044 /// Test indicator constraints with correct format
18362045 #[ test]
18372046 fn test_indicators_format ( ) -> Result < ( ) > {
0 commit comments