diff --git a/core/src/main/java/cucumber/api/DataTable.java b/core/src/main/java/cucumber/api/DataTable.java index 316ccfaa11..9bef0f825d 100644 --- a/core/src/main/java/cucumber/api/DataTable.java +++ b/core/src/main/java/cucumber/api/DataTable.java @@ -169,6 +169,29 @@ public void diff(DataTable other) throws TableDiffException { new TableDiffer(this, other).calculateDiffs(); } + /** + * Diffs this table with {@code other}. + * The order is not important. A set-difference is applied. + * @param other the other table to diff with. + * @throws TableDiffException if the tables are different. + */ + public void unorderedDiff(DataTable other) throws TableDiffException { + new TableDiffer(this, other).calculateUnorderedDiffs(); + } + + /** + * Diffs this table with {@code other}, which can be a {@code List<List<String>>} or a + * {@code List<YourType>}. + * + * @param other the other table to diff with. + * @throws cucumber.runtime.table.TableDiffException if the tables are different. + */ + public void unorderedDiff(List other) throws TableDiffException { + List topCells = topCells(); + DataTable otherTable = toTable(other, topCells.toArray(new String[topCells.size()])); + unorderedDiff(otherTable); + } + /** * Internal method. Do not use. * diff --git a/core/src/main/java/cucumber/runtime/table/TableDiffer.java b/core/src/main/java/cucumber/runtime/table/TableDiffer.java index 1d15221867..890702eccb 100644 --- a/core/src/main/java/cucumber/runtime/table/TableDiffer.java +++ b/core/src/main/java/cucumber/runtime/table/TableDiffer.java @@ -8,6 +8,7 @@ import gherkin.formatter.model.Row; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,6 +39,50 @@ public void calculateDiffs() throws TableDiffException { } } + public void calculateUnorderedDiffs() throws TableDiffException { + boolean isDifferent = false; + List diffTableRows = new ArrayList(); + List> missingRow = new ArrayList>(); + + ArrayList> extraRows = new ArrayList>(); + + // 1. add all "to" row in extra table + // 2. iterate over "from", when a common row occurs, remove it from extraRows + // finally, only extra rows are kept and in same order that in "to". + extraRows.addAll(to.raw()); + + int i = 1; + for (DataTableRow r : from.getGherkinRows()) { + if (!to.raw().contains(r.getCells())) { + missingRow.add(r.getCells()); + diffTableRows.add( + new DataTableRow(r.getComments(), + r.getCells(), + i, + Row.DiffType.DELETE)); + isDifferent = true; + } else { + diffTableRows.add( + new DataTableRow(r.getComments(), + r.getCells(), + i++)); + extraRows.remove(r.getCells()); + } + } + + for (List e : extraRows) { + diffTableRows.add(new DataTableRow(Collections.EMPTY_LIST, + e, + i++, + Row.DiffType.INSERT)); + isDifferent = true; + } + + if (isDifferent) { + throw new TableDiffException(from, to, new DataTable(diffTableRows, from.getTableConverter())); + } + } + private Map createDeltasByLine(List deltas) { Map deltasByLine = new HashMap(); for (Delta delta : deltas) { diff --git a/core/src/test/java/cucumber/runtime/table/TableDifferTest.java b/core/src/test/java/cucumber/runtime/table/TableDifferTest.java index 54acf4df24..01aef46e9d 100755 --- a/core/src/test/java/cucumber/runtime/table/TableDifferTest.java +++ b/core/src/test/java/cucumber/runtime/table/TableDifferTest.java @@ -21,6 +21,17 @@ private DataTable table() { return TableParser.parse(source, null); } + private DataTable tableWithDuplicate() { + String source = "" + + "| Aslak | aslak@email.com | 123 |\n" + + "| Joe | joe@email.com | 234 |\n" + + "| Bryan | bryan@email.org | 456 |\n" + + "| Joe | joe@email.com | 234 |\n" + + "| Ni | ni@email.com | 654 |\n" + + "| Ni | ni@email.com | 654 |\n" ; + return TableParser.parse(source, null); + } + private DataTable otherTableWithTwoConsecutiveRowsDeleted() { String source = "" + "| Aslak | aslak@email.com | 123 |\n" + @@ -69,6 +80,49 @@ private DataTable otherTableWithInsertedAtEnd() { return TableParser.parse(source, null); } + private DataTable otherTableWithDifferentOrder() { + String source = "" + + "| Joe | joe@email.com | 234 |\n" + + "| Aslak | aslak@email.com | 123 |\n" + + "| Bryan | bryan@email.org | 456 |\n" + + "| Ni | ni@email.com | 654 |\n"; + return TableParser.parse(source, null); + } + + private DataTable otherTableWithDifferentOrderAndDuplicate() { + String source = "" + + "| Joe | joe@email.com | 234 |\n" + + "| Aslak | aslak@email.com | 123 |\n" + + "| Bryan | bryan@email.org | 456 |\n" + + "| Ni | ni@email.com | 654 |\n"+ + "| Ni | ni@email.com | 654 |\n" + + "| Joe | joe@email.com | 234 |\n" ; + return TableParser.parse(source, null); + } + + private DataTable otherTableWithDifferentOrderDuplicateAndDeleted() { + String source = "" + + "| Joe | joe@email.com | 234 |\n" + + "| Bryan | bryan@email.org | 456 |\n" + + "| Bryan | bryan@email.org | 456 |\n" + + "| Ni | ni@email.com | 654 |\n" + + "| Bob | bob.email.com | 555 |\n" + + "| Bryan | bryan@email.org | 456 |\n" + + "| Ni | ni@email.com | 654 |\n" + + "| Joe | joe@email.com | 234 |\n" ; + + return TableParser.parse(source, null); + } + + private DataTable otherTableWithDeletedAndInsertedDifferentOrder() { + String source = "" + + "| Doe | joe@email.com | 234 |\n" + + "| Foo | schnickens@email.net | 789 |\n" + + "| Aslak | aslak@email.com | 123 |\n" + + "| Bryan | bryan@email.org | 456 |\n"; + return TableParser.parse(source, null); + } + @Test(expected = TableDiffException.class) public void shouldFindDifferences() { try { @@ -246,4 +300,129 @@ public void diff_with_list_of_pojos_and_camelcase_header_mapping() { actual.add(new TestPojo(3, "jdoe", 34545)); expected.diff(actual); } + + @Test + public void diff_set_with_itself() { + table().unorderedDiff(table()); + } + + @Test + public void diff_set_with_itself_in_different_order() { + DataTable other = otherTableWithDifferentOrder(); + table().unorderedDiff(other); + } + + @Test(expected = TableDiffException.class) + public void diff_set_with_less_lines_in_other() { + DataTable other = otherTableWithTwoConsecutiveRowsDeleted(); + try { + table().unorderedDiff(other); + } catch (TableDiffException e) { + String expected = "" + + "Tables were not identical:\n" + + " | Aslak | aslak@email.com | 123 |\n" + + " - | Joe | joe@email.com | 234 |\n" + + " - | Bryan | bryan@email.org | 456 |\n" + + " | Ni | ni@email.com | 654 |\n"; + assertEquals(expected, e.getMessage()); + throw e; + } + } + + @Test(expected = TableDiffException.class) + public void unordered_diff_with_more_lines_in_other() { + DataTable other = otherTableWithTwoConsecutiveRowsInserted(); + try { + table().unorderedDiff(other); + } catch (TableDiffException e) { + String expected = "" + + "Tables were not identical:\n" + + " | Aslak | aslak@email.com | 123 |\n" + + " | Joe | joe@email.com | 234 |\n" + + " | Bryan | bryan@email.org | 456 |\n" + + " | Ni | ni@email.com | 654 |\n" + + " + | Doe | joe@email.com | 234 |\n" + + " + | Foo | schnickens@email.net | 789 |\n"; + assertEquals(expected, e.getMessage()); + throw e; + } + } + + @Test(expected = TableDiffException.class) + public void unordered_diff_with_added_and_deleted_rows_in_other() { + DataTable other = otherTableWithDeletedAndInsertedDifferentOrder(); + try { + table().unorderedDiff(other); + } catch (TableDiffException e) { + String expected = "" + + "Tables were not identical:\n" + + " | Aslak | aslak@email.com | 123 |\n" + + " - | Joe | joe@email.com | 234 |\n" + + " | Bryan | bryan@email.org | 456 |\n" + + " - | Ni | ni@email.com | 654 |\n" + + " + | Doe | joe@email.com | 234 |\n" + + " + | Foo | schnickens@email.net | 789 |\n"; + assertEquals(expected, e.getMessage()); + throw e; + } + } + + @Test + public void unordered_diff_with_list_of_pojos_and_camelcase_header_mapping() { + String source = "" + + "| id | Given Name |\n" + + "| 1 | me |\n" + + "| 2 | you |\n" + + "| 3 | jdoe |\n"; + + DataTable expected = TableParser.parse(source, null); + + List actual = new ArrayList(); + actual.add(new TestPojo(2, "you", 222)); + actual.add(new TestPojo(3, "jdoe", 34545)); + actual.add(new TestPojo(1, "me", 123)); + expected.unorderedDiff(actual); + } + + @Test(expected = TableDiffException.class) + public void unordered_diff_with_added_duplicate_in_other() { + DataTable other = otherTableWithDifferentOrderAndDuplicate(); + try { + table().unorderedDiff(other); + } catch (TableDiffException e) { + System.out.println(e.getMessage()); + String expected = "" + + "Tables were not identical:\n" + + " | Aslak | aslak@email.com | 123 |\n" + + " | Joe | joe@email.com | 234 |\n" + + " | Bryan | bryan@email.org | 456 |\n" + + " | Ni | ni@email.com | 654 |\n" + + " + | Ni | ni@email.com | 654 |\n" + + " + | Joe | joe@email.com | 234 |\n" ; + assertEquals(expected, e.getMessage()); + throw e; + } + } + + @Test(expected = TableDiffException.class) + public void unordered_diff_with_added_duplicate_and_deleted_in_other() { + DataTable other = otherTableWithDifferentOrderDuplicateAndDeleted(); + try { + tableWithDuplicate().unorderedDiff(other); + } catch (TableDiffException e) { + String expected = "" + + "Tables were not identical:\n" + + " - | Aslak | aslak@email.com | 123 |\n" + + " | Joe | joe@email.com | 234 |\n" + + " | Bryan | bryan@email.org | 456 |\n" + + " | Joe | joe@email.com | 234 |\n" + + " | Ni | ni@email.com | 654 |\n" + + " | Ni | ni@email.com | 654 |\n" + + " + | Bryan | bryan@email.org | 456 |\n" + + " + | Bob | bob.email.com | 555 |\n" + + " + | Bryan | bryan@email.org | 456 |\n" ; + assertEquals(expected, e.getMessage()); + throw e; + } + } }