Skip to content

Commit c1915d9

Browse files
Define biginteger, bigdecimal, byte, short, long, double for all implementations (#42)
* Extract ParameterTypeRegistry#defineDefaultParameterTypes() * Linting * Extract defineDefaultParameterTypes * linting * Rename test, fix imports * Add test to match big decimal * Add bigdecimal test * Make javascript tests pass * Add BigDecimal support in Ruby * Add 1024 bit bigdecimal in go * Implement {biginteger} on all implementations * Add support for byte * Support remaining types * Remove conditional definition of types * Update docs * Add unit test * Update java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java Co-authored-by: Aurélien Reeves <[email protected]> Co-authored-by: Aurélien Reeves <[email protected]>
1 parent d016922 commit c1915d9

33 files changed

+490
-117
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
9+
### Changed
10+
- [Go] Parameters of type `{float}` are now parsed as `float32` (previously it was `float64`).
11+
Use `{double}` if you need `float64`. ([#42](https://github.com/cucumber/cucumber-expressions/pull/42))
12+
913
### Added
10-
- [.NET] Implemenation of Cucumber Expressions by porting the Java parser
14+
- [Ruby,JavaScript,Go] Add `bigdecimal`, `biginteger` parameter types ([#42](https://github.com/cucumber/cucumber-expressions/pull/42))
15+
- [.NET] Implementation of Cucumber Expressions by porting the Java parser
1116
([#1743](https://github.com/cucumber/cucumber-expressions/pull/45))
1217

1318
## [14.0.0] - 2021-10-12

README.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,22 @@ the following built-in parameter types:
5252

5353
| Parameter Type | Description |
5454
| --------------- | ----------- |
55-
| `{int}` | Matches integers, for example `71` or `-19`. |
56-
| `{float}` | Matches floats, for example `3.6`, `.8` or `-9.2`. |
55+
| `{int}` | Matches integers, for example `71` or `-19`. Converts to a 32-bit signed integer if the platform supports it.|
56+
| `{float}` | Matches floats, for example `3.6`, `.8` or `-9.2`. Converts to a 32 bit float if the platform supports it. |
5757
| `{word}` | Matches words without whitespace, for example `banana` (but not `banana split`). |
5858
| `{string}` | Matches single-quoted or double-quoted strings, for example `"banana split"` or `'banana split'` (but not `banana split`). Only the text between the quotes will be extracted. The quotes themselves are discarded. Empty pairs of quotes are valid and will be matched and passed to step code as empty strings. |
5959
| `{}` anonymous | Matches anything (`/.*/`). |
60+
| `{bigdecimal}` | Matches the same as `{float}`, but converts to a `BigDecimal` if the platform supports it. |
61+
| `{double}` | Matches the same as `{float}`, but converts to a 64 bit float if the platform supports it. |
62+
| `{biginteger}` | Matches the same as `{int}`, but converts to a `BigInteger` if the platform supports it. |
63+
| `{byte}` | Matches the same as `{int}`, but converts to an 8 bit signed integer if the platform supports it. |
64+
| `{short}` | Matches the same as `{int}`, but converts to a 16 bit signed integer if the platform supports it. |
65+
| `{long}` | Matches the same as `{int}`, but converts to a 64 bit signed integer if the platform supports it. |
6066

61-
### Cucumber-JVM additions
67+
### Cucumber-JVM
6268

63-
On the JVM, there are additional parameter types for `biginteger`, `bigdecimal`,
64-
`byte`, `short`, `long` and `double`.
65-
66-
The anonymous parameter type will be converted to the parameter type of the step definition using an object mapper.
67-
Cucumber comes with a built-in object mapper that can handle most basic types. Aside from `Enum` it supports conversion
68-
to `BigInteger`, `BigDecimal`, `Boolean`, `Byte`, `Short`, `Integer`, `Long`, `Float`, `Double` and `String`.
69+
The *anonymous* parameter type will be converted to the parameter type of the step definition using an object mapper.
70+
Cucumber comes with a built-in object mapper that can handle all numeric types as well as. `Enum`.
6971

7072
To automatically convert to other types it is recommended to install an object mapper. See [configuration](https://cucumber.io/docs/cucumber/configuration)
7173
to learn how.
@@ -153,7 +155,7 @@ public Color ConvertColor(string colorValue)
153155
}
154156
```
155157

156-
*Note: Currently the parameter name cannot be customized, so the custom paramters can only be used with the type name, e.g. `{Color}`.*
158+
*Note: Currently the parameter name cannot be customized, so the custom parameters can only be used with the type name, e.g. `{Color}`.*
157159

158160
## Optional text
159161

go/cucumber_expression_test.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
"github.com/stretchr/testify/require"
66
"gopkg.in/yaml.v3"
77
"io/ioutil"
8+
"math/big"
89
"reflect"
910
"regexp"
11+
"strconv"
1012
"testing"
1113
)
1214

@@ -27,7 +29,7 @@ func TestCucumberExpression(t *testing.T) {
2729
require.NoError(t, err)
2830
args, err := expression.Match(text)
2931
require.NoError(t, err)
30-
require.Equal(t, expectedArgs, argumentValues(args))
32+
require.Equal(t, expectedArgs, convertToYamlValues(args))
3133
}
3234

3335
assertThrows := func(t *testing.T, expected string, expr string, text string) {
@@ -75,6 +77,10 @@ func TestCucumberExpression(t *testing.T) {
7577
require.Equal(t, args[0].GetValue(), 7)
7678
})
7779

80+
t.Run("matches double", func(t *testing.T) {
81+
require.Equal(t, MatchCucumberExpression(t, "{double}", "0.1"), []interface{}{0.1})
82+
})
83+
7884
t.Run("matches float", func(t *testing.T) {
7985
require.Equal(t, MatchCucumberExpression(t, "{float}", ""), []interface{}(nil))
8086
require.Equal(t, MatchCucumberExpression(t, "{float}", "."), []interface{}(nil))
@@ -180,16 +186,33 @@ func MatchCucumberExpression(t *testing.T, expr string, text string, typeHints .
180186
require.NoError(t, err)
181187
args, err := expression.Match(text, typeHints...)
182188
require.NoError(t, err)
183-
return argumentValues(args)
189+
return convertToYamlValues(args)
184190
}
185191

186-
func argumentValues(args []*Argument) []interface{} {
192+
func convertToYamlValues(args []*Argument) []interface{} {
187193
if args == nil {
188194
return nil
189195
}
190196
result := make([]interface{}, len(args))
191197
for i, arg := range args {
192-
result[i] = arg.GetValue()
198+
value := arg.GetValue()
199+
switch v := value.(type) {
200+
case *big.Float:
201+
result[i] = fmt.Sprintf("%v", v)
202+
case *big.Int:
203+
result[i] = fmt.Sprintf("%v", v)
204+
case int8:
205+
result[i] = int(v)
206+
case int16:
207+
result[i] = int(v)
208+
case int64:
209+
result[i] = int(v)
210+
case float32:
211+
f, _ := strconv.ParseFloat(fmt.Sprint(v), 64)
212+
result[i] = f
213+
default:
214+
result[i] = value
215+
}
193216
}
194217
return result
195218
}

go/parameter_by_type_transformer.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package cucumberexpressions
22

33
import (
4-
"errors"
54
"fmt"
5+
"math/big"
66
"reflect"
77
"strconv"
88
)
99

1010
// can be imported from "math/bits". Not yet supported in go 1.8
1111
const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
1212

13+
const BigFloatKind = reflect.Invalid + 1000
14+
const BigIntKind = reflect.Invalid + 1001
15+
1316
type ParameterByTypeTransformer interface {
1417
// toValueType accepts either reflect.Type or reflect.Kind
1518
Transform(fromValue string, toValueType interface{}) (interface{}, error)
@@ -30,7 +33,7 @@ func (s BuiltInParameterTransformer) Transform(fromValue string, toValueType int
3033
return nil, createError(fromValue, toValueType)
3134
}
3235

33-
func transformKind(fromValue string, toValueKind reflect.Kind) (interface{}, error) {
36+
func transformKind(fromValue string, toValueKind interface{String() string}) (interface{}, error) {
3437
switch toValueKind {
3538
case reflect.String:
3639
return fromValue, nil
@@ -100,14 +103,23 @@ func transformKind(fromValue string, toValueKind reflect.Kind) (interface{}, err
100103
return nil, err
101104
case reflect.Float64:
102105
return strconv.ParseFloat(fromValue, 64)
106+
case BigFloatKind:
107+
floatVal, _, err := big.ParseFloat(fromValue, 10, 1024, big.ToNearestEven)
108+
return floatVal, err
109+
case BigIntKind:
110+
floatVal, success := new(big.Int).SetString(fromValue, 10)
111+
if !success {
112+
return nil, fmt.Errorf("Could not parse bigint: %s", fromValue)
113+
}
114+
return floatVal, nil
103115
default:
104116
return nil, createError(fromValue, toValueKind.String())
105117
}
106118
}
107119

108120
func createError(fromValue string, toValueType interface{}) error {
109-
return errors.New(fmt.Sprintf("Can't transform '%s' to %s. "+
121+
return fmt.Errorf("Can't transform '%s' to %s. "+
110122
"BuiltInParameterTransformer only supports a limited number of types. "+
111123
"Consider using a different object mapper or register a parameter type for %s",
112-
fromValue, toValueType, toValueType))
124+
fromValue, toValueType, toValueType)
113125
}

go/parameter_by_type_transformer_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cucumberexpressions
22

33
import (
44
"github.com/stretchr/testify/require"
5+
"math/big"
56
"reflect"
67
"testing"
78
)
@@ -28,7 +29,7 @@ func TestConvert(t *testing.T) {
2829
assertTransforms(t, int64(42), "42", reflect.Int64)
2930
})
3031

31-
t.Run("converts to kind unit", func(t *testing.T) {
32+
t.Run("converts to kind uint", func(t *testing.T) {
3233
assertTransforms(t, uint(42), "42", reflect.Uint)
3334
assertTransforms(t, uint8(42), "42", reflect.Uint8)
3435
assertTransforms(t, uint16(42), "42", reflect.Uint16)
@@ -45,6 +46,19 @@ func TestConvert(t *testing.T) {
4546
assertTransforms(t, float64(4.2e+12), "4.2e12", reflect.Float64)
4647
})
4748

49+
t.Run("converts to custom kind BigFloatKind", func(t *testing.T) {
50+
pi := "3.1415926535897932384626433832795028841971693993751"
51+
bigFloat, _, err := big.ParseFloat(pi, 10, 1024, big.ToNearestEven)
52+
require.NoError(t, err)
53+
assertTransforms(t, bigFloat, pi, BigFloatKind)
54+
})
55+
56+
t.Run("converts to custom kind BigIntKind", func(t *testing.T) {
57+
b := "31415926535897932384626433832795028841971693993751"
58+
bigInt, _ := new(big.Int).SetString(b, 10)
59+
assertTransforms(t, bigInt, b, BigIntKind)
60+
})
61+
4862
t.Run("errors un supported kind", func(t *testing.T) {
4963
transformer := BuiltInParameterTransformer{}
5064
_, err := transformer.Transform("Barbara Liskov", reflect.Complex64)

0 commit comments

Comments
 (0)