Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define biginteger, bigdecimal, byte, short, long, double for all implementations #42

Merged
merged 21 commits into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Changed
- [Go] Parameters of type `{float}` are now parsed as `float32` (previously it was `float64`).
Use `{double}` if you need `float64`. ([#42](https://github.com/cucumber/cucumber-expressions/pull/42))

### Added
- [.NET] Implemenation of Cucumber Expressions by porting the Java parser
- [Ruby,JavaScript,Go] Add `bigdecimal`, `biginteger` parameter types ([#42](https://github.com/cucumber/cucumber-expressions/pull/42))
- [.NET] Implementation of Cucumber Expressions by porting the Java parser
([#1743](https://github.com/cucumber/cucumber-expressions/pull/45))

## [14.0.0] - 2021-10-12
Expand Down
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,22 @@ the following built-in parameter types:

| Parameter Type | Description |
| --------------- | ----------- |
| `{int}` | Matches integers, for example `71` or `-19`. |
| `{float}` | Matches floats, for example `3.6`, `.8` or `-9.2`. |
| `{int}` | Matches integers, for example `71` or `-19`. Converts to a 32-bit signed integer if the platform supports it.|
| `{float}` | Matches floats, for example `3.6`, `.8` or `-9.2`. Converts to a 32 bit float if the platform supports it. |
| `{word}` | Matches words without whitespace, for example `banana` (but not `banana split`). |
| `{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. |
| `{}` anonymous | Matches anything (`/.*/`). |
| `{bigdecimal}` | Matches the same as `{float}`, but converts to a `BigDecimal` if the platform supports it. |
| `{double}` | Matches the same as `{float}`, but converts to a 64 bit float if the platform supports it. |
| `{biginteger}` | Matches the same as `{int}`, but converts to a `BigInteger` if the platform supports it. |
| `{byte}` | Matches the same as `{int}`, but converts to an 8 bit signed integer if the platform supports it. |
| `{short}` | Matches the same as `{int}`, but converts to a 16 bit signed integer if the platform supports it. |
| `{long}` | Matches the same as `{int}`, but converts to a 64 bit signed integer if the platform supports it. |

### Cucumber-JVM additions
### Cucumber-JVM

On the JVM, there are additional parameter types for `biginteger`, `bigdecimal`,
`byte`, `short`, `long` and `double`.

The anonymous parameter type will be converted to the parameter type of the step definition using an object mapper.
Cucumber comes with a built-in object mapper that can handle most basic types. Aside from `Enum` it supports conversion
to `BigInteger`, `BigDecimal`, `Boolean`, `Byte`, `Short`, `Integer`, `Long`, `Float`, `Double` and `String`.
The *anonymous* parameter type will be converted to the parameter type of the step definition using an object mapper.
Cucumber comes with a built-in object mapper that can handle all numeric types as well as. `Enum`.

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

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

## Optional text

Expand Down
31 changes: 27 additions & 4 deletions go/cucumber_expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
"io/ioutil"
"math/big"
"reflect"
"regexp"
"strconv"
"testing"
)

Expand All @@ -27,7 +29,7 @@ func TestCucumberExpression(t *testing.T) {
require.NoError(t, err)
args, err := expression.Match(text)
require.NoError(t, err)
require.Equal(t, expectedArgs, argumentValues(args))
require.Equal(t, expectedArgs, convertToYamlValues(args))
}

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

t.Run("matches double", func(t *testing.T) {
require.Equal(t, MatchCucumberExpression(t, "{double}", "0.1"), []interface{}{0.1})
})

t.Run("matches float", func(t *testing.T) {
require.Equal(t, MatchCucumberExpression(t, "{float}", ""), []interface{}(nil))
require.Equal(t, MatchCucumberExpression(t, "{float}", "."), []interface{}(nil))
Expand Down Expand Up @@ -180,16 +186,33 @@ func MatchCucumberExpression(t *testing.T, expr string, text string, typeHints .
require.NoError(t, err)
args, err := expression.Match(text, typeHints...)
require.NoError(t, err)
return argumentValues(args)
return convertToYamlValues(args)
}

func argumentValues(args []*Argument) []interface{} {
func convertToYamlValues(args []*Argument) []interface{} {
if args == nil {
return nil
}
result := make([]interface{}, len(args))
for i, arg := range args {
result[i] = arg.GetValue()
value := arg.GetValue()
switch v := value.(type) {
case *big.Float:
result[i] = fmt.Sprintf("%v", v)
case *big.Int:
result[i] = fmt.Sprintf("%v", v)
case int8:
result[i] = int(v)
case int16:
result[i] = int(v)
case int64:
result[i] = int(v)
case float32:
f, _ := strconv.ParseFloat(fmt.Sprint(v), 64)
result[i] = f
default:
result[i] = value
}
}
return result
}
20 changes: 16 additions & 4 deletions go/parameter_by_type_transformer.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package cucumberexpressions

import (
"errors"
"fmt"
"math/big"
"reflect"
"strconv"
)

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

const BigFloatKind = reflect.Invalid + 1000
const BigIntKind = reflect.Invalid + 1001

type ParameterByTypeTransformer interface {
// toValueType accepts either reflect.Type or reflect.Kind
Transform(fromValue string, toValueType interface{}) (interface{}, error)
Expand All @@ -30,7 +33,7 @@ func (s BuiltInParameterTransformer) Transform(fromValue string, toValueType int
return nil, createError(fromValue, toValueType)
}

func transformKind(fromValue string, toValueKind reflect.Kind) (interface{}, error) {
func transformKind(fromValue string, toValueKind interface{String() string}) (interface{}, error) {
switch toValueKind {
case reflect.String:
return fromValue, nil
Expand Down Expand Up @@ -100,14 +103,23 @@ func transformKind(fromValue string, toValueKind reflect.Kind) (interface{}, err
return nil, err
case reflect.Float64:
return strconv.ParseFloat(fromValue, 64)
case BigFloatKind:
floatVal, _, err := big.ParseFloat(fromValue, 10, 1024, big.ToNearestEven)
return floatVal, err
case BigIntKind:
floatVal, success := new(big.Int).SetString(fromValue, 10)
if !success {
return nil, fmt.Errorf("Could not parse bigint: %s", fromValue)
}
return floatVal, nil
default:
return nil, createError(fromValue, toValueKind.String())
}
}

func createError(fromValue string, toValueType interface{}) error {
return errors.New(fmt.Sprintf("Can't transform '%s' to %s. "+
return fmt.Errorf("Can't transform '%s' to %s. "+
"BuiltInParameterTransformer only supports a limited number of types. "+
"Consider using a different object mapper or register a parameter type for %s",
fromValue, toValueType, toValueType))
fromValue, toValueType, toValueType)
}
16 changes: 15 additions & 1 deletion go/parameter_by_type_transformer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cucumberexpressions

import (
"github.com/stretchr/testify/require"
"math/big"
"reflect"
"testing"
)
Expand All @@ -28,7 +29,7 @@ func TestConvert(t *testing.T) {
assertTransforms(t, int64(42), "42", reflect.Int64)
})

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

t.Run("converts to custom kind BigFloatKind", func(t *testing.T) {
pi := "3.1415926535897932384626433832795028841971693993751"
bigFloat, _, err := big.ParseFloat(pi, 10, 1024, big.ToNearestEven)
require.NoError(t, err)
assertTransforms(t, bigFloat, pi, BigFloatKind)
})

t.Run("converts to custom kind BigIntKind", func(t *testing.T) {
b := "31415926535897932384626433832795028841971693993751"
bigInt, _ := new(big.Int).SetString(b, 10)
assertTransforms(t, bigInt, b, BigIntKind)
})

t.Run("errors un supported kind", func(t *testing.T) {
transformer := BuiltInParameterTransformer{}
_, err := transformer.Transform("Barbara Liskov", reflect.Complex64)
Expand Down
Loading