From e97f6c82888b1879138b617053096db06140d6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 8 Nov 2021 12:29:15 +0000 Subject: [PATCH 01/17] Extract ParameterTypeRegistry#defineDefaultParameterTypes() --- javascript/src/ParameterTypeRegistry.ts | 12 +++++++++--- javascript/test/ParameterTypeRegistryTest.ts | 8 ++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/javascript/src/ParameterTypeRegistry.ts b/javascript/src/ParameterTypeRegistry.ts index ab8770316..c00547e63 100644 --- a/javascript/src/ParameterTypeRegistry.ts +++ b/javascript/src/ParameterTypeRegistry.ts @@ -13,7 +13,13 @@ export default class ParameterTypeRegistry { private readonly parameterTypeByName = new Map>() private readonly parameterTypesByRegexp = new Map>>() - constructor() { + constructor(withDefaultParameterTypes = true) { + if (withDefaultParameterTypes) { + this.defineDefaultParameterTypes() + } + } + + public defineDefaultParameterTypes() { this.defineParameterType( new ParameterType( 'int', @@ -42,7 +48,7 @@ export default class ParameterTypeRegistry { 'string', ParameterTypeRegistry.STRING_REGEXP, String, - (s1, s2) => (s1 || s2 || '').replace(/\\"/g, '"').replace(/\\'/g, "'"), + (s1, s2) => (s1 || s2 || '').replace(/\\"/g, '"').replace(/\\'/g, '\''), true, false ) @@ -114,7 +120,7 @@ export default class ParameterTypeRegistry { ) { throw new CucumberExpressionError( 'There can only be one preferential parameter type per regexp. ' + - `The regexp /${parameterTypeRegexp}/ is used for two preferential parameter types, {${existingParameterType.name}} and {${parameterType.name}}` + `The regexp /${parameterTypeRegexp}/ is used for two preferential parameter types, {${existingParameterType.name}} and {${parameterType.name}}` ) } if (parameterTypes.indexOf(parameterType) === -1) { diff --git a/javascript/test/ParameterTypeRegistryTest.ts b/javascript/test/ParameterTypeRegistryTest.ts index f8a5548de..46715b8af 100644 --- a/javascript/test/ParameterTypeRegistryTest.ts +++ b/javascript/test/ParameterTypeRegistryTest.ts @@ -55,4 +55,12 @@ describe('ParameterTypeRegistry', () => { person ) }) + + it('can be instantiated without default parameter types', () => { + registry = new ParameterTypeRegistry(false) + + const parameterType = new ParameterType('int', /\d+/, Number, (s) => +s, true, true) + registry.defineParameterType(parameterType) + assert.strictEqual(registry.lookupByTypeName('int'), parameterType) + }) }) From 0c307ef5311132da66a005b0783e3188ed0d10f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 8 Nov 2021 12:34:40 +0000 Subject: [PATCH 02/17] Linting --- javascript/src/ParameterTypeRegistry.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/src/ParameterTypeRegistry.ts b/javascript/src/ParameterTypeRegistry.ts index c00547e63..60554e060 100644 --- a/javascript/src/ParameterTypeRegistry.ts +++ b/javascript/src/ParameterTypeRegistry.ts @@ -48,7 +48,7 @@ export default class ParameterTypeRegistry { 'string', ParameterTypeRegistry.STRING_REGEXP, String, - (s1, s2) => (s1 || s2 || '').replace(/\\"/g, '"').replace(/\\'/g, '\''), + (s1, s2) => (s1 || s2 || '').replace(/\\"/g, '"').replace(/\\'/g, "'"), true, false ) @@ -120,7 +120,7 @@ export default class ParameterTypeRegistry { ) { throw new CucumberExpressionError( 'There can only be one preferential parameter type per regexp. ' + - `The regexp /${parameterTypeRegexp}/ is used for two preferential parameter types, {${existingParameterType.name}} and {${parameterType.name}}` + `The regexp /${parameterTypeRegexp}/ is used for two preferential parameter types, {${existingParameterType.name}} and {${parameterType.name}}` ) } if (parameterTypes.indexOf(parameterType) === -1) { From 59241d38872bfe16c7ed92bfa7680cb27748b780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 8 Nov 2021 12:54:22 +0000 Subject: [PATCH 03/17] Extract defineDefaultParameterTypes --- javascript/src/CucumberExpression.ts | 2 +- javascript/src/Expression.ts | 6 --- javascript/src/ExpressionFactory.ts | 2 +- javascript/src/ParameterTypeRegistry.ts | 51 ++----------------- javascript/src/RegularExpression.ts | 2 +- javascript/src/defineDefaultParameterTypes.ts | 47 +++++++++++++++++ javascript/src/index.ts | 2 +- javascript/src/types.ts | 11 ++++ javascript/test/RegularExpressionTest.ts | 14 ----- 9 files changed, 66 insertions(+), 71 deletions(-) delete mode 100644 javascript/src/Expression.ts create mode 100644 javascript/src/defineDefaultParameterTypes.ts create mode 100644 javascript/src/types.ts diff --git a/javascript/src/CucumberExpression.ts b/javascript/src/CucumberExpression.ts index a61905c84..4b608c30c 100644 --- a/javascript/src/CucumberExpression.ts +++ b/javascript/src/CucumberExpression.ts @@ -10,7 +10,7 @@ import { createParameterIsNotAllowedInOptional, createUndefinedParameterType, } from './Errors.js' -import Expression from './Expression.js' +import { Expression } from './types.js' import ParameterType from './ParameterType.js' import ParameterTypeRegistry from './ParameterTypeRegistry.js' import TreeRegexp from './TreeRegexp.js' diff --git a/javascript/src/Expression.ts b/javascript/src/Expression.ts deleted file mode 100644 index cae6148cc..000000000 --- a/javascript/src/Expression.ts +++ /dev/null @@ -1,6 +0,0 @@ -import Argument from './Argument.js' - -export default interface Expression { - readonly source: string - match(text: string): readonly Argument[] | null -} diff --git a/javascript/src/ExpressionFactory.ts b/javascript/src/ExpressionFactory.ts index f66dd24a0..c33261e39 100644 --- a/javascript/src/ExpressionFactory.ts +++ b/javascript/src/ExpressionFactory.ts @@ -1,7 +1,7 @@ import CucumberExpression from './CucumberExpression.js' -import Expression from './Expression.js' import ParameterTypeRegistry from './ParameterTypeRegistry.js' import RegularExpression from './RegularExpression.js' +import { Expression } from './types.js' export default class ExpressionFactory { public constructor(private readonly parameterTypeRegistry: ParameterTypeRegistry) {} diff --git a/javascript/src/ParameterTypeRegistry.ts b/javascript/src/ParameterTypeRegistry.ts index 60554e060..3ff05d2cc 100644 --- a/javascript/src/ParameterTypeRegistry.ts +++ b/javascript/src/ParameterTypeRegistry.ts @@ -1,63 +1,20 @@ import CucumberExpressionError from './CucumberExpressionError.js' import CucumberExpressionGenerator from './CucumberExpressionGenerator.js' +import defineDefaultParameterTypes from './defineDefaultParameterTypes.js' import { AmbiguousParameterTypeError } from './Errors.js' import ParameterType from './ParameterType.js' +import { DefinesParameterType } from './types' -export default class ParameterTypeRegistry { - public static readonly INTEGER_REGEXPS = [/-?\d+/, /\d+/] - public static readonly FLOAT_REGEXP = /(?=.*\d.*)[-+]?\d*(?:\.(?=\d.*))?\d*(?:\d+[E][+-]?\d+)?/ - public static readonly WORD_REGEXP = /[^\s]+/ - public static readonly STRING_REGEXP = /"([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'/ - public static readonly ANONYMOUS_REGEXP = /.*/ - +export default class ParameterTypeRegistry implements DefinesParameterType { private readonly parameterTypeByName = new Map>() private readonly parameterTypesByRegexp = new Map>>() constructor(withDefaultParameterTypes = true) { if (withDefaultParameterTypes) { - this.defineDefaultParameterTypes() + defineDefaultParameterTypes(this) } } - public defineDefaultParameterTypes() { - this.defineParameterType( - new ParameterType( - 'int', - ParameterTypeRegistry.INTEGER_REGEXPS, - Number, - (s) => (s === undefined ? null : Number(s)), - true, - true - ) - ) - this.defineParameterType( - new ParameterType( - 'float', - ParameterTypeRegistry.FLOAT_REGEXP, - Number, - (s) => (s === undefined ? null : parseFloat(s)), - true, - false - ) - ) - this.defineParameterType( - new ParameterType('word', ParameterTypeRegistry.WORD_REGEXP, String, (s) => s, false, false) - ) - this.defineParameterType( - new ParameterType( - 'string', - ParameterTypeRegistry.STRING_REGEXP, - String, - (s1, s2) => (s1 || s2 || '').replace(/\\"/g, '"').replace(/\\'/g, "'"), - true, - false - ) - ) - this.defineParameterType( - new ParameterType('', ParameterTypeRegistry.ANONYMOUS_REGEXP, String, (s) => s, false, true) - ) - } - get parameterTypes(): IterableIterator> { return this.parameterTypeByName.values() } diff --git a/javascript/src/RegularExpression.ts b/javascript/src/RegularExpression.ts index a84da44d2..408805129 100644 --- a/javascript/src/RegularExpression.ts +++ b/javascript/src/RegularExpression.ts @@ -1,8 +1,8 @@ import Argument from './Argument.js' -import Expression from './Expression.js' import ParameterType from './ParameterType.js' import ParameterTypeRegistry from './ParameterTypeRegistry.js' import TreeRegexp from './TreeRegexp.js' +import { Expression } from './types.js' export default class RegularExpression implements Expression { private readonly treeRegexp: TreeRegexp diff --git a/javascript/src/defineDefaultParameterTypes.ts b/javascript/src/defineDefaultParameterTypes.ts new file mode 100644 index 000000000..5805efd85 --- /dev/null +++ b/javascript/src/defineDefaultParameterTypes.ts @@ -0,0 +1,47 @@ +import ParameterType from './ParameterType.js' +import { DefinesParameterType } from './types.js' + +const INTEGER_REGEXPS = [/-?\d+/, /\d+/] +const FLOAT_REGEXP = /(?=.*\d.*)[-+]?\d*(?:\.(?=\d.*))?\d*(?:\d+[E][+-]?\d+)?/ +const WORD_REGEXP = /[^\s]+/ +const STRING_REGEXP = /"([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'/ +const ANONYMOUS_REGEXP = /.*/ + +export default function defineDefaultParameterTypes(registry: DefinesParameterType) { + registry.defineParameterType( + new ParameterType( + 'int', + INTEGER_REGEXPS, + Number, + (s) => (s === undefined ? null : Number(s)), + true, + true + ) + ) + registry.defineParameterType( + new ParameterType( + 'float', + FLOAT_REGEXP, + Number, + (s) => (s === undefined ? null : parseFloat(s)), + true, + false + ) + ) + registry.defineParameterType( + new ParameterType('word', WORD_REGEXP, String, (s) => s, false, false) + ) + registry.defineParameterType( + new ParameterType( + 'string', + STRING_REGEXP, + String, + (s1, s2) => (s1 || s2 || '').replace(/\\"/g, '"').replace(/\\'/g, "'"), + true, + false + ) + ) + registry.defineParameterType( + new ParameterType('', ANONYMOUS_REGEXP, String, (s) => s, false, true) + ) +} diff --git a/javascript/src/index.ts b/javascript/src/index.ts index 85be524c6..391dbab44 100644 --- a/javascript/src/index.ts +++ b/javascript/src/index.ts @@ -1,13 +1,13 @@ import Argument from './Argument.js' import CucumberExpression from './CucumberExpression.js' import CucumberExpressionGenerator from './CucumberExpressionGenerator.js' -import Expression from './Expression.js' import ExpressionFactory from './ExpressionFactory.js' import GeneratedExpression from './GeneratedExpression.js' import Group from './Group.js' import ParameterType from './ParameterType.js' import ParameterTypeRegistry from './ParameterTypeRegistry.js' import RegularExpression from './RegularExpression.js' +import { Expression } from './types.js' export { Argument, diff --git a/javascript/src/types.ts b/javascript/src/types.ts new file mode 100644 index 000000000..1404ca8a3 --- /dev/null +++ b/javascript/src/types.ts @@ -0,0 +1,11 @@ +import Argument from './Argument.js' +import ParameterType from './ParameterType' + +export interface DefinesParameterType { + defineParameterType(parameterType: ParameterType): void +} + +export interface Expression { + readonly source: string + match(text: string): readonly Argument[] | null +} diff --git a/javascript/test/RegularExpressionTest.ts b/javascript/test/RegularExpressionTest.ts index 641433edd..c7d039b42 100644 --- a/javascript/test/RegularExpressionTest.ts +++ b/javascript/test/RegularExpressionTest.ts @@ -46,20 +46,6 @@ describe('RegularExpression', () => { assert.deepStrictEqual(match(/(\d+)/, '22'), [22]) }) - it('transforms float without integer part', () => { - assert.deepStrictEqual( - match(new RegExp(`(${ParameterTypeRegistry.FLOAT_REGEXP.source})`), '.22'), - [0.22] - ) - }) - - it('transforms float with sign', () => { - assert.deepStrictEqual( - match(new RegExp(`(${ParameterTypeRegistry.FLOAT_REGEXP.source})`), '-1.22'), - [-1.22] - ) - }) - it('returns null when there is no match', () => { assert.strictEqual(match(/hello/, 'world'), null) }) From 852aad30ab1cb5fc67432f4802289868016c7a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 8 Nov 2021 13:07:34 +0000 Subject: [PATCH 04/17] linting --- javascript/src/CucumberExpression.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/src/CucumberExpression.ts b/javascript/src/CucumberExpression.ts index 4b608c30c..30cf9bc88 100644 --- a/javascript/src/CucumberExpression.ts +++ b/javascript/src/CucumberExpression.ts @@ -10,10 +10,10 @@ import { createParameterIsNotAllowedInOptional, createUndefinedParameterType, } from './Errors.js' -import { Expression } from './types.js' import ParameterType from './ParameterType.js' import ParameterTypeRegistry from './ParameterTypeRegistry.js' import TreeRegexp from './TreeRegexp.js' +import { Expression } from './types.js' const ESCAPE_PATTERN = () => /([\\^[({$.|?*+})\]])/g From 1257b26e00747a44e92c6d20cfe1e77e458ecd68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 8 Nov 2021 13:50:47 +0000 Subject: [PATCH 05/17] Rename test, fix imports --- .../cucumber/cucumberexpressions/CucumberExpressionTest.java | 4 ++-- ...ches-float-1.yaml => matches-float-with-integer-part.yaml} | 0 ...s-float-2.yaml => matches-float-without-integer-part.yaml} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename testdata/cucumber-expression/matching/{matches-float-1.yaml => matches-float-with-integer-part.yaml} (100%) rename testdata/cucumber-expression/matching/{matches-float-2.yaml => matches-float-without-integer-part.yaml} (100%) diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java index 9b7932e98..88b94833c 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java @@ -76,11 +76,11 @@ static class Converter implements ArgumentConverter { Yaml yaml = new Yaml(); @Override - public io.cucumber.cucumberexpressions.CucumberExpressionTest.Expectation convert(Object source, ParameterContext context) throws ArgumentConversionException { + public Expectation convert(Object source, ParameterContext context) throws ArgumentConversionException { try { Path path = (Path) source; InputStream inputStream = newInputStream(path); - return yaml.loadAs(inputStream, io.cucumber.cucumberexpressions.CucumberExpressionTest.Expectation.class); + return yaml.loadAs(inputStream, Expectation.class); } catch (IOException e) { throw new ArgumentConversionException("Could not load " + source, e); } diff --git a/testdata/cucumber-expression/matching/matches-float-1.yaml b/testdata/cucumber-expression/matching/matches-float-with-integer-part.yaml similarity index 100% rename from testdata/cucumber-expression/matching/matches-float-1.yaml rename to testdata/cucumber-expression/matching/matches-float-with-integer-part.yaml diff --git a/testdata/cucumber-expression/matching/matches-float-2.yaml b/testdata/cucumber-expression/matching/matches-float-without-integer-part.yaml similarity index 100% rename from testdata/cucumber-expression/matching/matches-float-2.yaml rename to testdata/cucumber-expression/matching/matches-float-without-integer-part.yaml From e8ae97463c8f2cd8d80d40bae883957d3c432460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 3 Jan 2022 11:07:28 +0000 Subject: [PATCH 06/17] Add test to match big decimal --- .../CucumberExpressionTest.java | 1 + .../cucumberexpressions/CustomMatchers.java | 32 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java index 88b94833c..f1af2a040 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java @@ -41,6 +41,7 @@ private static List acceptance_tests_pass() throws IOException { newDirectoryStream(Paths.get("..", "testdata", "cucumber-expression", "matching")).forEach(paths::add); paths.sort(Comparator.naturalOrder()); return paths; +// return singletonList(Paths.get("..", "testdata", "cucumber-expression", "matching", "matches-bigdecimal-with-integer-part.yaml")); } @ParameterizedTest diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java b/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java index 09b72d073..e667d2961 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java @@ -1,8 +1,13 @@ package io.cucumber.cucumberexpressions; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.collection.IsIterableContainingInOrder; +import org.hamcrest.core.IsEqual; +import org.hamcrest.number.IsCloseTo; +import java.math.BigDecimal; import java.util.List; import java.util.stream.Collectors; @@ -12,7 +17,32 @@ public class CustomMatchers { public static Matcher> equalOrCloseTo(List list) { if (list == null || list.isEmpty()) return equalTo(list); - List> matchers = list.stream().map(e -> e instanceof Double ? closeTo(((Double) e), 0.0001) : equalTo(e)).collect(Collectors.toList()); + List> matchers = list.stream().map(EqualOrCloseTo::new).collect(Collectors.toList()); return new IsIterableContainingInOrder(matchers); } + + private static class EqualOrCloseTo extends BaseMatcher { + private final Object expectedValue; + + public EqualOrCloseTo(Object expectedValue) { + this.expectedValue = expectedValue; + } + + @Override + public boolean matches(Object actual) { + if(actual instanceof BigDecimal) { + return new IsEqual(this.expectedValue).matches(actual.toString()); + } else if(actual instanceof Double) { + return new IsCloseTo(((Double)this.expectedValue), 0.0001).matches(actual); + } else if(actual instanceof Number || actual instanceof String || actual == null) { + return new IsEqual(this.expectedValue).matches(actual); + } + throw new RuntimeException("Unsupported type: " + actual.getClass()); + } + + @Override + public void describeTo(Description description) { + description.appendValue(expectedValue); + } + } } From 43209ec4c92a4c7090126fdf69649a936e30dee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 3 Jan 2022 11:24:31 +0000 Subject: [PATCH 07/17] Add bigdecimal test --- .../cucumber/cucumberexpressions/CucumberExpressionTest.java | 2 -- .../java/io/cucumber/cucumberexpressions/CustomMatchers.java | 4 ++-- .../cucumber/cucumberexpressions/RegularExpressionTest.java | 1 - .../matching/matches-bigdecimal-with-integer-part.yaml | 5 +++++ 4 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java index f1af2a040..d9554df54 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java @@ -41,7 +41,6 @@ private static List acceptance_tests_pass() throws IOException { newDirectoryStream(Paths.get("..", "testdata", "cucumber-expression", "matching")).forEach(paths::add); paths.sort(Comparator.naturalOrder()); return paths; -// return singletonList(Paths.get("..", "testdata", "cucumber-expression", "matching", "matches-bigdecimal-with-integer-part.yaml")); } @ParameterizedTest @@ -52,7 +51,6 @@ void acceptance_tests_pass(@ConvertWith(Converter.class) Expectation expectation List> match = expression.match(expectation.text); List values = match == null ? null : match.stream() .map(Argument::getValue) - .map(e -> e instanceof Float ? ((Float) e).doubleValue() : e) .collect(Collectors.toList()); assertThat(values, CustomMatchers.equalOrCloseTo(expectation.expected_args)); diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java b/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java index e667d2961..d08c0ba56 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java @@ -32,8 +32,8 @@ public EqualOrCloseTo(Object expectedValue) { public boolean matches(Object actual) { if(actual instanceof BigDecimal) { return new IsEqual(this.expectedValue).matches(actual.toString()); - } else if(actual instanceof Double) { - return new IsCloseTo(((Double)this.expectedValue), 0.0001).matches(actual); + } else if(actual instanceof Double || actual instanceof Float) { + return new IsCloseTo(((Double)this.expectedValue), 0.0001).matches(((Number)actual).doubleValue()); } else if(actual instanceof Number || actual instanceof String || actual == null) { return new IsEqual(this.expectedValue).matches(actual); } diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/RegularExpressionTest.java b/java/src/test/java/io/cucumber/cucumberexpressions/RegularExpressionTest.java index cdf5165cf..1aa4e809b 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/RegularExpressionTest.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/RegularExpressionTest.java @@ -48,7 +48,6 @@ void acceptance_tests_pass(@ConvertWith(Converter.class) Expectation expectation List> match = expression.match(expectation.text); List values = match == null ? null : match.stream() .map(Argument::getValue) - .map(e -> e instanceof Float ? ((Float)e).doubleValue() : e) .collect(Collectors.toList()); assertThat(values, CustomMatchers.equalOrCloseTo(expectation.expected_args)); diff --git a/testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml b/testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml new file mode 100644 index 000000000..fb9a9f412 --- /dev/null +++ b/testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml @@ -0,0 +1,5 @@ +--- +expression: "{bigdecimal}" +text: '3.14159265358979323846264338327950288419716939937510' +expected_args: +- '3.14159265358979323846264338327950288419716939937510' From ce4d6e33922b44112a4063fdd786362a85a9b5f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 3 Jan 2022 18:55:30 +0000 Subject: [PATCH 08/17] Make javascript tests pass --- javascript/src/defineDefaultParameterTypes.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/javascript/src/defineDefaultParameterTypes.ts b/javascript/src/defineDefaultParameterTypes.ts index 5805efd85..8d9a6c0d8 100644 --- a/javascript/src/defineDefaultParameterTypes.ts +++ b/javascript/src/defineDefaultParameterTypes.ts @@ -44,4 +44,17 @@ export default function defineDefaultParameterTypes(registry: DefinesParameterTy registry.defineParameterType( new ParameterType('', ANONYMOUS_REGEXP, String, (s) => s, false, true) ) + + // TODO: conditionally define this + + registry.defineParameterType( + new ParameterType( + 'bigdecimal', + FLOAT_REGEXP, + Number, + (s) => (s === undefined ? null : s), + false, + false + ) + ) } From c856081dab5edea767777af1a8686f2d0e04b460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 3 Jan 2022 22:52:44 +0000 Subject: [PATCH 09/17] Add BigDecimal support in Ruby --- CHANGELOG.md | 3 ++- .../ParameterByTypeTransformer.java | 2 +- .../ParameterByTypeTransformerTest.java | 23 ++++++++++--------- .../parameter_type_registry.rb | 2 ++ .../cucumber_expression_spec.rb | 5 +++- .../matches-bigdecimal-with-integer-part.yaml | 4 ++-- 6 files changed, 23 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7ce1e6d2..972b36202 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added -- [.NET] Implemenation of Cucumber Expressions by porting the Java parser +- [Ruby] Add `bigdecimal` parameter type +- [.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 diff --git a/java/src/main/java/io/cucumber/cucumberexpressions/ParameterByTypeTransformer.java b/java/src/main/java/io/cucumber/cucumberexpressions/ParameterByTypeTransformer.java index d283f1541..0d076b751 100644 --- a/java/src/main/java/io/cucumber/cucumberexpressions/ParameterByTypeTransformer.java +++ b/java/src/main/java/io/cucumber/cucumberexpressions/ParameterByTypeTransformer.java @@ -6,7 +6,7 @@ /** * The {@link ParameterTypeRegistry} uses the default transformer - * to execute all transforms for build in parameter types and all + * to execute all transforms for built-in parameter types and all * anonymous types. */ @API(status = API.Status.STABLE) diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/ParameterByTypeTransformerTest.java b/java/src/test/java/io/cucumber/cucumberexpressions/ParameterByTypeTransformerTest.java index 8e75e376f..39494b1d4 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/ParameterByTypeTransformerTest.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/ParameterByTypeTransformerTest.java @@ -1,17 +1,15 @@ package io.cucumber.cucumberexpressions; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Optional; -import java.util.OptionalDouble; -import java.util.OptionalInt; -import java.util.OptionalLong; import java.util.stream.Stream; import static java.util.Locale.ENGLISH; @@ -42,7 +40,8 @@ public void should_convert_null_to_optional(final ParameterByTypeTransformer def @ParameterizedTest @MethodSource("objectMapperImplementations") public void should_convert_null_to_optional_generic(final ParameterByTypeTransformer defaultTransformer) throws Throwable { - Type optionalIntType = new TypeReference>(){}.getType(); + Type optionalIntType = new TypeReference>() { + }.getType(); assertEquals(Optional.empty(), defaultTransformer.transform(null, optionalIntType)); } @@ -57,7 +56,8 @@ public void should_convert_to_string(final ParameterByTypeTransformer defaultTra @ParameterizedTest @MethodSource("objectMapperImplementations") public void should_convert_to_optional_string(final ParameterByTypeTransformer defaultTransformer) throws Throwable { - Type optionalStringType = new TypeReference>(){}.getType(); + Type optionalStringType = new TypeReference>() { + }.getType(); assertEquals(Optional.of("Barbara Liskov"), defaultTransformer.transform("Barbara Liskov", optionalStringType)); } @@ -107,7 +107,8 @@ public void should_convert_to_integer(final ParameterByTypeTransformer defaultTr @ParameterizedTest @MethodSource("objectMapperImplementations") public void should_convert_to_optional_integer(final ParameterByTypeTransformer defaultTransformer) throws Throwable { - Type optionalIntType = new TypeReference>(){}.getType(); + Type optionalIntType = new TypeReference>() { + }.getType(); assertEquals(Optional.of(Integer.decode("42")), defaultTransformer.transform("42", optionalIntType)); } @@ -140,11 +141,11 @@ public void should_convert_to_enum(final ParameterByTypeTransformer defaultTrans } private static class TestJacksonDefaultTransformer implements ParameterByTypeTransformer { - com.fasterxml.jackson.databind.ObjectMapper delegate = initMapper(); + ObjectMapper delegate = initMapper(); - private static com.fasterxml.jackson.databind.ObjectMapper initMapper() { - com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper(); - objectMapper.registerModule(new com.fasterxml.jackson.datatype.jdk8.Jdk8Module()); + private static ObjectMapper initMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new Jdk8Module()); return objectMapper; } diff --git a/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb b/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb index 424928772..50f303c4c 100644 --- a/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +++ b/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb @@ -1,6 +1,7 @@ require 'cucumber/cucumber_expressions/parameter_type' require 'cucumber/cucumber_expressions/errors' require 'cucumber/cucumber_expressions/cucumber_expression_generator' +require 'bigdecimal' module Cucumber module CucumberExpressions @@ -20,6 +21,7 @@ def initialize define_parameter_type(ParameterType.new('word', WORD_REGEXP, String, lambda {|s = nil| s}, false, false)) define_parameter_type(ParameterType.new('string', STRING_REGEXP, String, lambda { |s1, s2| arg = s1 != nil ? s1 : s2; arg.gsub(/\\"/, '"').gsub(/\\'/, "'")}, true, false)) define_parameter_type(ParameterType.new('', ANONYMOUS_REGEXP, String, lambda {|s = nil| s}, false, true)) + define_parameter_type(ParameterType.new('bigdecimal', FLOAT_REGEXP, BigDecimal, lambda {|s = nil| BigDecimal(s)}, false, false)) end def lookup_by_type_name(name) diff --git a/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb b/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb index 13baa299f..f00373326 100644 --- a/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb +++ b/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb @@ -18,7 +18,10 @@ module CucumberExpressions else cucumber_expression = CucumberExpression.new(expectation['expression'], parameter_registry) matches = cucumber_expression.match(expectation['text']) - values = matches.nil? ? nil : matches.map { |arg| arg.value(nil) } + values = matches.nil? ? nil : matches.map do |arg| + value = arg.value(nil) + BigDecimal === value ? value.to_s('f') : value + end expect(values).to eq(expectation['expected_args']) end end diff --git a/testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml b/testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml index fb9a9f412..6c08f2437 100644 --- a/testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml +++ b/testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml @@ -1,5 +1,5 @@ --- expression: "{bigdecimal}" -text: '3.14159265358979323846264338327950288419716939937510' +text: '3.1415926535897932384626433832795028841971693993751' expected_args: -- '3.14159265358979323846264338327950288419716939937510' +- '3.1415926535897932384626433832795028841971693993751' From 920ecfe5a071a8bfbd1814d005442b0531981b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Tue, 4 Jan 2022 00:53:42 +0000 Subject: [PATCH 10/17] Add 1024 bit bigdecimal in go --- go/cucumber_expression_test.go | 9 ++++- go/parameter_by_type_transformer.go | 8 ++++- go/parameter_type_registry.go | 54 +++++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/go/cucumber_expression_test.go b/go/cucumber_expression_test.go index 93b8b7f31..218787496 100644 --- a/go/cucumber_expression_test.go +++ b/go/cucumber_expression_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" "io/ioutil" + "math/big" "reflect" "regexp" "testing" @@ -189,7 +190,13 @@ func argumentValues(args []*Argument) []interface{} { } 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) + default: + result[i] = value + } } return result } diff --git a/go/parameter_by_type_transformer.go b/go/parameter_by_type_transformer.go index 228acca36..c5941db1a 100644 --- a/go/parameter_by_type_transformer.go +++ b/go/parameter_by_type_transformer.go @@ -3,6 +3,7 @@ package cucumberexpressions import ( "errors" "fmt" + "math/big" "reflect" "strconv" ) @@ -10,6 +11,8 @@ import ( // can be imported from "math/bits". Not yet supported in go 1.8 const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64 +const BigDecimalKind = reflect.Invalid + 1000 + type ParameterByTypeTransformer interface { // toValueType accepts either reflect.Type or reflect.Kind Transform(fromValue string, toValueType interface{}) (interface{}, error) @@ -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 @@ -100,6 +103,9 @@ func transformKind(fromValue string, toValueKind reflect.Kind) (interface{}, err return nil, err case reflect.Float64: return strconv.ParseFloat(fromValue, 64) + case BigDecimalKind: + floatVal, _, err := big.ParseFloat(fromValue, 10, 1024, big.ToNearestEven) + return floatVal, err default: return nil, createError(fromValue, toValueKind.String()) } diff --git a/go/parameter_type_registry.go b/go/parameter_type_registry.go index 0371575cf..9dc212838 100644 --- a/go/parameter_type_registry.go +++ b/go/parameter_type_registry.go @@ -37,6 +37,7 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { parameterTypesByRegexp: map[string][]*ParameterType{}, defaultTransformer: transformer, } + intParameterType, err := NewParameterType( "int", INTEGER_REGEXPS, @@ -55,7 +56,11 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { if err != nil { panic(err) } - result.DefineParameterType(intParameterType) + err = result.DefineParameterType(intParameterType) + if err != nil { + panic(err) + } + floatParameterType, err := NewParameterType( "float", FLOAT_REGEXPS, @@ -74,7 +79,34 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { if err != nil { panic(err) } - result.DefineParameterType(floatParameterType) + err = result.DefineParameterType(floatParameterType) + if err != nil { + panic(err) + } + + bigdecimalParameterType, err := NewParameterType( + "bigdecimal", + FLOAT_REGEXPS, + "float", + func(args ...*string) interface{} { + f, err := transformer.Transform(*args[0], BigDecimalKind) + if err != nil { + panic(err) + } + return f + }, + false, + false, + false, + ) + if err != nil { + panic(err) + } + err = result.DefineParameterType(bigdecimalParameterType) + if err != nil { + panic(err) + } + wordParameterType, err := NewParameterType( "word", WORD_REGEXPS, @@ -93,7 +125,11 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { if err != nil { panic(err) } - result.DefineParameterType(wordParameterType) + err = result.DefineParameterType(wordParameterType) + if err != nil { + panic(err) + } + stringParameterType, err := NewParameterType( "string", STRING_REGEXPS, @@ -124,13 +160,19 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { if err != nil { panic(err) } - result.DefineParameterType(stringParameterType) + err = result.DefineParameterType(stringParameterType) + if err != nil { + panic(err) + } - anonymouseParameterType, err := createAnonymousParameterType(ANONYMOUS_REGEXPS) + anonymousParameterType, err := createAnonymousParameterType(ANONYMOUS_REGEXPS) + if err != nil { + panic(err) + } + err = result.DefineParameterType(anonymousParameterType) if err != nil { panic(err) } - result.DefineParameterType(anonymouseParameterType) return result } From b4fa25081e13b67061fea4ca8f406c32650b2f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Tue, 4 Jan 2022 07:23:06 +0000 Subject: [PATCH 11/17] Implement {biginteger} on all implementations --- CHANGELOG.md | 2 +- go/cucumber_expression_test.go | 2 ++ go/parameter_by_type_transformer.go | 12 +++++++--- go/parameter_type_registry.go | 23 +++++++++++++++++++ .../cucumberexpressions/CustomMatchers.java | 3 +++ javascript/src/defineDefaultParameterTypes.ts | 13 +++++++++-- javascript/test/CucumberExpressionTest.ts | 9 +++++++- .../parameter_type_registry.rb | 1 + .../cucumber_expression_spec.rb | 11 ++++++++- ...eger-part.yaml => matches-bigdecimal.yaml} | 0 .../matching/matches-bigint.yaml | 5 ++++ 11 files changed, 73 insertions(+), 8 deletions(-) rename testdata/cucumber-expression/matching/{matches-bigdecimal-with-integer-part.yaml => matches-bigdecimal.yaml} (100%) create mode 100644 testdata/cucumber-expression/matching/matches-bigint.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 972b36202..8c51b5c2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added -- [Ruby] Add `bigdecimal` parameter type +- [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)) diff --git a/go/cucumber_expression_test.go b/go/cucumber_expression_test.go index 218787496..de2f0a5fc 100644 --- a/go/cucumber_expression_test.go +++ b/go/cucumber_expression_test.go @@ -194,6 +194,8 @@ func argumentValues(args []*Argument) []interface{} { switch v := value.(type) { case *big.Float: result[i] = fmt.Sprintf("%v", v) + case *big.Int: + result[i] = fmt.Sprintf("%v", v) default: result[i] = value } diff --git a/go/parameter_by_type_transformer.go b/go/parameter_by_type_transformer.go index c5941db1a..3de627662 100644 --- a/go/parameter_by_type_transformer.go +++ b/go/parameter_by_type_transformer.go @@ -1,7 +1,6 @@ package cucumberexpressions import ( - "errors" "fmt" "math/big" "reflect" @@ -12,6 +11,7 @@ import ( const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64 const BigDecimalKind = reflect.Invalid + 1000 +const BigIntKind = reflect.Invalid + 1001 type ParameterByTypeTransformer interface { // toValueType accepts either reflect.Type or reflect.Kind @@ -106,14 +106,20 @@ func transformKind(fromValue string, toValueKind interface{String() string}) (in case BigDecimalKind: 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) } diff --git a/go/parameter_type_registry.go b/go/parameter_type_registry.go index 9dc212838..537681f02 100644 --- a/go/parameter_type_registry.go +++ b/go/parameter_type_registry.go @@ -61,6 +61,29 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { panic(err) } + bigintParameterType, err := NewParameterType( + "biginteger", + INTEGER_REGEXPS, + "int", + func(args ...*string) interface{} { + i, err := transformer.Transform(*args[0], BigIntKind) + if err != nil { + panic(err) + } + return i + }, + false, + false, + false, + ) + if err != nil { + panic(err) + } + err = result.DefineParameterType(bigintParameterType) + if err != nil { + panic(err) + } + floatParameterType, err := NewParameterType( "float", FLOAT_REGEXPS, diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java b/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java index d08c0ba56..c1eda1ffa 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java @@ -8,6 +8,7 @@ import org.hamcrest.number.IsCloseTo; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.List; import java.util.stream.Collectors; @@ -32,6 +33,8 @@ public EqualOrCloseTo(Object expectedValue) { public boolean matches(Object actual) { if(actual instanceof BigDecimal) { return new IsEqual(this.expectedValue).matches(actual.toString()); + } else if(actual instanceof BigInteger) { + return new IsEqual(this.expectedValue).matches(actual.toString()); } else if(actual instanceof Double || actual instanceof Float) { return new IsCloseTo(((Double)this.expectedValue), 0.0001).matches(((Number)actual).doubleValue()); } else if(actual instanceof Number || actual instanceof String || actual == null) { diff --git a/javascript/src/defineDefaultParameterTypes.ts b/javascript/src/defineDefaultParameterTypes.ts index 8d9a6c0d8..ad9dc6b1d 100644 --- a/javascript/src/defineDefaultParameterTypes.ts +++ b/javascript/src/defineDefaultParameterTypes.ts @@ -45,8 +45,6 @@ export default function defineDefaultParameterTypes(registry: DefinesParameterTy new ParameterType('', ANONYMOUS_REGEXP, String, (s) => s, false, true) ) - // TODO: conditionally define this - registry.defineParameterType( new ParameterType( 'bigdecimal', @@ -57,4 +55,15 @@ export default function defineDefaultParameterTypes(registry: DefinesParameterTy false ) ) + + registry.defineParameterType( + new ParameterType( + 'biginteger', + INTEGER_REGEXPS, + Number, + (s) => (s === undefined ? null : BigInt(s)), + false, + false + ) + ) } diff --git a/javascript/test/CucumberExpressionTest.ts b/javascript/test/CucumberExpressionTest.ts index 86c4419d1..906456439 100644 --- a/javascript/test/CucumberExpressionTest.ts +++ b/javascript/test/CucumberExpressionTest.ts @@ -25,7 +25,14 @@ describe('CucumberExpression', () => { const expression = new CucumberExpression(expectation.expression, parameterTypeRegistry) const matches = expression.match(expectation.text) assert.deepStrictEqual( - JSON.parse(JSON.stringify(matches ? matches.map((value) => value.getValue(null)) : null)), // Removes type information. + JSON.parse( + JSON.stringify( + matches ? matches.map((value) => value.getValue(null)) : null, + (key, value) => { + return typeof value === 'bigint' ? value.toString() : value + } + ) + ), // Removes type information. expectation.expected_args ) } else if (expectation.exception !== undefined) { diff --git a/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb b/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb index 50f303c4c..ce2653f74 100644 --- a/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +++ b/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb @@ -22,6 +22,7 @@ def initialize define_parameter_type(ParameterType.new('string', STRING_REGEXP, String, lambda { |s1, s2| arg = s1 != nil ? s1 : s2; arg.gsub(/\\"/, '"').gsub(/\\'/, "'")}, true, false)) define_parameter_type(ParameterType.new('', ANONYMOUS_REGEXP, String, lambda {|s = nil| s}, false, true)) define_parameter_type(ParameterType.new('bigdecimal', FLOAT_REGEXP, BigDecimal, lambda {|s = nil| BigDecimal(s)}, false, false)) + define_parameter_type(ParameterType.new('biginteger', INTEGER_REGEXPS, Integer, lambda {|s = nil| s && s.to_i}, false, false)) end def lookup_by_type_name(name) diff --git a/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb b/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb index f00373326..12d8e3fc1 100644 --- a/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb +++ b/ruby/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb @@ -20,7 +20,16 @@ module CucumberExpressions matches = cucumber_expression.match(expectation['text']) values = matches.nil? ? nil : matches.map do |arg| value = arg.value(nil) - BigDecimal === value ? value.to_s('f') : value + case value + when BigDecimal + # Format {bigdecimal} as string (because it must be a string in matches-bigdecimal.yaml) + value.to_s('f') + when Integer + # Format {bigint} as string (because it must be a string in matches-bigint.yaml) + value.bit_length > 64 ? value.to_s : value + else + value + end end expect(values).to eq(expectation['expected_args']) end diff --git a/testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml b/testdata/cucumber-expression/matching/matches-bigdecimal.yaml similarity index 100% rename from testdata/cucumber-expression/matching/matches-bigdecimal-with-integer-part.yaml rename to testdata/cucumber-expression/matching/matches-bigdecimal.yaml diff --git a/testdata/cucumber-expression/matching/matches-bigint.yaml b/testdata/cucumber-expression/matching/matches-bigint.yaml new file mode 100644 index 000000000..1fc8e8e6c --- /dev/null +++ b/testdata/cucumber-expression/matching/matches-bigint.yaml @@ -0,0 +1,5 @@ +--- +expression: "{biginteger}" +text: '31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679' +expected_args: +- '31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679' From 9d7e1818c90f374a1628febe8b560715b40b8576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Tue, 4 Jan 2022 08:34:25 +0000 Subject: [PATCH 12/17] Add support for byte --- go/cucumber_expression_test.go | 2 ++ go/parameter_type_registry.go | 23 +++++++++++++++++++ .../CucumberExpressionTest.java | 3 ++- .../cucumberexpressions/CustomMatchers.java | 2 ++ .../parameter_type_registry.rb | 1 + ...es-bigint.yaml => matches-biginteger.yaml} | 0 .../matching/matches-byte.yaml | 5 ++++ 7 files changed, 35 insertions(+), 1 deletion(-) rename testdata/cucumber-expression/matching/{matches-bigint.yaml => matches-biginteger.yaml} (100%) create mode 100644 testdata/cucumber-expression/matching/matches-byte.yaml diff --git a/go/cucumber_expression_test.go b/go/cucumber_expression_test.go index de2f0a5fc..089c4f785 100644 --- a/go/cucumber_expression_test.go +++ b/go/cucumber_expression_test.go @@ -196,6 +196,8 @@ func argumentValues(args []*Argument) []interface{} { result[i] = fmt.Sprintf("%v", v) case *big.Int: result[i] = fmt.Sprintf("%v", v) + case int8: + result[i] = int(v) default: result[i] = value } diff --git a/go/parameter_type_registry.go b/go/parameter_type_registry.go index 537681f02..c20b32b0c 100644 --- a/go/parameter_type_registry.go +++ b/go/parameter_type_registry.go @@ -107,6 +107,29 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { panic(err) } + byteParameterType, err := NewParameterType( + "byte", + FLOAT_REGEXPS, + "byte", + func(args ...*string) interface{} { + f, err := transformer.Transform(*args[0], reflect.Int8) + if err != nil { + panic(err) + } + return f + }, + false, + false, + false, + ) + if err != nil { + panic(err) + } + err = result.DefineParameterType(byteParameterType) + if err != nil { + panic(err) + } + bigdecimalParameterType, err := NewParameterType( "bigdecimal", FLOAT_REGEXPS, diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java index d9554df54..6809a1432 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java @@ -40,7 +40,8 @@ private static List acceptance_tests_pass() throws IOException { List paths = new ArrayList<>(); newDirectoryStream(Paths.get("..", "testdata", "cucumber-expression", "matching")).forEach(paths::add); paths.sort(Comparator.naturalOrder()); - return paths; +// return paths; + return singletonList(Paths.get("..", "testdata", "cucumber-expression", "matching", "matches-byte.yaml")); } @ParameterizedTest diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java b/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java index c1eda1ffa..07934b07b 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/CustomMatchers.java @@ -37,6 +37,8 @@ public boolean matches(Object actual) { return new IsEqual(this.expectedValue).matches(actual.toString()); } else if(actual instanceof Double || actual instanceof Float) { return new IsCloseTo(((Double)this.expectedValue), 0.0001).matches(((Number)actual).doubleValue()); + } else if(actual instanceof Byte) { + return new IsEqual(((Integer)this.expectedValue).byteValue()).matches(actual); } else if(actual instanceof Number || actual instanceof String || actual == null) { return new IsEqual(this.expectedValue).matches(actual); } diff --git a/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb b/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb index ce2653f74..23572170b 100644 --- a/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +++ b/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb @@ -23,6 +23,7 @@ def initialize define_parameter_type(ParameterType.new('', ANONYMOUS_REGEXP, String, lambda {|s = nil| s}, false, true)) define_parameter_type(ParameterType.new('bigdecimal', FLOAT_REGEXP, BigDecimal, lambda {|s = nil| BigDecimal(s)}, false, false)) define_parameter_type(ParameterType.new('biginteger', INTEGER_REGEXPS, Integer, lambda {|s = nil| s && s.to_i}, false, false)) + define_parameter_type(ParameterType.new('byte', INTEGER_REGEXPS, Integer, lambda {|s = nil| s && s.to_i}, false, false)) end def lookup_by_type_name(name) diff --git a/testdata/cucumber-expression/matching/matches-bigint.yaml b/testdata/cucumber-expression/matching/matches-biginteger.yaml similarity index 100% rename from testdata/cucumber-expression/matching/matches-bigint.yaml rename to testdata/cucumber-expression/matching/matches-biginteger.yaml diff --git a/testdata/cucumber-expression/matching/matches-byte.yaml b/testdata/cucumber-expression/matching/matches-byte.yaml new file mode 100644 index 000000000..8973421ec --- /dev/null +++ b/testdata/cucumber-expression/matching/matches-byte.yaml @@ -0,0 +1,5 @@ +--- +expression: "{byte}" +text: '127' +expected_args: +- 127 From 50c5f9ba1c81da5fbcce0a2cc6c6b00f8b397082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Tue, 4 Jan 2022 10:09:51 +0000 Subject: [PATCH 13/17] Support remaining types --- CHANGELOG.md | 4 + go/cucumber_expression_test.go | 18 ++++- go/parameter_type_registry.go | 77 ++++++++++++++++++- javascript/src/defineDefaultParameterTypes.ts | 44 +++++++++++ .../parameter_type_registry.rb | 3 + .../matching/matches-double.yaml | 5 ++ .../matching/matches-float.yaml | 5 ++ .../matching/matches-int.yaml | 4 +- .../matching/matches-long.yaml | 5 ++ .../matching/matches-short.yaml | 5 ++ 10 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 testdata/cucumber-expression/matching/matches-double.yaml create mode 100644 testdata/cucumber-expression/matching/matches-float.yaml create mode 100644 testdata/cucumber-expression/matching/matches-long.yaml create mode 100644 testdata/cucumber-expression/matching/matches-short.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c51b5c2d..8137784a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ 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 - [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 diff --git a/go/cucumber_expression_test.go b/go/cucumber_expression_test.go index 089c4f785..0a07faa20 100644 --- a/go/cucumber_expression_test.go +++ b/go/cucumber_expression_test.go @@ -8,6 +8,7 @@ import ( "math/big" "reflect" "regexp" + "strconv" "testing" ) @@ -28,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) { @@ -76,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)) @@ -181,10 +186,10 @@ 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 } @@ -198,6 +203,13 @@ func argumentValues(args []*Argument) []interface{} { 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 } diff --git a/go/parameter_type_registry.go b/go/parameter_type_registry.go index c20b32b0c..98906433c 100644 --- a/go/parameter_type_registry.go +++ b/go/parameter_type_registry.go @@ -87,9 +87,9 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { floatParameterType, err := NewParameterType( "float", FLOAT_REGEXPS, - "float", + "float32", func(args ...*string) interface{} { - f, err := transformer.Transform(*args[0], reflect.Float64) + f, err := transformer.Transform(*args[0], reflect.Float32) if err != nil { panic(err) } @@ -107,10 +107,33 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { panic(err) } + doubleParameterType, err := NewParameterType( + "double", + FLOAT_REGEXPS, + "float64", + func(args ...*string) interface{} { + f, err := transformer.Transform(*args[0], reflect.Float64) + if err != nil { + panic(err) + } + return f + }, + false, + false, + false, + ) + if err != nil { + panic(err) + } + err = result.DefineParameterType(doubleParameterType) + if err != nil { + panic(err) + } + byteParameterType, err := NewParameterType( "byte", FLOAT_REGEXPS, - "byte", + "int8", func(args ...*string) interface{} { f, err := transformer.Transform(*args[0], reflect.Int8) if err != nil { @@ -130,10 +153,56 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { panic(err) } + shortParameterType, err := NewParameterType( + "short", + FLOAT_REGEXPS, + "int16", + func(args ...*string) interface{} { + f, err := transformer.Transform(*args[0], reflect.Int16) + if err != nil { + panic(err) + } + return f + }, + false, + false, + false, + ) + if err != nil { + panic(err) + } + err = result.DefineParameterType(shortParameterType) + if err != nil { + panic(err) + } + + longParameterType, err := NewParameterType( + "long", + FLOAT_REGEXPS, + "int32", + func(args ...*string) interface{} { + f, err := transformer.Transform(*args[0], reflect.Int64) + if err != nil { + panic(err) + } + return f + }, + false, + false, + false, + ) + if err != nil { + panic(err) + } + err = result.DefineParameterType(longParameterType) + if err != nil { + panic(err) + } + bigdecimalParameterType, err := NewParameterType( "bigdecimal", FLOAT_REGEXPS, - "float", + "BigFloat", func(args ...*string) interface{} { f, err := transformer.Transform(*args[0], BigDecimalKind) if err != nil { diff --git a/javascript/src/defineDefaultParameterTypes.ts b/javascript/src/defineDefaultParameterTypes.ts index ad9dc6b1d..ada1a9504 100644 --- a/javascript/src/defineDefaultParameterTypes.ts +++ b/javascript/src/defineDefaultParameterTypes.ts @@ -45,6 +45,17 @@ export default function defineDefaultParameterTypes(registry: DefinesParameterTy new ParameterType('', ANONYMOUS_REGEXP, String, (s) => s, false, true) ) + registry.defineParameterType( + new ParameterType( + 'double', + FLOAT_REGEXP, + Number, + (s) => (s === undefined ? null : parseFloat(s)), + false, + false + ) + ) + registry.defineParameterType( new ParameterType( 'bigdecimal', @@ -56,6 +67,39 @@ export default function defineDefaultParameterTypes(registry: DefinesParameterTy ) ) + registry.defineParameterType( + new ParameterType( + 'byte', + INTEGER_REGEXPS, + Number, + (s) => (s === undefined ? null : Number(s)), + false, + false + ) + ) + + registry.defineParameterType( + new ParameterType( + 'short', + INTEGER_REGEXPS, + Number, + (s) => (s === undefined ? null : Number(s)), + false, + false + ) + ) + + registry.defineParameterType( + new ParameterType( + 'long', + INTEGER_REGEXPS, + Number, + (s) => (s === undefined ? null : Number(s)), + false, + false + ) + ) + registry.defineParameterType( new ParameterType( 'biginteger', diff --git a/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb b/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb index 23572170b..de78ad981 100644 --- a/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +++ b/ruby/lib/cucumber/cucumber_expressions/parameter_type_registry.rb @@ -24,6 +24,9 @@ def initialize define_parameter_type(ParameterType.new('bigdecimal', FLOAT_REGEXP, BigDecimal, lambda {|s = nil| BigDecimal(s)}, false, false)) define_parameter_type(ParameterType.new('biginteger', INTEGER_REGEXPS, Integer, lambda {|s = nil| s && s.to_i}, false, false)) define_parameter_type(ParameterType.new('byte', INTEGER_REGEXPS, Integer, lambda {|s = nil| s && s.to_i}, false, false)) + define_parameter_type(ParameterType.new('short', INTEGER_REGEXPS, Integer, lambda {|s = nil| s && s.to_i}, false, false)) + define_parameter_type(ParameterType.new('long', INTEGER_REGEXPS, Integer, lambda {|s = nil| s && s.to_i}, false, false)) + define_parameter_type(ParameterType.new('double', FLOAT_REGEXP, Float, lambda {|s = nil| s && s.to_f}, false, false)) end def lookup_by_type_name(name) diff --git a/testdata/cucumber-expression/matching/matches-double.yaml b/testdata/cucumber-expression/matching/matches-double.yaml new file mode 100644 index 000000000..a0f908fa7 --- /dev/null +++ b/testdata/cucumber-expression/matching/matches-double.yaml @@ -0,0 +1,5 @@ +--- +expression: "{double}" +text: '3.141592653589793' +expected_args: +- 3.141592653589793 diff --git a/testdata/cucumber-expression/matching/matches-float.yaml b/testdata/cucumber-expression/matching/matches-float.yaml new file mode 100644 index 000000000..0832e2817 --- /dev/null +++ b/testdata/cucumber-expression/matching/matches-float.yaml @@ -0,0 +1,5 @@ +--- +expression: "{float}" +text: '3.141593' +expected_args: +- 3.141593 diff --git a/testdata/cucumber-expression/matching/matches-int.yaml b/testdata/cucumber-expression/matching/matches-int.yaml index 9ffbd4eae..91d6f1517 100644 --- a/testdata/cucumber-expression/matching/matches-int.yaml +++ b/testdata/cucumber-expression/matching/matches-int.yaml @@ -1,5 +1,5 @@ --- expression: "{int}" -text: '22' +text: '2147483647' expected_args: -- 22 +- 2147483647 diff --git a/testdata/cucumber-expression/matching/matches-long.yaml b/testdata/cucumber-expression/matching/matches-long.yaml new file mode 100644 index 000000000..06c872d20 --- /dev/null +++ b/testdata/cucumber-expression/matching/matches-long.yaml @@ -0,0 +1,5 @@ +--- +expression: "{long}" +text: '9223372036854775807' +expected_args: +- 9223372036854775807 diff --git a/testdata/cucumber-expression/matching/matches-short.yaml b/testdata/cucumber-expression/matching/matches-short.yaml new file mode 100644 index 000000000..887d66ce4 --- /dev/null +++ b/testdata/cucumber-expression/matching/matches-short.yaml @@ -0,0 +1,5 @@ +--- +expression: "{short}" +text: '32767' +expected_args: +- 32767 From f69bd0da15a52eeb3e9b3ab6f09c04fbbe1f1fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Tue, 4 Jan 2022 10:14:35 +0000 Subject: [PATCH 14/17] Remove conditional definition of types --- javascript/src/ParameterTypeRegistry.ts | 6 ++---- javascript/test/ParameterTypeRegistryTest.ts | 8 -------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/javascript/src/ParameterTypeRegistry.ts b/javascript/src/ParameterTypeRegistry.ts index 3ff05d2cc..5ee760257 100644 --- a/javascript/src/ParameterTypeRegistry.ts +++ b/javascript/src/ParameterTypeRegistry.ts @@ -9,10 +9,8 @@ export default class ParameterTypeRegistry implements DefinesParameterType { private readonly parameterTypeByName = new Map>() private readonly parameterTypesByRegexp = new Map>>() - constructor(withDefaultParameterTypes = true) { - if (withDefaultParameterTypes) { - defineDefaultParameterTypes(this) - } + constructor() { + defineDefaultParameterTypes(this) } get parameterTypes(): IterableIterator> { diff --git a/javascript/test/ParameterTypeRegistryTest.ts b/javascript/test/ParameterTypeRegistryTest.ts index 46715b8af..f8a5548de 100644 --- a/javascript/test/ParameterTypeRegistryTest.ts +++ b/javascript/test/ParameterTypeRegistryTest.ts @@ -55,12 +55,4 @@ describe('ParameterTypeRegistry', () => { person ) }) - - it('can be instantiated without default parameter types', () => { - registry = new ParameterTypeRegistry(false) - - const parameterType = new ParameterType('int', /\d+/, Number, (s) => +s, true, true) - registry.defineParameterType(parameterType) - assert.strictEqual(registry.lookupByTypeName('int'), parameterType) - }) }) From 9cff5d1f8e6192b27bbb2105b16c255c70c46175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Tue, 4 Jan 2022 10:25:33 +0000 Subject: [PATCH 15/17] Update docs --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1beded019..7230ecb40 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 From 3f0bedef60bba436def80275a68f7f60d735a468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Tue, 4 Jan 2022 11:15:04 +0000 Subject: [PATCH 16/17] Add unit test --- go/parameter_by_type_transformer.go | 4 ++-- go/parameter_by_type_transformer_test.go | 16 +++++++++++++++- go/parameter_type_registry.go | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/go/parameter_by_type_transformer.go b/go/parameter_by_type_transformer.go index 3de627662..d5bc9ccca 100644 --- a/go/parameter_by_type_transformer.go +++ b/go/parameter_by_type_transformer.go @@ -10,7 +10,7 @@ import ( // can be imported from "math/bits". Not yet supported in go 1.8 const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64 -const BigDecimalKind = reflect.Invalid + 1000 +const BigFloatKind = reflect.Invalid + 1000 const BigIntKind = reflect.Invalid + 1001 type ParameterByTypeTransformer interface { @@ -103,7 +103,7 @@ func transformKind(fromValue string, toValueKind interface{String() string}) (in return nil, err case reflect.Float64: return strconv.ParseFloat(fromValue, 64) - case BigDecimalKind: + case BigFloatKind: floatVal, _, err := big.ParseFloat(fromValue, 10, 1024, big.ToNearestEven) return floatVal, err case BigIntKind: diff --git a/go/parameter_by_type_transformer_test.go b/go/parameter_by_type_transformer_test.go index aa954c7f6..510683f4e 100644 --- a/go/parameter_by_type_transformer_test.go +++ b/go/parameter_by_type_transformer_test.go @@ -2,6 +2,7 @@ package cucumberexpressions import ( "github.com/stretchr/testify/require" + "math/big" "reflect" "testing" ) @@ -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) @@ -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) diff --git a/go/parameter_type_registry.go b/go/parameter_type_registry.go index 98906433c..8265f0126 100644 --- a/go/parameter_type_registry.go +++ b/go/parameter_type_registry.go @@ -204,7 +204,7 @@ func NewParameterTypeRegistry() *ParameterTypeRegistry { FLOAT_REGEXPS, "BigFloat", func(args ...*string) interface{} { - f, err := transformer.Transform(*args[0], BigDecimalKind) + f, err := transformer.Transform(*args[0], BigFloatKind) if err != nil { panic(err) } From cde50139d1a51c756f98b9a0ec16c2420fbd2516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= <1000+aslakhellesoy@users.noreply.github.com> Date: Tue, 4 Jan 2022 13:07:44 +0000 Subject: [PATCH 17/17] Update java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Aurélien Reeves --- .../io/cucumber/cucumberexpressions/CucumberExpressionTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java index 6809a1432..8e3fef456 100644 --- a/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java +++ b/java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java @@ -40,7 +40,6 @@ private static List acceptance_tests_pass() throws IOException { List paths = new ArrayList<>(); newDirectoryStream(Paths.get("..", "testdata", "cucumber-expression", "matching")).forEach(paths::add); paths.sort(Comparator.naturalOrder()); -// return paths; return singletonList(Paths.get("..", "testdata", "cucumber-expression", "matching", "matches-byte.yaml")); }