├── .gitignore ├── .gs-project.yml ├── img └── html_report.png ├── .gitlab-ci.yml ├── tablasco-junit ├── src │ ├── test │ │ ├── resources │ │ │ ├── logging.properties │ │ │ ├── JsonTableFactoryTest_object.txt │ │ │ ├── JsonTableFactoryTest_emptySimpleArray.txt │ │ │ ├── JsonTableFactoryTest_array.txt │ │ │ ├── JsonTableFactoryTest_simpleArray.txt │ │ │ ├── CreateActualResultsOnFailureTest_testTrueFail.txt │ │ │ ├── JsonTableFactoryTest_singleSimpleArray.txt │ │ │ ├── CreateActualResultsOnFailureTest_testFalseFail.txt │ │ │ ├── CreateActualResultsTest_testFalse.txt │ │ │ ├── CreateActualResultsTest_testTrue.txt │ │ │ ├── CustomFileAndDirectoryStrategyTest.txt │ │ │ ├── ClassAnnotationTest.txt │ │ │ ├── CreateActualResultsTest_testDefault.txt │ │ │ ├── JsonTableFactoryTest_arrayOfSimpleArrays.txt │ │ │ ├── maven_input │ │ │ │ └── MavenStyleDirectoryStrategyTest.txt │ │ │ ├── CreateActualResultsOnFailureTest_testTrue.txt │ │ │ ├── CreateActualResultsOnFailureTest_testFalse.txt │ │ │ ├── JsonTableFactoryTest_deepSimpleArray.txt │ │ │ ├── JsonTableFactoryTest_arrayWithObjectsWithDifferentKeys.txt │ │ │ ├── JsonTableFactoryTest_arrayWithObjectsWithDifferentKeysAndTypes.txt │ │ │ ├── JsonTableFactoryTest_multiSimpleArray.txt │ │ │ ├── JsonTableFactoryTest_emptySimpleArrayWithParent.txt │ │ │ ├── ExampleTableVerifierTest_example.txt │ │ │ ├── JsonTableFactoryTest_deepObject.txt │ │ │ ├── ExpectedResultsLoaderTest.txt │ │ │ ├── JsonTableFactoryTest_objectWithArrayAndObject.txt │ │ │ ├── JsonTableFactoryTest_objectWithArray.txt │ │ │ ├── JsonTableFactoryTest_arrayOfObjectsWithArrays.txt │ │ │ ├── JsonTableFactoryTest_objectWithMultiArraysA.txt │ │ │ ├── JsonTableFactoryTest_objectWithMultiArraysB.txt │ │ │ ├── JsonTableFactoryTest_objectWithMultiArraysC.txt │ │ │ ├── ExpectedResultsParserTest.txt │ │ │ ├── examples │ │ │ │ ├── BswCpsAnalysisRegressionTest_regrBalanceBusinessType_ACTUAL.txt │ │ │ │ └── BswCpsAnalysisRegressionTest_regrBalanceBusinessType_EXPECTED.txt │ │ │ └── JsonTableFactoryTest_deepArraysWithParentsWithDifferentKeys.txt │ │ └── java │ │ │ └── com │ │ │ └── gs │ │ │ └── tablasco │ │ │ ├── NoExpectedResultsTest.java │ │ │ ├── MavenStyleDirectoryStrategyTest.java │ │ │ ├── files │ │ │ └── FixedDirectoryStrategyTest.java │ │ │ ├── verify │ │ │ ├── MetadataTest.java │ │ │ ├── indexmap │ │ │ │ ├── IndexMapGeneratorTest.java │ │ │ │ ├── UnmatchedIndexMapTest.java │ │ │ │ ├── TimeBoundPartialMatcherTest.java │ │ │ │ └── PartialMatcherTest.java │ │ │ ├── ResultSetTableTest.java │ │ │ ├── SingleTableVerifierMinBestMatchThresholdTest.java │ │ │ ├── CellFormatterTest.java │ │ │ └── SummaryResultTableTest.java │ │ │ ├── IgnoreColumnsTest.java │ │ │ ├── TestTable.java │ │ │ ├── IgnoreTablesTest.java │ │ │ ├── SummarisedResultsTest.java │ │ │ ├── OutputEncodingSecurityTest.java │ │ │ ├── HideMatchedTablesTest.java │ │ │ ├── CreateActualResultsTest.java │ │ │ ├── legal │ │ │ └── CopyrightTest.java │ │ │ ├── ExpectedResultsLoaderTest.java │ │ │ ├── CreateActualResultsOnFailureTest.java │ │ │ ├── ExampleTableVerifierTest.java │ │ │ ├── results │ │ │ └── parser │ │ │ │ └── ExpectedResultsParserTest.java │ │ │ ├── FileAndDirectoryStrategyTest.java │ │ │ ├── adapters │ │ │ └── TableAdaptersTest.java │ │ │ └── HtmlRowLimitTest.java │ └── main │ │ └── java │ │ └── com │ │ └── gs │ │ └── tablasco │ │ └── files │ │ ├── FilePerMethodStrategy.java │ │ ├── FixedDirectoryStrategy.java │ │ ├── DirectoryStrategy.java │ │ ├── FilePerClassStrategy.java │ │ └── FilenameStrategy.java └── pom.xml ├── tablasco-core ├── src │ ├── test │ │ ├── resources │ │ │ ├── ExpectedResultsLoaderTest.txt │ │ │ ├── html-test-actual.csv │ │ │ └── html-test-expected.csv │ │ └── java │ │ │ └── com │ │ │ └── gs │ │ │ └── tablasco │ │ │ └── core │ │ │ ├── HtmlConfigTest.java │ │ │ └── VerifierConfigTest.java │ └── main │ │ └── java │ │ └── com │ │ └── gs │ │ └── tablasco │ │ ├── verify │ │ ├── KeyedVerifiableTable.java │ │ ├── SingleTableVerifier.java │ │ ├── indexmap │ │ │ ├── PartialMatcher.java │ │ │ ├── ActualRowView.java │ │ │ ├── ExpectedRowView.java │ │ │ ├── ActualRowIterator.java │ │ │ ├── ExpectedRowIterator.java │ │ │ ├── RowIterator.java │ │ │ ├── RowView.java │ │ │ ├── BestMatchPartialMatcher.java │ │ │ ├── TimeBoundPartialMatcher.java │ │ │ ├── IndexMap.java │ │ │ ├── UnmatchedIndexMap.java │ │ │ └── KeyColumnPartialMatcher.java │ │ ├── FormattableTable.java │ │ ├── KeyedVerifiableTableAdapter.java │ │ ├── ToleranceCellComparator.java │ │ ├── VarianceCellComparator.java │ │ ├── CellComparator.java │ │ ├── DefaultVerifiableTableAdapter.java │ │ ├── ResultSetTable.java │ │ ├── HtmlOptions.java │ │ ├── Metadata.java │ │ ├── ColumnCardinality.java │ │ ├── MultiTableVerifier.java │ │ └── ListVerifiableTable.java │ │ ├── results │ │ ├── ExpectedResultsLoader.java │ │ ├── parser │ │ │ ├── ParserState.java │ │ │ ├── ExpectedResultsCache.java │ │ │ ├── BeginningOfLineState.java │ │ │ ├── SectionReaderState.java │ │ │ ├── HeaderParserState.java │ │ │ ├── MetadataReaderState.java │ │ │ ├── DataReaderState.java │ │ │ └── ExpectedTable.java │ │ ├── FileSystemExpectedResultsLoader.java │ │ └── ExpectedResults.java │ │ ├── NamedTable.java │ │ ├── adapters │ │ ├── RowFilterAdapter.java │ │ ├── ColumnFilterAdapter.java │ │ └── TableAdapters.java │ │ ├── VerifiableTable.java │ │ ├── core │ │ ├── Tablasco.java │ │ └── Tables.java │ │ └── rebase │ │ └── Rebaser.java └── pom.xml ├── NOTICE.txt ├── .github └── workflows │ ├── main.yml │ └── maven-publish.yml ├── CONTRIBUTOR_COVENANT.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea -------------------------------------------------------------------------------- /.gs-project.yml: -------------------------------------------------------------------------------- 1 | productGuid: "product::297906" -------------------------------------------------------------------------------- /img/html_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goldmansachs/tablasco/HEAD/img/html_report.png -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - project: 'fp/open-source/os-ci' 3 | file: 4 | - '.gitlab-ci.yml' 5 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/logging.properties: -------------------------------------------------------------------------------- 1 | # Logging 2 | handlers = java.util.logging.ConsoleHandler 3 | 4 | # Console Logging 5 | java.util.logging.ConsoleHandler.level = INFO -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_object.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:15" 2 | 3 | Section "object" "object: /" 4 | "/age","/name" 5 | 70,"Donald" 6 | 7 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_emptySimpleArray.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-28 19:35:10" 2 | 3 | Section "emptySimpleArray" "emptySimpleArray: /" 4 | "/" 5 | 6 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_array.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:16" 2 | 3 | Section "array" "array: /" 4 | "/age","/name" 5 | 70,"Donald" 6 | 68,"Hillary" 7 | 8 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_simpleArray.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-28 16:20:18" 2 | 3 | Section "simpleArray" "simpleArray: /" 4 | "/" 5 | "Donald" 6 | "Hillary" 7 | 8 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/CreateActualResultsOnFailureTest_testTrueFail.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-02-14 05:53:21" 2 | 3 | Section "testTrueFail" "peopleTable" 4 | "First","Last","Age" 5 | "Barry","White",21.3 6 | 7 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_singleSimpleArray.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-28 13:47:02" 2 | 3 | Section "singleSimpleArray" "singleSimpleArray: /parties" 4 | "/parties" 5 | "Rep" 6 | "Dem" 7 | 8 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/CreateActualResultsOnFailureTest_testFalseFail.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-02-14 05:53:21" 2 | 3 | Section "testFalseFail" "peopleTable" 4 | "First","Last","Age" 5 | "Barry","White",21.3 6 | 7 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/CreateActualResultsTest_testFalse.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-02-14 05:53:21" 2 | 3 | Section "testFalse" "peopleTable" 4 | "First","Last","Age" 5 | "Barry","White",21.3 6 | "Oscar","White",7.6 7 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/CreateActualResultsTest_testTrue.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-02-14 05:53:21" 2 | 3 | Section "testTrue" "peopleTable" 4 | "First","Last","Age" 5 | "Barry","White",21.3 6 | "Oscar","White",7.6 7 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/CustomFileAndDirectoryStrategyTest.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-02-14 05:53:21" 2 | 3 | Section "testFiles" "peopleTable" 4 | "First","Last","Age" 5 | "Barry","White",21.3 6 | "Oscar","White",7.6 7 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/ClassAnnotationTest.txt: -------------------------------------------------------------------------------- 1 | Section "toleranceAndRowOrderSuccess" "peopleTable" 2 | "First","Last","Age" 3 | "Oscar","White",7.7 4 | "Barry","White",21.2 5 | 6 | Metadata "Recorded At" "2014-02-14 05:53:21" 7 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/CreateActualResultsTest_testDefault.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-02-14 05:53:21" 2 | 3 | Section "testDefault" "peopleTable" 4 | "First","Last","Age" 5 | "Barry","White",21.3 6 | "Oscar","White",7.6 7 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_arrayOfSimpleArrays.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-10-21 10:22:49" 2 | 3 | Section "arrayOfSimpleArrays" "arrayOfSimpleArrays: /array" 4 | "/array" 5 | "[foo]" 6 | "[bar, baz]" 7 | 8 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/maven_input/MavenStyleDirectoryStrategyTest.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-08-31 16:09:54" 2 | 3 | Section "testMavenStyleDirectoryStrategy" "maven" 4 | "h1","h2" 5 | "r11","r12" 6 | "r21","r22" 7 | 8 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/CreateActualResultsOnFailureTest_testTrue.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-02-14 05:53:21" 2 | 3 | Section "testTrue" "peopleTable" 4 | "First","Last","Age" 5 | "Barry","White",21.3 6 | "Oscar","White",7.6 7 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/CreateActualResultsOnFailureTest_testFalse.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-02-14 05:53:21" 2 | 3 | Section "testFalse" "peopleTable" 4 | "First","Last","Age" 5 | "Barry","White",21.3 6 | "Oscar","White",7.6 7 | 8 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_deepSimpleArray.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-28 13:50:08" 2 | 3 | Section "deepSimpleArray" "deepSimpleArray: /parent/candidates" 4 | "/parent/candidates" 5 | "Donald" 6 | "Hillary" 7 | 8 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_arrayWithObjectsWithDifferentKeys.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:16" 2 | 3 | Section "arrayWithObjectsWithDifferentKeys" "arrayWithObjectsWithDifferentKeys: /" 4 | "/age","/name","/party" 5 | "","Donald","Rep" 6 | 68,"Hillary","" 7 | 8 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_arrayWithObjectsWithDifferentKeysAndTypes.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:15" 2 | 3 | Section "arrayWithObjectsWithDifferentKeysAndTypes" "arrayWithObjectsWithDifferentKeysAndTypes: /" 4 | "/name","/party","/address/state" 5 | "Donald","Rep","" 6 | "Bernie","","Vermont" 7 | 8 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_multiSimpleArray.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-28 13:50:08" 2 | 3 | Section "multiSimpleArray" "multiSimpleArray: /candidates" 4 | "/candidates" 5 | "Donald" 6 | "Hillary" 7 | 8 | Section "multiSimpleArray" "multiSimpleArray: /parties" 9 | "/parties" 10 | "Rep" 11 | "Dem" 12 | 13 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_emptySimpleArrayWithParent.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-28 19:35:10" 2 | 3 | Section "emptySimpleArrayWithParent" "emptySimpleArrayWithParent: /" 4 | "/year" 5 | 2016 6 | 7 | Section "emptySimpleArrayWithParent" "emptySimpleArrayWithParent: /candidates" 8 | "/year","/candidates" 9 | 10 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/ExampleTableVerifierTest_example.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2022-03-29 21:28:19" 2 | 3 | Section "example" "Most Popular Movies" 4 | "Title","Year","User Rank","IMDb Rating" 5 | "The Batman","2022",1,8.3 6 | "Deep Water","2022",2,5.4 7 | "X","2022",3,7.4 8 | "The Adam Project","2022",4,6.7 9 | "Turning Red","2022",5,7.1 10 | "Windfall","2022",6,5.7 11 | 12 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_deepObject.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:15" 2 | 3 | Section "deepObject" "deepObject: /" 4 | "/level","/candidate/age","/candidate/name","/child/level","/child/candidate/age","/child/candidate/name","/child/child/level","/child/child/candidate/age","/child/child/candidate/name" 5 | 1,68,"Hillary",2,70,"Donald",3,74,"Bernie" 6 | 7 | -------------------------------------------------------------------------------- /tablasco-core/src/test/resources/ExpectedResultsLoaderTest.txt: -------------------------------------------------------------------------------- 1 | Section "testOne" "peopleTable" 2 | "First","Last","Age" 3 | "Barry","White",21.3 4 | "Oscar","White",7.6 5 | 6 | Section "testTwo" "peopleTable" 7 | "First","Last","Age" 8 | "Barry","White",21.3 9 | "Oscar","White",7.6 10 | 11 | Section "testThree" "peopleTable" 12 | "First","Last","Age" 13 | "Barry","White",21.3 14 | "Oscar","White",7.6 15 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/ExpectedResultsLoaderTest.txt: -------------------------------------------------------------------------------- 1 | Section "testOne" "peopleTable" 2 | "First","Last","Age" 3 | "Barry","White",21.3 4 | "Oscar","White",7.6 5 | 6 | Section "testTwo" "peopleTable" 7 | "First","Last","Age" 8 | "Barry","White",21.3 9 | "Oscar","White",7.6 10 | 11 | Section "testThree" "peopleTable" 12 | "First","Last","Age" 13 | "Barry","White",21.3 14 | "Oscar","White",7.6 15 | -------------------------------------------------------------------------------- /tablasco-core/src/test/resources/html-test-actual.csv: -------------------------------------------------------------------------------- 1 | "id","str2","str1","num1","num3" 2 | "44","caseX","test",-10,-20,-30 3 | "5","data","sample",42,84,126 4 | "8","delta","gamma",-1,-2,-3 5 | "7","beta","alpha",1,2,3 6 | "6","text","example",7,14,21 7 | "9","two","one",119,22,33 8 | "10","blue bell","red",99,889,77 9 | "11","yellow","green",55,66,779 10 | "12","white","black",7,8,9 11 | "13","dog","cat",3,6,9 12 | "14","moon","sun",8,16,24 -------------------------------------------------------------------------------- /tablasco-core/src/test/resources/html-test-expected.csv: -------------------------------------------------------------------------------- 1 | "id","str1","str2","num1","num2" 2 | "1","hello","world",10,20,30 3 | "2","foo","bar",5,15,25 4 | "3","lorem","ipsum",0,100,200 5 | "4","test","case",-10,-20,-30 6 | "5","sample","data",42,84,126 7 | "6","example","text",7,14,21 8 | "7","alpha","beta",1,2,3 9 | "8","gamma","delta",-1,-2,-3 10 | "9","one","two",11,22,33 11 | "10","red","blue",99,88,77 12 | "11","green","yellow",55,66,77 -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_objectWithArrayAndObject.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:16" 2 | 3 | Section "objectWithArrayAndObject" "objectWithArrayAndObject: /" 4 | "/year","/loser/age","/loser/name" 5 | 2016,74,"Bernie" 6 | 7 | Section "objectWithArrayAndObject" "objectWithArrayAndObject: /candidates" 8 | "/year","/candidates/age","/candidates/name" 9 | 2016,70,"Donald" 10 | 2016,68,"Hillary" 11 | 12 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_objectWithArray.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-22 21:27:31" 2 | 3 | Section "objectWithArray" "objectWithArray: /" 4 | "/year" 5 | 2016 6 | 7 | Section "objectWithArray" "objectWithArray: /candidates" 8 | "/year","/candidates/age","/candidates/name" 9 | 2016,70,"Donald" 10 | 2016,68,"Hillary" 11 | 12 | Section "objectWithArray" "objectWithArray: /parties" 13 | "/year","/parties" 14 | 2016,"Rep" 15 | 2016,"Dem" 16 | 17 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Tablasco 2 | Copyright 2017 Goldman Sachs 3 | Licensed under Apache 2.0 license 4 | 5 | This product includes software developed at 6 | The Apache Software Foundation (http://www.apache.org/). 7 | 8 | This product depends on (links to) the following software: 9 | SLF4J API Module under MIT License 10 | Eclipse Collections Main Library under Eclipse Distribution License - v 1.0 11 | JUNit under BSD License 12 | ContiPerf Licensed under Apache 2.0 license 13 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_arrayOfObjectsWithArrays.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:16" 2 | 3 | Section "arrayOfObjectsWithArrays" "arrayOfObjectsWithArrays: /" 4 | "/vote" 5 | "president" 6 | "democrat" 7 | 8 | Section "arrayOfObjectsWithArrays" "arrayOfObjectsWithArrays: /candidates" 9 | "/vote","/candidates/age","/candidates/name" 10 | "president",70,"Donald" 11 | "president",68,"Hillary" 12 | "democrat",74,"Bernie" 13 | "democrat",68,"Hillary" 14 | 15 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_objectWithMultiArraysA.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:16" 2 | 3 | Section "objectWithMultiArraysA" "objectWithMultiArraysA: /" 4 | "/year" 5 | 2016 6 | 7 | Section "objectWithMultiArraysA" "objectWithMultiArraysA: /a1" 8 | "/year","/a1/age","/a1/name" 9 | 2016,70,"Donald" 10 | 2016,68,"Hillary" 11 | 12 | Section "objectWithMultiArraysA" "objectWithMultiArraysA: /a2" 13 | "/year","/a2/age","/a2/name" 14 | 2016,68,"Hillary" 15 | 2016,74,"Bernie" 16 | 17 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_objectWithMultiArraysB.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:16" 2 | 3 | Section "objectWithMultiArraysB" "objectWithMultiArraysB: /" 4 | "/year" 5 | 2016 6 | 7 | Section "objectWithMultiArraysB" "objectWithMultiArraysB: /a1" 8 | "/year","/a1/age","/a1/name" 9 | 2016,70,"Donald" 10 | 2016,68,"Hillary" 11 | 12 | Section "objectWithMultiArraysB" "objectWithMultiArraysB: /a2" 13 | "/year","/a2/age","/a2/name" 14 | 2016,68,"Hillary" 15 | 2016,74,"Bernie" 16 | 2016,70,"Donald" 17 | 18 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_objectWithMultiArraysC.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-16 11:06:16" 2 | 3 | Section "objectWithMultiArraysC" "objectWithMultiArraysC: /" 4 | "/year" 5 | 2016 6 | 7 | Section "objectWithMultiArraysC" "objectWithMultiArraysC: /a1" 8 | "/year","/a1/age","/a1/name" 9 | 2016,68,"Hillary" 10 | 2016,74,"Bernie" 11 | 2016,70,"Donald" 12 | 13 | Section "objectWithMultiArraysC" "objectWithMultiArraysC: /a2" 14 | "/year","/a2/age","/a2/name" 15 | 2016,70,"Donald" 16 | 2016,68,"Hillary" 17 | 18 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/ExpectedResultsParserTest.txt: -------------------------------------------------------------------------------- 1 | Section Summary 2 | "Transaction ID","Trade Reference","Entity","Entity ID","Account","Value (USD)" 3 | 13,T3,GSCO,9263331,"10000002.01",123100.00 4 | 15,T4,GSCO,9263331,"20000005.01",999.25 5 | 16,T5,GSCO,9263331,"10000005.01",102 6 | 17,T6,GSCO,9263331,"10000005.01",386.32 7 | 18,T7,GSCO,9263331,"10000005.01",386.32 8 | 9 | Section DrillDown 10 | "Transaction ID","Trade Reference","Entity","Entity ID","Account","Value (USD)" 11 | 13,T3,GSCO,9263331,"10000002.01",123100.00 12 | 13 | Metadata "Recorded At" "2013-06-26 12:00:00", "App Server URL" "http://test" 14 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: [push] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Set up JDK 21 14 | uses: actions/setup-java@v4 15 | with: 16 | java-version: '21' 17 | distribution: 'adopt' 18 | cache: maven 19 | - name: Build with Maven 20 | run: mvn -B package --file pom.xml 21 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/KeyedVerifiableTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | 21 | public interface KeyedVerifiableTable extends VerifiableTable { 22 | boolean isKeyColumn(int columnIndex); 23 | } 24 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/SingleTableVerifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | 21 | public interface SingleTableVerifier { 22 | ResultTable verify(VerifiableTable actualData, VerifiableTable expectedData); 23 | } 24 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/ExpectedResultsLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | 23 | public interface ExpectedResultsLoader { 24 | InputStream load(File expectedFile) throws IOException; 25 | } 26 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/PartialMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import java.util.List; 20 | 21 | public interface PartialMatcher { 22 | void match( 23 | List allMissingRows, 24 | List allSurplusRows, 25 | List matchedColumns); 26 | } 27 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/FormattableTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import java.util.List; 20 | import org.w3c.dom.Element; 21 | 22 | public interface FormattableTable { 23 | int getPassedCellCount(); 24 | 25 | int getTotalCellCount(); 26 | 27 | List getHeaders(); 28 | 29 | int getMatchedColumnsAhead(int col); 30 | 31 | void appendTo(String testName, String tableName, Element table, HtmlOptions htmlOptions); 32 | 33 | boolean isSuccess(); 34 | } 35 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/parser/ParserState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results.parser; 18 | 19 | import java.io.IOException; 20 | import java.io.StreamTokenizer; 21 | import java.text.ParseException; 22 | 23 | public abstract class ParserState { 24 | private final ExpectedResultsParser parser; 25 | 26 | ParserState(ExpectedResultsParser parser) { 27 | this.parser = parser; 28 | } 29 | 30 | public ExpectedResultsParser getParser() { 31 | return parser; 32 | } 33 | 34 | public abstract ParserState parse(StreamTokenizer st) throws IOException, ParseException; 35 | } 36 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/ActualRowView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.ColumnComparators; 21 | import java.util.List; 22 | 23 | public class ActualRowView extends RowView { 24 | ActualRowView( 25 | VerifiableTable actualData, List keyColumns, ColumnComparators columnComparators, int rowIndex) { 26 | super(actualData, keyColumns, columnComparators, rowIndex); 27 | } 28 | 29 | @Override 30 | protected int getColumnIndex(IndexMap column) { 31 | return column.getActualIndex(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/FileSystemExpectedResultsLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results; 18 | 19 | import java.io.*; 20 | import java.nio.file.Files; 21 | 22 | public class FileSystemExpectedResultsLoader implements ExpectedResultsLoader { 23 | @Override 24 | public InputStream load(final File expectedFile) throws IOException { 25 | if (!expectedFile.canRead()) { 26 | throw new IllegalStateException("Could not find expected results '" + expectedFile 27 | + "' - if this is a new test, do you need to run this test in rebase mode first? (-Drebase=true)"); 28 | } 29 | return Files.newInputStream(expectedFile.toPath()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/ExpectedRowView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.ColumnComparators; 21 | import java.util.List; 22 | 23 | public class ExpectedRowView extends RowView { 24 | ExpectedRowView( 25 | VerifiableTable expectedData, 26 | List keyColumns, 27 | ColumnComparators columnComparators, 28 | int rowIndex) { 29 | super(expectedData, keyColumns, columnComparators, rowIndex); 30 | } 31 | 32 | @Override 33 | protected int getColumnIndex(IndexMap column) { 34 | return column.getExpectedIndex(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/NoExpectedResultsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import java.util.Collections; 20 | import java.util.Map; 21 | import org.junit.jupiter.api.Test; 22 | import org.junit.jupiter.api.extension.RegisterExtension; 23 | 24 | public class NoExpectedResultsTest { 25 | 26 | @RegisterExtension 27 | private final TableVerifier verifier = new TableVerifier().withMavenDirectoryStrategy(); 28 | 29 | @Test 30 | void asynchronousResultsLoadingOnlyFailsIfResultsAreRequired() { 31 | Map tables = 32 | Collections.singletonMap(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL); 33 | this.verifier.verify(tables, tables); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path 3 | 4 | name: Maven Central Release 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Set up JDK 21 22 | uses: actions/setup-java@v4 23 | with: 24 | java-version: '21' 25 | distribution: 'temurin' 26 | server-id: central # Value of the distributionManagement/repository/id field of the pom.xml 27 | server-username: MAVEN_USERNAME 28 | server-password: MAVEN_PASSWORD 29 | 30 | - name: Set release version 31 | run: mvn -B versions:set -DnewVersion=$GITHUB_REF_NAME -DgenerateBackupPoms=false 32 | 33 | - name: Publish to Maven Central 34 | run: mvn -P release deploy 35 | env: 36 | MAVEN_GPG_KEY: ${{ secrets.MAVEN_GPG_KEY }} 37 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} 38 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 39 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 40 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/examples/BswCpsAnalysisRegressionTest_regrBalanceBusinessType_ACTUAL.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-07-13 22:00:47" 2 | 3 | Section "regrBalanceBusinessType" "results" 4 | "Business Type","Unfactored Quantity (SD)","Dirty Market Value","Segregated Unfactored Quantity","Cash","Contractual Value","Collateral","Option Market Value","Factor","Trade Date Unfactored Quantity","Gaap Trade Date Market Value","Held Away Unfactored Quantity" 5 | "",0,0,0,647516,0,0,0,3,0,0,0 6 | "VEGE",0,0,0,-411883,0,0,0,15,0,0,0 7 | "HEWE",0,0,0,-715075450,0,0,0,57,0,0,0 8 | "BUNU",0,0,0,16503879098,0,0,0,5891,0,0,0 9 | "QOXA",0,0,0,-181799,0,0,0,21,0,0,0 10 | "XAHA",0,0,0,3006802,0,0,0,71,0,0,0 11 | "XUWU",0,0,0,406335037,0,0,0,86,0,0,0 12 | "VUCU",0,0,0,-506556611988,0,0,0,584676,0,0,0 13 | "FOLE",0,0,0,1245853557551,0,0,0,782,0,0,0 14 | "MANA",0,0,0,-318843209,0,0,0,6,0,0,0 15 | "XAGU",0,0,0,69379672,0,0,0,3,0,0,0 16 | "YUPE",0,0,0,-861627281677,0,0,0,341,0,0,0 17 | "LAJU",0,0,0,32168372848,0,0,0,11137,0,0,0 18 | "MUTO",0,0,0,-316086808,0,0,0,1941,0,0,0 19 | "JaK",0,0,0,-575102432,0,0,0,133,0,0,0 20 | "QUKI",0,0,0,-1644782703,0,0,0,78,0,0,0 21 | "ZIKU",0,0,0,180159141,0,0,0,4,0,0,0 22 | "YULI",0,0,0,-263484760,0,0,0,3,0,0,0 23 | "YOMI",0,0,0,131925902,0,0,0,13,0,0,0 24 | "DOCU",0,0,0,-1848685,0,0,0,3,0,0,0 25 | "CEQA",0,0,0,17775573,0,0,0,117,0,0,0 26 | "Cuwicufozace",0,0,0,-76684672256,0,0,0,605382,0,0,0 27 | 28 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/NamedTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | /** 20 | * A table with a name 21 | */ 22 | public class NamedTable { 23 | private final String name; 24 | private final VerifiableTable table; 25 | 26 | /** 27 | * @param name the name 28 | * @param table the table 29 | */ 30 | public NamedTable(String name, VerifiableTable table) { 31 | this.name = name; 32 | this.table = table; 33 | } 34 | 35 | /** 36 | * @return the table name 37 | */ 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | /** 43 | * @return the verifiable table 44 | */ 45 | public VerifiableTable getTable() { 46 | return table; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/ActualRowIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.ColumnComparators; 21 | import java.util.List; 22 | 23 | public class ActualRowIterator extends RowIterator { 24 | ActualRowIterator( 25 | VerifiableTable table, 26 | List columns, 27 | ColumnComparators columnComparators, 28 | int initialIndex, 29 | int lastUnMatchedOffset) { 30 | super(table, columns, columnComparators, initialIndex, lastUnMatchedOffset); 31 | } 32 | 33 | @Override 34 | protected RowView createRowView(int rowIndex) { 35 | return new ActualRowView(this.getTable(), this.getColumns(), this.getColumnComparators(), rowIndex); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/ExpectedRowIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.ColumnComparators; 21 | import java.util.List; 22 | 23 | public class ExpectedRowIterator extends RowIterator { 24 | ExpectedRowIterator( 25 | VerifiableTable table, 26 | List columns, 27 | ColumnComparators columnComparators, 28 | int initialIndex, 29 | int lastUnMatchedOffset) { 30 | super(table, columns, columnComparators, initialIndex, lastUnMatchedOffset); 31 | } 32 | 33 | @Override 34 | protected RowView createRowView(int rowIndex) { 35 | return new ExpectedRowView(this.getTable(), this.getColumns(), this.getColumnComparators(), rowIndex); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/KeyedVerifiableTableAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | 21 | public class KeyedVerifiableTableAdapter extends DefaultVerifiableTableAdapter implements KeyedVerifiableTable { 22 | private final boolean[] keyColumnIndices; 23 | 24 | public KeyedVerifiableTableAdapter(VerifiableTable delegate, int... keyColumnIndices) { 25 | super(delegate); 26 | this.keyColumnIndices = new boolean[delegate.getColumnCount()]; 27 | for (int keyColumnIndex : keyColumnIndices) { 28 | this.keyColumnIndices[keyColumnIndex] = true; 29 | } 30 | } 31 | 32 | @Override 33 | public boolean isKeyColumn(int columnIndex) { 34 | return columnIndex >= 0 && columnIndex < this.keyColumnIndices.length && this.keyColumnIndices[columnIndex]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/MavenStyleDirectoryStrategyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import com.gs.tablasco.files.MavenStyleDirectoryStrategy; 20 | import org.junit.jupiter.api.Test; 21 | import org.junit.jupiter.api.extension.RegisterExtension; 22 | 23 | public class MavenStyleDirectoryStrategyTest { 24 | 25 | @RegisterExtension 26 | private final TableVerifier tableVerifier = new TableVerifier() 27 | .withFilePerClass() 28 | .withDirectoryStrategy(new MavenStyleDirectoryStrategy() 29 | .withAnchorFile("pom.xml") 30 | .withExpectedSubDir("maven_input") 31 | .withOutputSubDir("maven_output")); 32 | 33 | @Test 34 | void testMavenStyleDirectoryStrategy() { 35 | this.tableVerifier.verify( 36 | "maven", new TestTable("h1", "h2").withRow("r11", "r12").withRow("r21", "r22")); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/parser/ExpectedResultsCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results.parser; 18 | 19 | import com.gs.tablasco.results.ExpectedResults; 20 | import com.gs.tablasco.results.ExpectedResultsLoader; 21 | import java.io.File; 22 | import java.util.Objects; 23 | 24 | public class ExpectedResultsCache { 25 | private static File lastExpectedResultsFile; 26 | private static ExpectedResults lastExpectedResults; 27 | 28 | public static synchronized ExpectedResults getExpectedResults( 29 | ExpectedResultsLoader expectedResultsLoader, File expectedResultsFile) { 30 | if (!Objects.equals(lastExpectedResultsFile, expectedResultsFile)) { 31 | lastExpectedResultsFile = expectedResultsFile; 32 | lastExpectedResults = new ExpectedResultsParser(expectedResultsLoader, expectedResultsFile).parse(); 33 | } 34 | return lastExpectedResults; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/files/FixedDirectoryStrategyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.files; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | import java.io.File; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class FixedDirectoryStrategyTest { 25 | @Test 26 | void testDefault() { 27 | FixedDirectoryStrategy directoryStrategy = new FixedDirectoryStrategy(); 28 | File outputDirectory = directoryStrategy.getOutputDirectory(null); 29 | File expectedDirectory = directoryStrategy.getExpectedDirectory(null); 30 | File actualDirectory = directoryStrategy.getActualDirectory(null); 31 | assertTrue(outputDirectory.exists()); 32 | assertTrue(expectedDirectory.exists()); 33 | assertTrue(actualDirectory.exists()); 34 | assertEquals(actualDirectory, outputDirectory); 35 | assertNotEquals(expectedDirectory, outputDirectory); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tablasco-junit/src/main/java/com/gs/tablasco/files/FilePerMethodStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.files; 18 | 19 | /** 20 | * A {@link FilenameStrategy} that returns a different filename for each test in a class. The filename is constructed 21 | * using the simple class name of the test plus the method name and file extension. 22 | */ 23 | public class FilePerMethodStrategy implements FilenameStrategy { 24 | @Override 25 | public String getExpectedFilename(Class testClass, String methodName) { 26 | return testClass.getSimpleName() + '_' + methodName + ".txt"; 27 | } 28 | 29 | @Override 30 | public String getOutputFilename(Class testClass, String methodName) { 31 | return testClass.getSimpleName() + '_' + methodName + ".html"; 32 | } 33 | 34 | @Override 35 | public String getActualFilename(Class testClass, String methodName) { 36 | return testClass.getSimpleName() + '_' + methodName + ".txt"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tablasco-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.goldmansachs.tablasco 9 | tablasco 10 | 1.0-SNAPSHOT 11 | 12 | 13 | tablasco-core 14 | 1.0-SNAPSHOT 15 | Tablasco Core 16 | 17 | 18 | 19 | org.slf4j 20 | slf4j-api 21 | 22 | 23 | 24 | org.junit.jupiter 25 | junit-jupiter-api 26 | test 27 | 28 | 29 | org.junit.jupiter 30 | junit-jupiter-params 31 | test 32 | 33 | 34 | org.apache.commons 35 | commons-csv 36 | test 37 | 38 | 39 | de.skuzzle.test 40 | snapshot-tests-junit5 41 | test 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/ToleranceCellComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import java.io.Serializable; 20 | 21 | public class ToleranceCellComparator extends CellComparator implements Serializable { 22 | public ToleranceCellComparator(CellFormatter formatter) { 23 | super(formatter); 24 | } 25 | 26 | @Override 27 | public boolean compare(Object actual, Object expected) { 28 | if (isFloatingPoint(expected) && isFloatingPoint(actual)) { 29 | double actualVal = ((Number) actual).doubleValue(); 30 | double expectVal = ((Number) expected).doubleValue(); 31 | return Double.compare(expectVal, actualVal) == 0 32 | || Math.abs(expectVal - actualVal) <= getFormatter().getTolerance(); 33 | } 34 | return false; 35 | } 36 | 37 | static double getDifference(Object actual, Object expected) { 38 | return ((Number) expected).doubleValue() - ((Number) actual).doubleValue(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tablasco-junit/src/main/java/com/gs/tablasco/files/FixedDirectoryStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.files; 18 | 19 | import java.io.File; 20 | 21 | public class FixedDirectoryStrategy implements DirectoryStrategy { 22 | private final File expectedDir; 23 | private final File outputDir; 24 | 25 | public FixedDirectoryStrategy() { 26 | this(new File(System.getProperty("user.dir")), new File(System.getProperty("java.io.tmpdir"))); 27 | } 28 | 29 | public FixedDirectoryStrategy(File expectedDir, File outputDir) { 30 | this.expectedDir = expectedDir; 31 | this.outputDir = outputDir; 32 | } 33 | 34 | @Override 35 | public File getExpectedDirectory(Class testClass) { 36 | return this.expectedDir; 37 | } 38 | 39 | @Override 40 | public File getOutputDirectory(Class testClass) { 41 | return this.outputDir; 42 | } 43 | 44 | @Override 45 | public File getActualDirectory(Class testClass) { 46 | return this.outputDir; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/examples/BswCpsAnalysisRegressionTest_regrBalanceBusinessType_EXPECTED.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2014-07-13 22:00:47" 2 | 3 | Section "regrBalanceBusinessType" "results" 4 | "Business Type","Unfactored Quantity (SD)","Dirty Market Value","Segregated Unfactored Quantity","Cash","Contractual Value","Contractual-Market Value Diff","COLLATERAL","Imputed Margin Debit","Imputed Free Credit","Fails Debit Balance","Fails Debit Balance/liab.","Fails Credit Balance","Option Market Value","Unpaired","Contractual Value (Native)","Fails Age","FACTOR","Trade Date Quantity","Trade Date Market Value","Held Away Quantity" 5 | "",0,0,0,215839,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 6 | "VEGE",0,0,0,-137295,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0 7 | "HEWE",0,0,0,-238358483,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0 8 | "BUNU",0,0,0,5501293033,0,0,0,0,0,0,0,0,0,0,0,0,1219,0,0,0 9 | "QOXA",0,0,0,-60600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 10 | "XAHA",0,0,0,1002267,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0 11 | "XUWU",0,0,0,135445013,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0 12 | "VUCU",0,0,0,153522000000,0,0,0,0,0,0,0,0,0,0,0,0,52876,0,0,0 13 | "FOLE",0,0,0,39374735,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0 14 | "MANA",0,0,0,-106281069,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 15 | "YUPE",0,0,0,-287208000000,0,0,0,0,0,0,0,0,0,0,0,0,69,0,0,0 16 | "LAJU",0,0,0,376853052,0,0,0,0,0,0,0,0,0,0,0,0,1268,0,0,0 17 | "MUTO",0,0,0,-105362270,0,0,0,0,0,0,0,0,0,0,0,0,174,0,0,0 18 | "QUKI",0,0,0,-548260902,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0 19 | "YULI",0,0,0,-87828253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 20 | "YOMI",0,0,0,43975300,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0 21 | "CEQA",0,0,0,5925191,0,0,0,0,0,0,0,0,0,0,0,0,23,0,0,0 22 | "Cuwicufozace",0,0,0,-128668000000,0,0,0,0,0,0,0,0,0,0,0,0,55682,0,0,0 23 | 24 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/adapters/RowFilterAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.adapters; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.DefaultVerifiableTableAdapter; 21 | import java.util.function.IntPredicate; 22 | 23 | class RowFilterAdapter extends DefaultVerifiableTableAdapter { 24 | private final int[] indexMap; 25 | private int rowCount = 0; 26 | 27 | RowFilterAdapter(VerifiableTable delegate, IntPredicate rowFilter) { 28 | super(delegate); 29 | this.indexMap = new int[delegate.getRowCount()]; 30 | for (int i = 0; i < delegate.getRowCount(); i++) { 31 | if (rowFilter.test(i)) { 32 | indexMap[this.rowCount++] = i; 33 | } 34 | } 35 | } 36 | 37 | @Override 38 | public int getRowCount() { 39 | return this.rowCount; 40 | } 41 | 42 | @Override 43 | public Object getValueAt(int rowIndex, int columnIndex) { 44 | return super.getValueAt(this.indexMap[rowIndex], columnIndex); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tablasco-junit/src/main/java/com/gs/tablasco/files/DirectoryStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.files; 18 | 19 | import java.io.File; 20 | 21 | /** 22 | * A strategy for determining the expected results and verification output directories for a given test class. 23 | */ 24 | public interface DirectoryStrategy { 25 | /** 26 | * Returns the expected results directory for a given test class. 27 | * @param testClass the test class 28 | * @return the expected results directory 29 | */ 30 | File getExpectedDirectory(Class testClass); 31 | 32 | /** 33 | * Returns the verification output directory for a given test class. 34 | * @param testClass the test class 35 | * @return the verification output directory 36 | */ 37 | File getOutputDirectory(Class testClass); 38 | 39 | /** 40 | * Returns the actual results directory for a given test class. 41 | * @param testClass the test class 42 | * @return the verification output directory 43 | */ 44 | File getActualDirectory(Class testClass); 45 | } 46 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/VarianceCellComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import java.io.Serializable; 20 | 21 | public class VarianceCellComparator extends CellComparator implements Serializable { 22 | private final double varianceThreshold; 23 | 24 | public VarianceCellComparator(CellFormatter formatter, double varianceThreshold) { 25 | super(formatter); 26 | this.varianceThreshold = varianceThreshold; 27 | } 28 | 29 | @Override 30 | public boolean compare(Object actual, Object expected) { 31 | if (isFloatingPoint(expected) && isFloatingPoint(actual)) { 32 | double variance = getVariance(actual, expected); 33 | return Math.abs(variance) <= this.varianceThreshold; 34 | } 35 | return false; 36 | } 37 | 38 | static double getVariance(Object actual, Object expected) { 39 | double number1 = ((Number) actual).doubleValue(); 40 | double number2 = ((Number) expected).doubleValue(); 41 | return (number1 - number2) * 100.0d / number2; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tablasco-junit/src/main/java/com/gs/tablasco/files/FilePerClassStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.files; 18 | 19 | /** 20 | * A {@link FilenameStrategy} that returns the same filename for each test in a class. The filename is constructed 21 | * using the simple class name of the test plus file extension. 22 | * 23 | * @deprecated Rebase does not work correctly with this strategy which will be removed eventually. Please use the 24 | * default FilePerMethod instead. 25 | */ 26 | @SuppressWarnings("DeprecatedIsStillUsed") 27 | @Deprecated 28 | public class FilePerClassStrategy implements FilenameStrategy { 29 | @Override 30 | public String getExpectedFilename(Class testClass, String methodName) { 31 | return testClass.getSimpleName() + ".txt"; 32 | } 33 | 34 | @Override 35 | public String getOutputFilename(Class testClass, String methodName) { 36 | return testClass.getSimpleName() + ".html"; 37 | } 38 | 39 | @Override 40 | public String getActualFilename(Class testClass, String methodName) { 41 | return testClass.getSimpleName() + ".txt"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/CellComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import java.io.Serializable; 20 | 21 | public abstract class CellComparator implements Serializable { 22 | private final CellFormatter formatter; 23 | 24 | public CellComparator(CellFormatter formatter) { 25 | this.formatter = formatter; 26 | } 27 | 28 | public CellFormatter getFormatter() { 29 | return this.formatter; 30 | } 31 | 32 | protected abstract boolean compare(Object actual, Object expected); 33 | 34 | public static boolean isFloatingPoint(Object object) { 35 | return object instanceof Double || object instanceof Float; 36 | } 37 | 38 | public boolean equals(Object actual, Object expected) { 39 | String formattedActual = this.getFormatter().format(actual); 40 | String formattedExpected = this.getFormatter().format(expected); 41 | return formattedActual.equals(formattedExpected) || compare(actual, expected); 42 | } 43 | 44 | public int computeHashCode(Object object) { 45 | return this.formatter.format(object).hashCode(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/verify/MetadataTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | 22 | import org.junit.jupiter.api.Test; 23 | 24 | class MetadataTest { 25 | @Test 26 | void testMetadataCreatedForRecordingResults() { 27 | Metadata metadata = Metadata.newWithRecordedAt(); 28 | metadata.add("App Server URL", "http://test"); 29 | metadata.add("testKey", "testValue"); 30 | String asString = metadata.toString("#"); 31 | assertTrue(asString.contains("#Recorded At#")); 32 | assertTrue(asString.contains("#App Server URL# #http://test#, #testKey# #testValue#")); 33 | } 34 | 35 | @Test 36 | void testMetadataCreatedForParsingResults() { 37 | Metadata metadata = Metadata.newEmpty(); 38 | metadata.add("testKey", "testValue"); 39 | metadata.add("App Server URL", "http://test"); 40 | 41 | String asString = metadata.toString(); 42 | assertEquals("testKey testValue, App Server URL http://test", asString); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tablasco-junit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.goldmansachs.tablasco 9 | tablasco 10 | 1.0-SNAPSHOT 11 | 12 | tablasco-junit 13 | 1.0-SNAPSHOT 14 | 15 | Tablasco JUnit 5 Extension 16 | Tablasco is a JUnit extension that adds table verification to your unit tests 17 | 18 | 19 | 20 | com.goldmansachs.tablasco 21 | tablasco-core 22 | ${project.version} 23 | 24 | 25 | org.junit.jupiter 26 | junit-jupiter-api 27 | provided 28 | 29 | 30 | 31 | org.slf4j 32 | slf4j-simple 33 | test 34 | 35 | 36 | com.h2database 37 | h2 38 | test 39 | 40 | 41 | de.skuzzle.test 42 | snapshot-tests-junit5 43 | test 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/VerifiableTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | /** 20 | * The table model that {@link com.gs.tablasco.verify.SingleTableVerifier} is able to verify. Data structures must be adapted to instances of 21 | * {@link VerifiableTable} in order to be verified. 22 | */ 23 | public interface VerifiableTable { 24 | /** 25 | * Returns the number of data rows in this table. This does not include column headers. 26 | * @return the row count 27 | */ 28 | int getRowCount(); 29 | 30 | /** 31 | * Returns the number of columns in this table. 32 | * @return the column count 33 | */ 34 | int getColumnCount(); 35 | 36 | /** 37 | * Returns the column name at a given index. 38 | * @param columnIndex zero-based column index 39 | * @return the column name at given index 40 | */ 41 | String getColumnName(int columnIndex); 42 | 43 | /** 44 | * Returns the value at a given row and column index 45 | * @param rowIndex zero-based row index 46 | * @param columnIndex zero-based column index 47 | * @return the value at given row and column index 48 | */ 49 | Object getValueAt(int rowIndex, int columnIndex); 50 | } 51 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/resources/JsonTableFactoryTest_deepArraysWithParentsWithDifferentKeys.txt: -------------------------------------------------------------------------------- 1 | Metadata "Recorded At" "2016-09-22 21:31:44" 2 | 3 | Section "deepArraysWithParentsWithDifferentKeys" "deepArraysWithParentsWithDifferentKeys: /" 4 | "/level","/row","/tr" 5 | 1,"one","" 6 | 1,"","two" 7 | 8 | Section "deepArraysWithParentsWithDifferentKeys" "deepArraysWithParentsWithDifferentKeys: /kids" 9 | "/level","/row","/tr","/kids/level","/kids/row","/kids/tr" 10 | 1,"one","",2,"one one","" 11 | 1,"one","",2,"","one two" 12 | 1,"","two",2,"two one","" 13 | 1,"","two",2,"","two two" 14 | 15 | Section "deepArraysWithParentsWithDifferentKeys" "deepArraysWithParentsWithDifferentKeys: /kids/kids" 16 | "/kids/level","/kids/row","/kids/tr","/kids/kids/level","/kids/kids/row","/kids/kids/tr" 17 | 2,"one one","",3,"one one one","" 18 | 2,"one one","",3,"","one one two" 19 | 2,"","one two",3,"one two one","" 20 | 2,"","one two",3,"","one two two" 21 | 2,"two one","",3,"two one one","" 22 | 2,"two one","",3,"","two one two" 23 | 2,"","two two",3,"two two one","" 24 | 2,"","two two",3,"","two two two" 25 | 26 | Section "deepArraysWithParentsWithDifferentKeys" "deepArraysWithParentsWithDifferentKeys: /kids/kids/arr" 27 | "/kids/kids/level","/kids/kids/row","/kids/kids/tr","/kids/kids/arr" 28 | 3,"one one one","",1 29 | 3,"one one one","",1 30 | 3,"one one one","",1 31 | 3,"","one one two",1 32 | 3,"","one one two",1 33 | 3,"","one one two",2 34 | 3,"one two one","",1 35 | 3,"one two one","",2 36 | 3,"one two one","",1 37 | 3,"","one two two",1 38 | 3,"","one two two",2 39 | 3,"","one two two",2 40 | 3,"two one one","",2 41 | 3,"two one one","",1 42 | 3,"two one one","",1 43 | 3,"","two one two",2 44 | 3,"","two one two",1 45 | 3,"","two one two",2 46 | 3,"two two one","",2 47 | 3,"two two one","",2 48 | 3,"two two one","",1 49 | 3,"","two two two",2 50 | 3,"","two two two",2 51 | 3,"","two two two",2 52 | 53 | -------------------------------------------------------------------------------- /tablasco-junit/src/main/java/com/gs/tablasco/files/FilenameStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.files; 18 | 19 | /** 20 | * A strategy for determining the expected results and verification output filenames for a given test class. 21 | */ 22 | public interface FilenameStrategy { 23 | /** 24 | * Returns the expected results filename for a given test class. 25 | * @param testClass the test class 26 | * @param methodName the test method name 27 | * @return the expected results filename 28 | */ 29 | String getExpectedFilename(Class testClass, String methodName); 30 | 31 | /** 32 | * Returns the verification output filename for a given test class. 33 | * @param testClass the test class 34 | * @param methodName the test method name 35 | * @return the verification output filename 36 | */ 37 | String getOutputFilename(Class testClass, String methodName); 38 | 39 | /** 40 | * Returns the actual results filename for a given test class. 41 | * @param testClass the test class 42 | * @param methodName the test method name 43 | * @return the verification output filename 44 | */ 45 | String getActualFilename(Class testClass, String methodName); 46 | } 47 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/IgnoreColumnsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import de.skuzzle.test.snapshots.Snapshot; 20 | import de.skuzzle.test.snapshots.junit5.EnableSnapshotTests; 21 | import java.io.IOException; 22 | import org.junit.jupiter.api.Test; 23 | import org.junit.jupiter.api.extension.RegisterExtension; 24 | 25 | @EnableSnapshotTests 26 | public class IgnoreColumnsTest { 27 | 28 | @RegisterExtension 29 | private final TableVerifier tableVerifier = 30 | new TableVerifier().withFilePerMethod().withMavenDirectoryStrategy(); 31 | 32 | @Test 33 | void ignoreColumns(Snapshot snapshot) throws IOException { 34 | VerifiableTable table1 = 35 | TableTestUtils.createTable(4, "Col 1", "Col 2", "Col 3", "Col 4", "A1", "A2", "A3", "A4"); 36 | VerifiableTable table2 = 37 | TableTestUtils.createTable(4, "Col 1", "Col 2", "Col 3", "Col 4", "A1", "XX", "A3", "XX"); 38 | this.tableVerifier.withIgnoreColumns("Col 2", "Col 4").verify("name", table1, table2); 39 | 40 | TableTestUtils.getHtml(this.tableVerifier); 41 | snapshot.assertThat(TableTestUtils.getHtml(this.tableVerifier)).asText().matchesSnapshotText(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/adapters/ColumnFilterAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.adapters; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.DefaultVerifiableTableAdapter; 21 | import java.util.function.Predicate; 22 | 23 | class ColumnFilterAdapter extends DefaultVerifiableTableAdapter { 24 | private final int[] indexMap; 25 | private int columnCount = 0; 26 | 27 | ColumnFilterAdapter(VerifiableTable delegate, Predicate columnFilter) { 28 | super(delegate); 29 | this.indexMap = new int[delegate.getColumnCount()]; 30 | for (int i = 0; i < delegate.getColumnCount(); i++) { 31 | if (columnFilter.test(delegate.getColumnName(i))) { 32 | indexMap[this.columnCount++] = i; 33 | } 34 | } 35 | } 36 | 37 | @Override 38 | public int getColumnCount() { 39 | return this.columnCount; 40 | } 41 | 42 | @Override 43 | public String getColumnName(int columnIndex) { 44 | return super.getColumnName(this.indexMap[columnIndex]); 45 | } 46 | 47 | @Override 48 | public Object getValueAt(int rowIndex, int columnIndex) { 49 | return super.getValueAt(rowIndex, this.indexMap[columnIndex]); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/TestTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | public class TestTable implements VerifiableTable { 23 | private final String[] headers; 24 | private final List rows; 25 | 26 | public TestTable(String... headers) { 27 | this.headers = headers; 28 | this.rows = new ArrayList<>(); 29 | } 30 | 31 | public TestTable withRow(Object... row) { 32 | if (row.length != this.headers.length) { 33 | throw new IllegalArgumentException( 34 | "Row size " + row.length + " does not match header count " + this.headers.length); 35 | } 36 | this.rows.add(row); 37 | return this; 38 | } 39 | 40 | @Override 41 | public int getRowCount() { 42 | return this.rows.size(); 43 | } 44 | 45 | @Override 46 | public int getColumnCount() { 47 | return this.headers.length; 48 | } 49 | 50 | @Override 51 | public String getColumnName(int columnIndex) { 52 | return this.headers[columnIndex]; 53 | } 54 | 55 | @Override 56 | public Object getValueAt(int rowIndex, int columnIndex) { 57 | return this.rows.get(rowIndex)[columnIndex]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/core/Tablasco.java: -------------------------------------------------------------------------------- 1 | package com.gs.tablasco.core; 2 | 3 | import com.gs.tablasco.NamedTable; 4 | import com.gs.tablasco.VerifiableTable; 5 | import com.gs.tablasco.verify.*; 6 | import java.nio.file.Path; 7 | import java.util.HashSet; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.stream.Collectors; 11 | 12 | public class Tablasco { 13 | 14 | private final VerifierConfig verifierConfig; 15 | private final HtmlConfig htmlConfig; 16 | private final String testName; 17 | 18 | public Tablasco(VerifierConfig verifierConfig, HtmlConfig htmlConfig, String testName) { 19 | this.verifierConfig = verifierConfig; 20 | this.htmlConfig = htmlConfig; 21 | this.testName = testName; 22 | } 23 | 24 | public Map verifyTables(List expectedTables, List actualTables) { 25 | Map expectedTableMap = 26 | expectedTables.stream().collect(Collectors.toMap(NamedTable::getName, NamedTable::getTable)); 27 | Map actualTableMap = 28 | actualTables.stream().collect(Collectors.toMap(NamedTable::getName, NamedTable::getTable)); 29 | return this.verifyTables(expectedTableMap, actualTableMap); 30 | } 31 | 32 | public Map verifyTables( 33 | Map expectedTables, Map actualTables) { 34 | MultiTableVerifier multiTableVerifier = new MultiTableVerifier(verifierConfig); 35 | return multiTableVerifier.verifyTables(expectedTables, actualTables); 36 | } 37 | 38 | public void writeResults(Path outputFile, Map results) { 39 | HtmlFormatter htmlFormatter = new HtmlFormatter(outputFile.toFile(), this.htmlConfig, new HashSet<>()); 40 | htmlFormatter.appendResults(this.testName, results, null); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/IgnoreTablesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import de.skuzzle.test.snapshots.Snapshot; 20 | import de.skuzzle.test.snapshots.junit5.EnableSnapshotTests; 21 | import java.io.IOException; 22 | import org.junit.jupiter.api.Test; 23 | import org.junit.jupiter.api.extension.RegisterExtension; 24 | 25 | @EnableSnapshotTests 26 | public class IgnoreTablesTest { 27 | 28 | @RegisterExtension 29 | private final TableVerifier tableVerifier = 30 | new TableVerifier().withFilePerMethod().withMavenDirectoryStrategy(); 31 | 32 | @Test 33 | void ignoreTables(Snapshot snapshot) throws IOException { 34 | VerifiableTable tableA = TableTestUtils.createTable(1, "Col 1", "A"); 35 | VerifiableTable tableX = TableTestUtils.createTable(1, "Col 1", "X"); 36 | this.tableVerifier 37 | .withIgnoreTables("table1", "table3") 38 | .verify( 39 | TableTestUtils.toNamedTables("table1", tableA, "table2", tableA, "table3", tableX), 40 | TableTestUtils.toNamedTables("table1", tableX, "table2", tableA, "table3", tableA)); 41 | 42 | TableTestUtils.getHtml(this.tableVerifier); 43 | snapshot.assertThat(TableTestUtils.getHtml(this.tableVerifier)).asText().matchesSnapshotText(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/verify/indexmap/IndexMapGeneratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | 21 | import java.util.Arrays; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class IndexMapGeneratorTest { 25 | @Test 26 | void indexOrderIsCorrect() { 27 | IndexMapGenerator generator = new IndexMapGenerator<>( 28 | Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J").iterator(), 29 | Arrays.asList("F", "G", "H", "I", "J", "K", "L", "M", "N", "O").iterator(), 30 | 0); 31 | generator.generate(); 32 | assertEquals(Arrays.asList(im(5, 0), im(6, 1), im(7, 2), im(8, 3), im(9, 4)), generator.getMatched()); 33 | assertEquals(Arrays.asList(uim(0, -1), uim(1, -1), uim(2, -1), uim(3, -1), uim(4, -1)), generator.getMissing()); 34 | assertEquals(Arrays.asList(uim(-1, 5), uim(-1, 6), uim(-1, 7), uim(-1, 8), uim(-1, 9)), generator.getSurplus()); 35 | } 36 | 37 | private static IndexMap im(int expectedIndex, int actualIndex) { 38 | return new IndexMap(expectedIndex, actualIndex); 39 | } 40 | 41 | private static UnmatchedIndexMap uim(int expectedIndex, int actualIndex) { 42 | return new UnmatchedIndexMap(expectedIndex, actualIndex); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/adapters/TableAdapters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.adapters; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import java.util.function.IntPredicate; 21 | import java.util.function.Predicate; 22 | 23 | public class TableAdapters { 24 | /** 25 | * Takes a table and row filter and returns an adapted table that contains only rows that match the filter. 26 | * 27 | * @param delegate the table to adapt 28 | * @param rowFilter a predicate that takes row index and table and returns true if the row should be included 29 | * @return the adapted table 30 | */ 31 | public static VerifiableTable withRows(VerifiableTable delegate, IntPredicate rowFilter) { 32 | return new RowFilterAdapter(delegate, rowFilter); 33 | } 34 | 35 | /** 36 | * Takes a table and column filter and returns an adapted table that contains only columns that match the filter. 37 | * 38 | * @param delegate the table to adapt 39 | * @param columnFilter a predicate that takes row index and table and returns true if the row should be included 40 | * @return the adapted table 41 | */ 42 | public static VerifiableTable withColumns(VerifiableTable delegate, Predicate columnFilter) { 43 | return new ColumnFilterAdapter(delegate, columnFilter); 44 | } 45 | 46 | private TableAdapters() {} 47 | } 48 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/SummarisedResultsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import de.skuzzle.test.snapshots.Snapshot; 20 | import de.skuzzle.test.snapshots.junit5.EnableSnapshotTests; 21 | import java.io.IOException; 22 | import org.junit.jupiter.api.Test; 23 | import org.junit.jupiter.api.extension.RegisterExtension; 24 | 25 | @EnableSnapshotTests 26 | public class SummarisedResultsTest { 27 | 28 | @RegisterExtension 29 | private final TableVerifier tableVerifier = 30 | new TableVerifier().withFilePerMethod().withMavenDirectoryStrategy().withSummarisedResults(true); 31 | 32 | @Test 33 | void summarisedResults(Snapshot snapshot) throws IOException { 34 | final VerifiableTable table1 = TableTestUtils.createTable( 35 | 2, "key", "v1", "d", "4", "d", "4", "d", "4", "d", "4", "e", "5", "e", "5", "e", "5", "e", "5"); 36 | final VerifiableTable table2 = TableTestUtils.createTable( 37 | 2, "key", "v1", "d", "4", "d", "4", "d", "4", "d", "4", "e", "x", "e", "x", "e", "x", "e", "x"); 38 | TableTestUtils.assertAssertionError(() -> tableVerifier.verify( 39 | TableTestUtils.toNamedTables("name1", table1, "name2", table1), 40 | TableTestUtils.toNamedTables("name1", table2, "name2", table2))); 41 | snapshot.assertThat(TableTestUtils.getHtml(this.tableVerifier)).asText().matchesSnapshotText(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/DefaultVerifiableTableAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | 21 | /** 22 | * A default {@link VerifiableTable} adapter that delegates all calls to an underlying delegate table. Extend this 23 | * class if you only need to modify behaviour of some methods of the udnerlying table. 24 | */ 25 | public abstract class DefaultVerifiableTableAdapter implements VerifiableTable { 26 | private final VerifiableTable delegate; 27 | 28 | /** 29 | * Creates a new {@link DefaultVerifiableTableAdapter} with an underlying table to which calls should be delegated. 30 | * @param delegate underlying table to which calls should be delegated 31 | */ 32 | protected DefaultVerifiableTableAdapter(VerifiableTable delegate) { 33 | this.delegate = delegate; 34 | } 35 | 36 | @Override 37 | public int getRowCount() { 38 | return this.delegate.getRowCount(); 39 | } 40 | 41 | @Override 42 | public int getColumnCount() { 43 | return this.delegate.getColumnCount(); 44 | } 45 | 46 | @Override 47 | public String getColumnName(int columnIndex) { 48 | return this.delegate.getColumnName(columnIndex); 49 | } 50 | 51 | @Override 52 | public Object getValueAt(int rowIndex, int columnIndex) { 53 | return this.delegate.getValueAt(rowIndex, columnIndex); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/OutputEncodingSecurityTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import de.skuzzle.test.snapshots.Snapshot; 20 | import de.skuzzle.test.snapshots.junit5.EnableSnapshotTests; 21 | import java.io.IOException; 22 | import org.junit.jupiter.api.Test; 23 | import org.junit.jupiter.api.extension.RegisterExtension; 24 | 25 | @EnableSnapshotTests 26 | public class OutputEncodingSecurityTest { 27 | 28 | @RegisterExtension 29 | private final TableVerifier tableVerifier = 30 | new TableVerifier().withFilePerMethod().withMavenDirectoryStrategy(); 31 | 32 | @Test 33 | void htmlTagsAreEncoded(Snapshot snapshot) throws IOException { 34 | final VerifiableTable table1 = TableTestUtils.createTable( 35 | 1, 36 | "Col", 37 | "", 38 | ""); 39 | final VerifiableTable table2 = TableTestUtils.createTable( 40 | 1, 41 | "Col", 42 | "", 43 | ""); 44 | TableTestUtils.assertAssertionError(() -> tableVerifier.verify("name", table1, table2)); 45 | snapshot.assertThat(TableTestUtils.getHtml(this.tableVerifier)).asText().matchesSnapshotText(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/ResultSetTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.core.Tables; 21 | import java.sql.ResultSet; 22 | import java.sql.ResultSetMetaData; 23 | import java.sql.SQLException; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | public class ResultSetTable { 28 | 29 | private ResultSetTable() {} 30 | 31 | /** 32 | * Creates a {@link VerifiableTable} from a {@link ResultSet} 33 | * 34 | * @param resultSet the result set 35 | * @return a verifiable table 36 | * @throws SQLException from reading ResultSet 37 | */ 38 | public static VerifiableTable create(ResultSet resultSet) throws SQLException { 39 | ResultSetMetaData metaData = resultSet.getMetaData(); 40 | int columnCount = metaData.getColumnCount(); 41 | List headers = new ArrayList<>(columnCount); 42 | for (int n = 1; n <= columnCount; n++) { 43 | headers.add(metaData.getColumnName(n)); 44 | } 45 | List> rows = new ArrayList<>(); 46 | while (resultSet.next()) { 47 | List row = new ArrayList<>(columnCount); 48 | for (int n = 1; n <= columnCount; n++) { 49 | row.add(resultSet.getObject(n)); 50 | } 51 | rows.add(row); 52 | } 53 | return Tables.fromList(headers, rows); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/parser/BeginningOfLineState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results.parser; 18 | 19 | import java.io.IOException; 20 | import java.io.StreamTokenizer; 21 | 22 | public class BeginningOfLineState extends ParserState { 23 | BeginningOfLineState(ExpectedResultsParser parserState) { 24 | super(parserState); 25 | } 26 | 27 | @Override 28 | public ParserState parse(StreamTokenizer st) throws IOException { 29 | ParserState nextState = null; 30 | while (nextState == null && st.ttype != StreamTokenizer.TT_EOF) { 31 | int nextToken = st.nextToken(); 32 | if (nextToken != StreamTokenizer.TT_EOL && nextToken != StreamTokenizer.TT_EOF) { 33 | if (nextValueIs(ExpectedResultsParser.SECTION_IDENTIFIER, st, nextToken)) { 34 | nextState = this.getParser().getSectionReaderState(); 35 | } else if (nextValueIs(ExpectedResultsParser.METADATA_IDENTIFIER, st, nextToken)) { 36 | nextState = this.getParser().getMetadataReaderState(); 37 | } else { 38 | nextState = this.getParser().getDataReaderState(); 39 | } 40 | } 41 | } 42 | return nextState; 43 | } 44 | 45 | private boolean nextValueIs(String sectionIdentifier, StreamTokenizer st, int nextToken) { 46 | return nextToken == StreamTokenizer.TT_WORD && st.sval.equals(sectionIdentifier); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/parser/SectionReaderState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results.parser; 18 | 19 | import java.io.IOException; 20 | import java.io.StreamTokenizer; 21 | import java.text.ParseException; 22 | 23 | public class SectionReaderState extends ParserState { 24 | SectionReaderState(ExpectedResultsParser parserState) { 25 | super(parserState); 26 | } 27 | 28 | @Override 29 | public ParserState parse(StreamTokenizer st) throws IOException, ParseException { 30 | int token = st.ttype; 31 | if (token != StreamTokenizer.TT_WORD || !st.sval.equals(ExpectedResultsParser.SECTION_IDENTIFIER)) { 32 | throw new ParseException("expected line " + st.lineno() + " to begin with Section", st.lineno()); 33 | } 34 | token = st.nextToken(); 35 | if (token != StreamTokenizer.TT_WORD && token != '"') { 36 | throw new ParseException("expected a section name on line " + st.lineno(), st.lineno()); 37 | } 38 | String testName = st.sval; 39 | 40 | token = st.nextToken(); 41 | String tableName = null; 42 | if (token == StreamTokenizer.TT_WORD || token == '"') { 43 | tableName = st.sval; 44 | token = st.nextToken(); 45 | } 46 | 47 | this.getParser().startNewSection(testName, tableName); 48 | 49 | if (token != StreamTokenizer.TT_EOL) { 50 | throw new ParseException("invalid data after the class name on line " + st.lineno(), st.lineno()); 51 | } 52 | 53 | return this.getParser().getHeaderState(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/verify/ResultSetTableTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import com.gs.tablasco.*; 20 | import com.gs.tablasco.core.Tables; 21 | import java.sql.Date; 22 | import java.sql.SQLException; 23 | import java.sql.Types; 24 | import java.util.Collections; 25 | import org.h2.tools.SimpleResultSet; 26 | import org.junit.jupiter.api.Test; 27 | import org.junit.jupiter.api.extension.RegisterExtension; 28 | 29 | public class ResultSetTableTest { 30 | 31 | @RegisterExtension 32 | private final TableVerifier tableVerifier = new TableVerifier(); 33 | 34 | @Test 35 | void create() throws SQLException { 36 | SimpleResultSet resultSet = new SimpleResultSet(); 37 | resultSet.addColumn("Name", Types.VARCHAR, 0, 0); 38 | resultSet.addColumn("Age", Types.INTEGER, 0, 0); 39 | resultSet.addColumn("Height", Types.DOUBLE, 0, 0); 40 | resultSet.addColumn("DoB", Types.DATE, 0, 0); 41 | resultSet.addRow("Joe", 70, 6.0, Date.valueOf("1940-02-16")); 42 | resultSet.addRow("Sue", 45, 5.8, Date.valueOf("1975-02-16")); 43 | VerifiableTable expected = new TestTable("Name", "Age", "Height", "DoB") 44 | .withRow("Joe", 70, 6.0, Date.valueOf("1940-02-16")) 45 | .withRow("Sue", 45, 5.8, Date.valueOf("1975-02-16")); 46 | VerifiableTable actual = Tables.fromResultSet(resultSet); 47 | this.tableVerifier.verify( 48 | Collections.singletonList(new NamedTable("table", expected)), 49 | Collections.singletonList(new NamedTable("table", actual))); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/rebase/Rebaser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.rebase; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.core.VerifierConfig; 21 | import com.gs.tablasco.verify.Metadata; 22 | import java.io.File; 23 | import java.util.Map; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | public class Rebaser { 28 | private static final Logger LOGGER = LoggerFactory.getLogger(Rebaser.class); 29 | private static Boolean rebaseMode; 30 | private final VerifierConfig verifierConfig; 31 | private final Metadata metadata; 32 | private final String[] baselineHeaders; 33 | 34 | public static boolean inRebaseMode() { 35 | if (rebaseMode == null) { 36 | rebaseMode = initializeRebaseFlag(); 37 | } 38 | return rebaseMode; 39 | } 40 | 41 | public Rebaser(VerifierConfig verifierConfig, Metadata metadata, String[] baselineHeaders) { 42 | this.verifierConfig = verifierConfig; 43 | this.metadata = metadata; 44 | this.baselineHeaders = baselineHeaders; 45 | } 46 | 47 | public void rebase(String methodName, Map actualResults, File outputFile) { 48 | LOGGER.info("Actual results for {} to {}", methodName, outputFile.getAbsolutePath()); 49 | new RebaseFileWriter( 50 | this.metadata, this.baselineHeaders, this.verifierConfig.getColumnComparators(), outputFile) 51 | .writeRebasedResults(methodName, actualResults); 52 | } 53 | 54 | private static Boolean initializeRebaseFlag() { 55 | return Boolean.valueOf(System.getProperty("rebase", "false")); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/parser/HeaderParserState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results.parser; 18 | 19 | import java.io.IOException; 20 | import java.io.StreamTokenizer; 21 | import java.text.ParseException; 22 | 23 | public class HeaderParserState extends ParserState { 24 | HeaderParserState(ExpectedResultsParser parserState) { 25 | super(parserState); 26 | } 27 | 28 | private static final int QUOTE_TOKEN = '"'; 29 | 30 | @Override 31 | public ParserState parse(StreamTokenizer st) throws IOException, ParseException { 32 | parseAttributes(st); 33 | return this.getParser().getBeginningOfLineState(); 34 | } 35 | 36 | private void parseAttributes(StreamTokenizer st) throws IOException, ParseException { 37 | int token = st.nextToken(); 38 | boolean wantAttribute = true; 39 | while (token != StreamTokenizer.TT_EOL) { 40 | if (wantAttribute) { 41 | if (token != StreamTokenizer.TT_WORD && token != QUOTE_TOKEN) { 42 | throw new ParseException("expected an column name on line " + st.lineno(), st.lineno()); 43 | } 44 | this.getParser().getExpectedTable().addColumnHeader(st.sval); 45 | } else { 46 | if (token != ',') { 47 | throw new ParseException("Expected a comma on line " + st.lineno(), st.lineno()); 48 | } 49 | } 50 | wantAttribute = !wantAttribute; 51 | token = st.nextToken(); 52 | } 53 | if (wantAttribute) { 54 | throw new ParseException("extra comma at the end of line " + st.lineno(), st.lineno()); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/HideMatchedTablesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import de.skuzzle.test.snapshots.Snapshot; 20 | import de.skuzzle.test.snapshots.junit5.EnableSnapshotTests; 21 | import java.io.IOException; 22 | import org.junit.jupiter.api.Test; 23 | import org.junit.jupiter.api.extension.RegisterExtension; 24 | 25 | @EnableSnapshotTests 26 | public class HideMatchedTablesTest { 27 | 28 | @RegisterExtension 29 | private final TableVerifier tableVerifier = 30 | new TableVerifier().withFilePerMethod().withMavenDirectoryStrategy().withHideMatchedTables(true); 31 | 32 | @Test 33 | void matchedTablesAreHidden(Snapshot snapshot) throws IOException { 34 | final VerifiableTable matchTable = new TestTable("Col").withRow("A"); 35 | final VerifiableTable outOfOrderTableExpected = new TestTable("Col 1", "Col 2").withRow("A", "B"); 36 | final VerifiableTable outOfOrderTableActual = new TestTable("Col 2", "Col 1").withRow("B", "A"); 37 | final VerifiableTable breakTableExpected = new TestTable("Col").withRow("A"); 38 | final VerifiableTable breakTableActual = new TestTable("Col").withRow("B"); 39 | TableTestUtils.assertAssertionError(() -> tableVerifier.verify( 40 | TableTestUtils.toNamedTables( 41 | "match", matchTable, "break", breakTableExpected, "outOfOrder", outOfOrderTableExpected), 42 | TableTestUtils.toNamedTables( 43 | "match", matchTable, "break", breakTableActual, "outOfOrder", outOfOrderTableActual))); 44 | TableTestUtils.getHtml(this.tableVerifier); 45 | snapshot.assertThat(TableTestUtils.getHtml(this.tableVerifier)).asText().matchesSnapshotText(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/parser/MetadataReaderState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results.parser; 18 | 19 | import java.io.IOException; 20 | import java.io.StreamTokenizer; 21 | import java.text.ParseException; 22 | 23 | /** 24 | * Expected metadata format 25 | * Metadata "key1" "value1", "key2" "value2" 26 | */ 27 | public class MetadataReaderState extends ParserState { 28 | MetadataReaderState(ExpectedResultsParser parser) { 29 | super(parser); 30 | } 31 | 32 | @Override 33 | public ParserState parse(StreamTokenizer st) throws IOException, ParseException { 34 | int token = st.ttype; 35 | while (!endOfLineOrFile(token)) { 36 | // token 1: key 37 | st.nextToken(); 38 | if (!endOfLineOrFile(token)) { 39 | String key = st.sval; 40 | 41 | // token 2: value 42 | token = st.nextToken(); 43 | if (endOfLineOrFile(token)) { 44 | throw new ParseException("Expected a value for metadata key: " + key, st.lineno()); 45 | } 46 | String value = st.sval; 47 | this.getParser().getExpectedResults().addMetadata(key, value); 48 | 49 | // token 3: EOL or , 50 | token = st.nextToken(); 51 | if (!endOfLineOrFile(token) && token != (int) ',') { 52 | throw new ParseException("Expected EOL or EOF or a comma on line " + st.lineno(), st.lineno()); 53 | } 54 | } 55 | } 56 | 57 | return this.getParser().getBeginningOfLineState(); 58 | } 59 | 60 | private static boolean endOfLineOrFile(int token) { 61 | return token == StreamTokenizer.TT_EOL || token == StreamTokenizer.TT_EOF; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/parser/DataReaderState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results.parser; 18 | 19 | import java.io.IOException; 20 | import java.io.StreamTokenizer; 21 | import java.text.ParseException; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | public class DataReaderState extends ParserState { 26 | private String sectionName; 27 | 28 | DataReaderState(ExpectedResultsParser parserState) { 29 | super(parserState); 30 | } 31 | 32 | @Override 33 | public ParserState parse(StreamTokenizer st) throws IOException, ParseException { 34 | if (this.sectionName == null) { 35 | throw new ParseException("no section name found before line " + st.lineno(), st.lineno()); 36 | } 37 | 38 | // parse the data 39 | int currentAttribute = 0; 40 | int token = st.ttype; 41 | 42 | boolean wantData = true; 43 | List rowValue = new ArrayList<>(); 44 | while (token != StreamTokenizer.TT_EOL && token != StreamTokenizer.TT_EOF) { 45 | if (wantData) { 46 | this.getParser().getExpectedTable().parseData(st, currentAttribute, rowValue); 47 | currentAttribute++; 48 | } else { 49 | if (token != ',') { 50 | throw new ParseException("Expected a comma on line " + st.lineno(), st.lineno()); 51 | } 52 | } 53 | wantData = !wantData; 54 | token = st.nextToken(); 55 | } 56 | if (!rowValue.isEmpty()) { 57 | this.getParser().getExpectedTable().addRowToList(rowValue); 58 | } 59 | return this.getParser().getBeginningOfLineState(); 60 | } 61 | 62 | void setSectionName(String sectionName) { 63 | this.sectionName = sectionName; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/CreateActualResultsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | 22 | import java.nio.file.Files; 23 | import org.junit.jupiter.api.BeforeEach; 24 | import org.junit.jupiter.api.Test; 25 | import org.junit.jupiter.api.extension.RegisterExtension; 26 | 27 | public class CreateActualResultsTest { 28 | @RegisterExtension 29 | private final TableVerifier verifier = 30 | new TableVerifier().withMavenDirectoryStrategy().withFilePerMethod(); 31 | 32 | @RegisterExtension 33 | public final TableTestUtils.TestExtensionContext extensionContext = new TableTestUtils.TestExtensionContext(); 34 | 35 | @BeforeEach 36 | void setUp() throws Exception { 37 | this.verifier.beforeEach(this.extensionContext.get()); 38 | Files.deleteIfExists(this.verifier.getActualFile().toPath()); 39 | } 40 | 41 | @Test 42 | void testDefault() { 43 | this.verifier.verify(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL); 44 | this.verifier.afterEach(this.extensionContext.get()); 45 | assertTrue(this.verifier.getActualFile().exists()); 46 | } 47 | 48 | @Test 49 | void testTrue() { 50 | this.verifier.withCreateActualResults(true).verify(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL); 51 | this.verifier.afterEach(this.extensionContext.get()); 52 | assertTrue(this.verifier.getActualFile().exists()); 53 | } 54 | 55 | @Test 56 | void testFalse() { 57 | this.verifier.withCreateActualResults(false).verify(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL); 58 | this.verifier.afterEach(this.extensionContext.get()); 59 | assertFalse(this.verifier.getActualFile().exists()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/RowIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.ColumnComparators; 21 | import java.util.Iterator; 22 | import java.util.List; 23 | 24 | public abstract class RowIterator implements Iterator { 25 | private final VerifiableTable table; 26 | private final List columns; 27 | private final ColumnComparators columnComparators; 28 | private int rowIndex; 29 | private final int lastUnMatchedOffset; 30 | 31 | RowIterator( 32 | VerifiableTable table, 33 | List columns, 34 | ColumnComparators columnComparators, 35 | int initialIndex, 36 | int lastUnMatchedOffset) { 37 | this.table = table; 38 | this.columns = columns; 39 | this.columnComparators = columnComparators; 40 | this.rowIndex = initialIndex; 41 | this.lastUnMatchedOffset = lastUnMatchedOffset; 42 | } 43 | 44 | @Override 45 | public boolean hasNext() { 46 | return this.rowIndex < this.table.getRowCount() - this.lastUnMatchedOffset; 47 | } 48 | 49 | @Override 50 | public RowView next() { 51 | RowView rowView = this.createRowView(this.rowIndex); 52 | this.rowIndex++; 53 | return rowView; 54 | } 55 | 56 | protected abstract RowView createRowView(int rowIndex); 57 | 58 | @Override 59 | public void remove() { 60 | throw new UnsupportedOperationException(); 61 | } 62 | 63 | protected VerifiableTable getTable() { 64 | return this.table; 65 | } 66 | 67 | protected List getColumns() { 68 | return this.columns; 69 | } 70 | 71 | protected ColumnComparators getColumnComparators() { 72 | return this.columnComparators; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/ExpectedResults.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.Metadata; 21 | import java.util.Collections; 22 | import java.util.LinkedHashMap; 23 | import java.util.Map; 24 | 25 | public class ExpectedResults { 26 | private final Map> tablesByTestName = new LinkedHashMap<>(); 27 | private final Metadata metadata = Metadata.newEmpty(); 28 | 29 | public VerifiableTable getTable(String testName) { 30 | return this.getTable(testName, null); 31 | } 32 | 33 | public Map getTables(String testName) { 34 | Map tables = this.tablesByTestName.get(testName); 35 | return tables == null ? Collections.emptyMap() : tables; 36 | } 37 | 38 | public VerifiableTable getTable(String testName, String tableName) { 39 | return this.getTables(testName).get(translateTableName(tableName)); 40 | } 41 | 42 | public void addTable(String testName, String tableName, VerifiableTable table) { 43 | Map tables = 44 | this.tablesByTestName.computeIfAbsent(testName, k -> new LinkedHashMap<>()); 45 | String key = translateTableName(tableName); 46 | if (tables.containsKey(key)) { 47 | throw new IllegalStateException("Duplicate expected table detected: " + testName + '/' + tableName); 48 | } 49 | tables.put(key, table); 50 | } 51 | 52 | private static String translateTableName(String tableName) { 53 | return tableName == null ? "" : tableName; 54 | } 55 | 56 | public void addMetadata(String key, String value) { 57 | this.metadata.add(key, value); 58 | } 59 | 60 | public Metadata getMetadata() { 61 | return this.metadata; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/verify/indexmap/UnmatchedIndexMapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class UnmatchedIndexMapTest { 24 | private final UnmatchedIndexMap unmatched = new UnmatchedIndexMap(0, 0); 25 | private final UnmatchedIndexMap unmatched1 = new UnmatchedIndexMap(1, 1); 26 | private final UnmatchedIndexMap unmatched2 = new UnmatchedIndexMap(2, 2); 27 | 28 | @Test 29 | void testAddPartialMatchFailsIfSelf() { 30 | assertThrows(IllegalArgumentException.class, () -> this.unmatched.addMatch(2, this.unmatched)); 31 | } 32 | 33 | @Test 34 | void testInitialState() { 35 | assertNull(this.unmatched.getBestMutualMatch()); 36 | } 37 | 38 | @Test 39 | void addSinglePartialMatch() { 40 | this.unmatched.addMatch(1, this.unmatched1); 41 | assertTrue(this.unmatched.match()); 42 | assertEquals(this.unmatched1, this.unmatched.getBestMutualMatch()); 43 | assertEquals(this.unmatched, this.unmatched1.getBestMutualMatch()); 44 | } 45 | 46 | @Test 47 | void bestMatchAddedFirst() { 48 | this.unmatched.addMatch(2, this.unmatched1); 49 | this.unmatched.addMatch(1, this.unmatched2); 50 | assertTrue(this.unmatched.match()); 51 | assertEquals(this.unmatched1, this.unmatched.getBestMutualMatch()); 52 | assertEquals(this.unmatched, this.unmatched1.getBestMutualMatch()); 53 | } 54 | 55 | @Test 56 | void bestMatchAddedLast() { 57 | this.unmatched.addMatch(1, this.unmatched2); 58 | this.unmatched.addMatch(2, this.unmatched1); 59 | assertTrue(this.unmatched.match()); 60 | assertEquals(this.unmatched1, this.unmatched.getBestMutualMatch()); 61 | assertEquals(this.unmatched, this.unmatched1.getBestMutualMatch()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/results/parser/ExpectedTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results.parser; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import java.io.StreamTokenizer; 21 | import java.text.ParseException; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | public class ExpectedTable implements VerifiableTable { 26 | private final List headers = new ArrayList<>(); 27 | private final List> rowValues = new ArrayList<>(); 28 | 29 | void addColumnHeader(String header) { 30 | this.headers.add(header); 31 | } 32 | 33 | void parseData(StreamTokenizer st, int currentNumber, List rowValue) throws ParseException { 34 | if (currentNumber >= this.headers.size()) { 35 | throw new ParseException("extra data on line " + st.lineno(), st.lineno()); 36 | } 37 | if (st.ttype == StreamTokenizer.TT_NUMBER) { 38 | rowValue.add(st.nval); 39 | } else { 40 | rowValue.add(st.sval); 41 | } 42 | } 43 | 44 | void addRowToList(List rowValue) { 45 | this.rowValues.add(rowValue); 46 | } 47 | 48 | @Override 49 | public int getRowCount() { 50 | return this.rowValues.size(); 51 | } 52 | 53 | @Override 54 | public int getColumnCount() { 55 | return this.headers.size(); 56 | } 57 | 58 | @Override 59 | public String getColumnName(int columnIndex) { 60 | return columnIndex < this.getColumnCount() ? this.headers.get(columnIndex) : null; 61 | } 62 | 63 | @Override 64 | public Object getValueAt(int rowIndex, int columnIndex) { 65 | if (rowIndex < this.getRowCount()) { 66 | List rowData = this.rowValues.get(rowIndex); 67 | if (columnIndex < rowData.size()) { 68 | return rowData.get(columnIndex); 69 | } 70 | } 71 | return null; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/HtmlOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import java.util.Set; 20 | 21 | public class HtmlOptions { 22 | private final boolean displayAssertionSummary; 23 | private final int htmlRowLimit; 24 | private final boolean hideMatchedTables; 25 | private final boolean hideMatchedRows; 26 | private final boolean hideMatchedColumns; 27 | private final boolean summarizedResults; 28 | private final Set tablesToAlwaysShowMatchedRowsFor; 29 | 30 | public HtmlOptions( 31 | boolean displayAssertionSummary, 32 | int htmlRowLimit, 33 | boolean hideMatchedTables, 34 | boolean hideMatchedRows, 35 | boolean hideMatchedColumns, 36 | boolean summarizedResults, 37 | Set tablesToAlwaysShowMatchedRowsFor) { 38 | this.displayAssertionSummary = displayAssertionSummary; 39 | this.hideMatchedColumns = hideMatchedColumns; 40 | this.hideMatchedTables = hideMatchedTables; 41 | this.htmlRowLimit = htmlRowLimit; 42 | this.hideMatchedRows = hideMatchedRows; 43 | this.summarizedResults = summarizedResults; 44 | this.tablesToAlwaysShowMatchedRowsFor = tablesToAlwaysShowMatchedRowsFor; 45 | } 46 | 47 | boolean isHideMatchedColumns() { 48 | return this.hideMatchedColumns; 49 | } 50 | 51 | boolean isDisplayAssertionSummary() { 52 | return this.displayAssertionSummary; 53 | } 54 | 55 | int getHtmlRowLimit() { 56 | return this.htmlRowLimit; 57 | } 58 | 59 | boolean isHideMatchedRowsFor(String tableName) { 60 | return this.hideMatchedRows && !this.tablesToAlwaysShowMatchedRowsFor.contains(tableName); 61 | } 62 | 63 | boolean isHideMatchedTables() { 64 | return this.hideMatchedTables; 65 | } 66 | 67 | public boolean isSummarizedResults() { 68 | return summarizedResults; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/legal/CopyrightTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.legal; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | 22 | import java.io.File; 23 | import java.io.IOException; 24 | import java.nio.charset.Charset; 25 | import java.nio.file.Files; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import org.junit.jupiter.api.Test; 29 | 30 | class CopyrightTest { 31 | @Test 32 | void test() throws IOException { 33 | List javaSource = new ArrayList<>(); 34 | scan(new File("src"), javaSource); 35 | assertFalse(javaSource.isEmpty()); 36 | for (File file : javaSource) { 37 | List lines = Files.readAllLines(file.toPath(), Charset.defaultCharset()); 38 | boolean foundApacheLicence = false; 39 | boolean foundApacheUrl = false; 40 | for (String line : lines) { 41 | if (line.contains("Licensed under the Apache License, Version 2.0")) { 42 | foundApacheLicence = true; 43 | } 44 | if (line.contains("http://www.apache.org/licenses/LICENSE-2.0")) { 45 | foundApacheUrl = true; 46 | } 47 | } 48 | assertTrue(foundApacheLicence, "Found Apache license in " + file.getName()); 49 | assertTrue(foundApacheUrl, "Found Apache license URL in " + file.getName()); 50 | } 51 | } 52 | 53 | private void scan(File dir, List javaSource) { 54 | File[] files = dir.listFiles(); 55 | if (files != null) { 56 | for (File file : files) { 57 | if (file.isDirectory()) { 58 | scan(file, javaSource); 59 | } else if (file.getName().endsWith(".java")) { 60 | javaSource.add(file); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/core/Tables.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.core; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.ListVerifiableTable; 21 | import com.gs.tablasco.verify.ResultSetTable; 22 | import java.sql.ResultSet; 23 | import java.sql.SQLException; 24 | import java.util.List; 25 | 26 | @SuppressWarnings({"unchecked", "rawtypes"}) 27 | public class Tables { 28 | /** 29 | * Creates a {@link VerifiableTable} from a list containing headers and rows as lists. The first must be the list 30 | * of headers, the remaining items are rows as lists of objects. The size of each row must match the number of 31 | * headers. 32 | * 33 | * @param headersAndRows list of headers and rows 34 | * @return verifiable table 35 | */ 36 | public static VerifiableTable fromList(List> headersAndRows) { 37 | List headers = (List) headersAndRows.get(0); 38 | List rows = headersAndRows.subList(1, headersAndRows.size()); 39 | return new ListVerifiableTable(headers, (List>) rows); 40 | } 41 | 42 | /** 43 | * Creates a {@link VerifiableTable} from a list of headers and list containing rows as lists. The size of each row 44 | * must match the number of headers. 45 | * 46 | * @param headers list of headers 47 | * @param rows list rows 48 | * @return the verifiable table 49 | */ 50 | public static VerifiableTable fromList(List headers, List> rows) { 51 | List rowsList = rows; 52 | return new ListVerifiableTable(headers, (List>) rowsList); 53 | } 54 | 55 | /** 56 | * Creates a {@link VerifiableTable} from a JDBC {@link ResultSet} 57 | * 58 | * @param resultSet the result set 59 | * @return the verifiable table 60 | */ 61 | public static VerifiableTable fromResultSet(ResultSet resultSet) throws SQLException { 62 | return ResultSetTable.create(resultSet); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/ExpectedResultsLoaderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | 21 | import com.gs.tablasco.files.FilePerClassStrategy; 22 | import com.gs.tablasco.results.ExpectedResultsLoader; 23 | import java.io.File; 24 | import java.io.IOException; 25 | import java.io.InputStream; 26 | import java.nio.file.Files; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | import org.junit.jupiter.api.Test; 29 | import org.junit.jupiter.api.extension.RegisterExtension; 30 | 31 | public class ExpectedResultsLoaderTest implements ExpectedResultsLoader { 32 | @RegisterExtension 33 | private final TableVerifier verifier = new TableVerifier() 34 | .withMavenDirectoryStrategy() 35 | .withFileStrategy(new FilePerClassStrategy() { 36 | @Override 37 | public String getExpectedFilename(Class testClass, String methodName) { 38 | return super.getExpectedFilename(testClass, methodName).replace(".txt", ".raw.txt"); 39 | } 40 | }) 41 | .withExpectedResultsLoader(this); 42 | 43 | private static final AtomicInteger loadCount = new AtomicInteger(); 44 | 45 | @Override 46 | public InputStream load(File expectedFile) throws IOException { 47 | loadCount.incrementAndGet(); 48 | File file = new File(expectedFile.getPath().replace(".raw.txt", ".txt")); 49 | return Files.newInputStream(file.toPath()); 50 | } 51 | 52 | @Test 53 | void testOne() { 54 | runTest(); 55 | } 56 | 57 | @Test 58 | void testTwo() { 59 | runTest(); 60 | } 61 | 62 | @Test 63 | void testThree() { 64 | runTest(); 65 | } 66 | 67 | private void runTest() { 68 | this.verifier.verify(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL); 69 | assertEquals(1, loadCount.intValue(), "Results should be loaded once and used by all three tests"); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/CreateActualResultsOnFailureTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertFalse; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | 22 | import java.nio.file.Files; 23 | import org.junit.jupiter.api.BeforeEach; 24 | import org.junit.jupiter.api.Test; 25 | import org.junit.jupiter.api.extension.RegisterExtension; 26 | 27 | public class CreateActualResultsOnFailureTest { 28 | 29 | @RegisterExtension 30 | private final TableVerifier verifier = 31 | new TableVerifier().withMavenDirectoryStrategy().withFilePerMethod(); 32 | 33 | @RegisterExtension 34 | public final TableTestUtils.TestExtensionContext extensionContext = new TableTestUtils.TestExtensionContext(); 35 | 36 | @BeforeEach 37 | void setUp() throws Exception { 38 | this.verifier.beforeEach(this.extensionContext.get()); 39 | Files.deleteIfExists(this.verifier.getActualFile().toPath()); 40 | } 41 | 42 | @Test 43 | void testTrue() { 44 | this.verifier.withCreateActualResultsOnFailure(true).verify(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL); 45 | assertFalse(this.verifier.getActualFile().exists()); 46 | } 47 | 48 | @Test 49 | void testFalse() { 50 | this.verifier.withCreateActualResultsOnFailure(false).verify(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL); 51 | assertTrue(this.verifier.getActualFile().exists()); 52 | } 53 | 54 | @Test 55 | void testTrueFail() { 56 | TableTestUtils.assertAssertionError(() -> verifier.withCreateActualResultsOnFailure(true) 57 | .verify(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL)); 58 | assertTrue(this.verifier.getActualFile().exists()); 59 | } 60 | 61 | @Test 62 | void testFalseFail() { 63 | TableTestUtils.assertAssertionError(() -> verifier.withCreateActualResultsOnFailure(false) 64 | .verify(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL)); 65 | assertTrue(this.verifier.getActualFile().exists()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/ExampleTableVerifierTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import java.util.Arrays; 20 | import java.util.List; 21 | import org.junit.jupiter.api.Test; 22 | import org.junit.jupiter.api.extension.RegisterExtension; 23 | 24 | public class ExampleTableVerifierTest { 25 | 26 | @RegisterExtension 27 | private final TableVerifier tableVerifier = new TableVerifier().withMavenDirectoryStrategy(); 28 | 29 | @Test 30 | void example() { 31 | List movieRanks = Arrays.asList( 32 | new Movie("The Batman", "2022", 1, 8.3), 33 | new Movie("Deep Water", "2022", 2, 5.4), 34 | new Movie("X", "2022", 3, 7.4), 35 | new Movie("The Adam Project", "2022", 4, 6.7), 36 | new Movie("Turning Red", "2022", 5, 7.1), 37 | new Movie("Windfall", "2022", 6, 5.7)); 38 | this.tableVerifier.verify("Most Popular Movies", new MovieTable(movieRanks)); 39 | } 40 | 41 | private record MovieTable(List rows) implements VerifiableTable { 42 | 43 | @Override 44 | public int getRowCount() { 45 | return this.rows.size(); 46 | } 47 | 48 | @Override 49 | public int getColumnCount() { 50 | return 4; 51 | } 52 | 53 | @Override 54 | public String getColumnName(int columnIndex) { 55 | return switch (columnIndex) { 56 | case 0 -> "Title"; 57 | case 1 -> "Year"; 58 | case 2 -> "User Rank"; 59 | default -> "IMDb Rating"; 60 | }; 61 | } 62 | 63 | @Override 64 | public Object getValueAt(int rowIndex, int columnIndex) { 65 | Movie row = this.rows.get(rowIndex); 66 | return switch (columnIndex) { 67 | case 0 -> row.title; 68 | case 1 -> row.year; 69 | case 2 -> row.rank; 70 | default -> row.rating; 71 | }; 72 | } 73 | } 74 | 75 | private record Movie(String title, String year, int rank, double rating) {} 76 | } 77 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/verify/SingleTableVerifierMinBestMatchThresholdTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import com.gs.tablasco.verify.indexmap.IndexMapTableVerifier; 20 | import java.util.Arrays; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import org.junit.jupiter.api.MethodOrderer.MethodName; 25 | import org.junit.jupiter.api.TestMethodOrder; 26 | 27 | @TestMethodOrder(MethodName.class) 28 | public class SingleTableVerifierMinBestMatchThresholdTest extends AbstractSingleTableVerifierTest { 29 | @Override 30 | protected IndexMapTableVerifier createSingleTableVerifier(ColumnComparators columnComparators) { 31 | return new IndexMapTableVerifier(columnComparators, true, 1, false, false); 32 | } 33 | 34 | @Override 35 | protected List> getExpectedVerification(String methodName) { 36 | return ROW_KEY_VERIFICATIONS.get(methodName); 37 | } 38 | 39 | static final Map>> ROW_KEY_VERIFICATIONS; 40 | 41 | static { 42 | ROW_KEY_VERIFICATIONS = new HashMap<>(SingleTableVerifierMaxBestMatchThresholdTest.MAX_BEST_MATCH_THRESHOLD); 43 | addVerification( 44 | row(pass("Col 1"), pass("Col 2"), pass("Col 3")), 45 | row(pass("A"), pass("A"), fail("0", "1")), 46 | row(pass("A"), pass("A"), fail("2", "3")), 47 | row(pass("B"), pass("A"), fail("0", "1")), 48 | row(pass("C"), pass("B"), fail("0", "1")), 49 | row(pass("D"), pass("B"), fail("0", "1")), 50 | row(pass("E"), pass("C"), fail("0", "1")), 51 | row(surplus("X"), surplus("C"), surplus("0")), 52 | row(missing("Y"), missing("C"), missing("1")), 53 | row(surplus("X"), surplus("X"), surplus("0")), 54 | row(missing("Y"), missing("Y"), missing("1"))); 55 | } 56 | 57 | private static void addVerification(List... rows) { 58 | List> castRows = Arrays.stream(rows) 59 | .map(r -> r.stream().map(ResultCell.class::cast).toList()) 60 | .toList(); 61 | ROW_KEY_VERIFICATIONS.put("adaptiveMatcherLeavesLeastUnmatchedRows", castRows); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/Metadata.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import java.text.Format; 20 | import java.text.SimpleDateFormat; 21 | import java.util.*; 22 | 23 | public class Metadata { 24 | private static final Format DATE_TIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 25 | private static final String RECORDED_AT = "Recorded At"; 26 | 27 | private final Map data = new LinkedHashMap<>(); 28 | 29 | private Metadata() {} 30 | 31 | public static Metadata newEmpty() { 32 | return new Metadata(); 33 | } 34 | 35 | public static Metadata newWithRecordedAt() { 36 | Metadata metadata = new Metadata(); 37 | metadata.addDate(RECORDED_AT, new Date()); 38 | return metadata; 39 | } 40 | 41 | public void add(String key, String value) { 42 | this.data.put(key, value); 43 | } 44 | 45 | public void addDate(String key, Date date) { 46 | this.add(key, DATE_TIME_FORMATTER.format(date)); 47 | } 48 | 49 | public List> getData() { 50 | return new ArrayList<>(this.data.entrySet()); 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return toString(""); 56 | } 57 | 58 | public String toString(String stringQualifier) { 59 | StringBuilder builder = new StringBuilder(); 60 | makeString(builder, stringQualifier); 61 | return builder.toString(); 62 | } 63 | 64 | private void makeString(StringBuilder builder, String stringQualifier) { 65 | Iterator> iterator = this.data.entrySet().iterator(); 66 | while (iterator.hasNext()) { 67 | Map.Entry pair = iterator.next(); 68 | addData(pair.getKey(), pair.getValue(), builder, stringQualifier); 69 | if (iterator.hasNext()) { 70 | builder.append(',').append(' '); 71 | } 72 | } 73 | } 74 | 75 | private static void addData(String key, String value, StringBuilder builder, String stringQualifier) { 76 | builder.append(stringQualifier).append(key).append(stringQualifier); 77 | builder.append(' '); 78 | builder.append(stringQualifier).append(value).append(stringQualifier); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/RowView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.CellComparator; 21 | import com.gs.tablasco.verify.ColumnComparators; 22 | import java.util.List; 23 | 24 | public abstract class RowView { 25 | private final VerifiableTable table; 26 | private final List columnIndices; 27 | private final int rowIndex; 28 | private final ColumnComparators columnComparators; 29 | 30 | RowView(VerifiableTable table, List columnIndices, ColumnComparators columnComparators, int rowIndex) { 31 | this.table = table; 32 | this.columnIndices = columnIndices; 33 | this.rowIndex = rowIndex; 34 | this.columnComparators = columnComparators; 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | int hashCode = 0; 40 | for (IndexMap column : this.columnIndices) { 41 | if (column.isMatched()) { 42 | CellComparator comparator = getCellComparator(column); 43 | hashCode += comparator.computeHashCode(this.getValue(column)); 44 | } 45 | } 46 | return hashCode; 47 | } 48 | 49 | private CellComparator getCellComparator(IndexMap column) { 50 | return this.columnComparators.getComparator(this.table.getColumnName(this.getColumnIndex(column))); 51 | } 52 | 53 | private Object getValue(IndexMap column) { 54 | return this.table.getValueAt(this.rowIndex, getColumnIndex(column)); 55 | } 56 | 57 | protected abstract int getColumnIndex(IndexMap column); 58 | 59 | @Override 60 | public boolean equals(Object obj) { 61 | if (obj instanceof RowView) { 62 | RowView that = (RowView) obj; 63 | for (IndexMap column : this.columnIndices) { 64 | if (column.isMatched()) { 65 | Object thisVal = this.getValue(column); 66 | Object thatVal = that.getValue(column); 67 | if (!this.getCellComparator(column).equals(thisVal, thatVal)) { 68 | return false; 69 | } 70 | } 71 | } 72 | return true; 73 | } 74 | return false; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/BestMatchPartialMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.CellComparator; 21 | import com.gs.tablasco.verify.ColumnComparators; 22 | import java.util.List; 23 | 24 | public class BestMatchPartialMatcher implements PartialMatcher { 25 | private final VerifiableTable actualData; 26 | private final VerifiableTable expectedData; 27 | private final ColumnComparators columnComparators; 28 | 29 | BestMatchPartialMatcher( 30 | VerifiableTable actualData, VerifiableTable expectedData, ColumnComparators columnComparators) { 31 | this.actualData = actualData; 32 | this.expectedData = expectedData; 33 | this.columnComparators = columnComparators; 34 | } 35 | 36 | @Override 37 | public void match( 38 | List allMissingRows, 39 | List allSurplusRows, 40 | List matchedColumns) { 41 | for (UnmatchedIndexMap expected : allMissingRows) { 42 | for (UnmatchedIndexMap actual : allSurplusRows) { 43 | int matchScore = 0; 44 | for (int colIndex = 0; colIndex < matchedColumns.size(); colIndex++) { 45 | IndexMap column = matchedColumns.get(colIndex); 46 | Object expectedValue = 47 | this.expectedData.getValueAt(expected.getExpectedIndex(), column.getExpectedIndex()); 48 | Object actualValue = this.actualData.getValueAt(actual.getActualIndex(), column.getActualIndex()); 49 | CellComparator comparator = 50 | this.columnComparators.getComparator(expectedData.getColumnName(column.getExpectedIndex())); 51 | if (comparator.equals(actualValue, expectedValue)) { 52 | int inverseColumnNumber = matchedColumns.size() - colIndex; 53 | matchScore += inverseColumnNumber * inverseColumnNumber; 54 | } 55 | } 56 | if (matchScore > 0) { 57 | expected.addMatch(matchScore, actual); 58 | } 59 | } 60 | } 61 | UnmatchedIndexMap.linkBestMatches(allMissingRows); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/verify/indexmap/TimeBoundPartialMatcherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | import java.util.concurrent.TimeoutException; 22 | import java.util.concurrent.atomic.AtomicBoolean; 23 | import org.junit.jupiter.api.Test; 24 | 25 | class TimeBoundPartialMatcherTest { 26 | @Test 27 | void executionTimesOut() { 28 | PartialMatcher endlessMatcher = (allMissingRows, allSurplusRows, matchedColumns) -> { 29 | try { 30 | Thread.sleep(10_000); 31 | } catch (InterruptedException e) { 32 | Thread.currentThread().interrupt(); 33 | fail("Unexpected interrupt"); 34 | } 35 | }; 36 | RuntimeException runtimeException = assertThrows( 37 | RuntimeException.class, () -> new TimeBoundPartialMatcher(endlessMatcher, 1L).match(null, null, null)); 38 | assertInstanceOf(TimeoutException.class, runtimeException.getCause()); 39 | } 40 | 41 | @Test 42 | void matchingExceptionPropagates() { 43 | assertThrows(IndexOutOfBoundsException.class, () -> { 44 | PartialMatcher dyingMatcher = (allMissingRows, allSurplusRows, matchedColumns) -> { 45 | throw new IndexOutOfBoundsException("Boom"); 46 | }; 47 | new TimeBoundPartialMatcher(dyingMatcher, Long.MAX_VALUE).match(null, null, null); 48 | }); 49 | } 50 | 51 | @Test 52 | void matchingErrorPropagates() { 53 | PartialMatcher dyingMatcher = (allMissingRows, allSurplusRows, matchedColumns) -> { 54 | throw new NoSuchMethodError(); 55 | }; 56 | RuntimeException runtimeException = 57 | assertThrows(RuntimeException.class, () -> new TimeBoundPartialMatcher(dyingMatcher, Long.MAX_VALUE) 58 | .match(null, null, null)); 59 | assertInstanceOf(NoSuchMethodError.class, runtimeException.getCause()); 60 | } 61 | 62 | @Test 63 | void successfulMatch() { 64 | final AtomicBoolean matched = new AtomicBoolean(false); 65 | PartialMatcher matcher = (allMissingRows, allSurplusRows, matchedColumns) -> matched.set(true); 66 | new TimeBoundPartialMatcher(matcher, Long.MAX_VALUE).match(null, null, null); 67 | assertTrue(matched.get()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/TimeBoundPartialMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import java.util.List; 20 | import java.util.concurrent.*; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | public class TimeBoundPartialMatcher implements PartialMatcher { 25 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeBoundPartialMatcher.class); 26 | 27 | private final PartialMatcher delegate; 28 | private final long timeoutMillis; 29 | 30 | TimeBoundPartialMatcher(PartialMatcher delegate, long timeoutMillis) { 31 | this.delegate = delegate; 32 | this.timeoutMillis = timeoutMillis; 33 | } 34 | 35 | @Override 36 | public void match( 37 | final List allMissingRows, 38 | final List allSurplusRows, 39 | final List matchedColumns) { 40 | LOGGER.debug("Starting partial match"); 41 | ExecutorService executorService = Executors.newSingleThreadExecutor(); 42 | try { 43 | Future result = executorService.submit( 44 | () -> TimeBoundPartialMatcher.this.delegate.match(allMissingRows, allSurplusRows, matchedColumns)); 45 | result.get(this.timeoutMillis, TimeUnit.MILLISECONDS); 46 | LOGGER.debug("Partial match complete"); 47 | } catch (InterruptedException e) { 48 | LOGGER.error("Partial match interrupted", e); 49 | Thread.currentThread().interrupt(); 50 | } catch (ExecutionException e) { 51 | LOGGER.error("Partial match exception", e); 52 | Throwable cause = e.getCause(); 53 | throw cause instanceof RuntimeException ? (RuntimeException) cause : new RuntimeException(cause); 54 | } catch (TimeoutException e) { 55 | LOGGER.error("Partial match timed out"); 56 | throw new RuntimeException(e); 57 | } finally { 58 | executorService.shutdown(); 59 | try { 60 | if (!executorService.awaitTermination(1, TimeUnit.SECONDS)) { 61 | executorService.shutdownNow(); 62 | } 63 | } catch (InterruptedException e) { 64 | executorService.shutdownNow(); 65 | Thread.currentThread().interrupt(); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/ColumnCardinality.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import java.io.Serializable; 20 | import java.util.Comparator; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | import java.util.function.BiConsumer; 24 | import java.util.function.Function; 25 | 26 | class ColumnCardinality implements Serializable { 27 | private final int maximumCardinalityToCount; 28 | private Map bag = new HashMap<>(); 29 | 30 | ColumnCardinality(int maximumCardinalityToCount) { 31 | this.maximumCardinalityToCount = maximumCardinalityToCount; 32 | } 33 | 34 | int getDistinctCount() { 35 | if (this.isFull()) { 36 | return this.maximumCardinalityToCount; 37 | } 38 | return this.bag.size(); 39 | } 40 | 41 | void merge(ColumnCardinality that) { 42 | if (that.isFull()) { 43 | this.setFull(); 44 | } else { 45 | that.bag.forEach(ColumnCardinality.this::addOccurrences); 46 | } 47 | } 48 | 49 | void addOccurrence(Object value) { 50 | this.addOccurrences(value, 1); 51 | } 52 | 53 | void removeOccurrence(Object value) { 54 | if (!this.isFull()) { 55 | Integer integer = this.bag.get(value); 56 | if (integer != null) { 57 | if (integer > 1) { 58 | this.bag.put(value, integer - 1); 59 | } else { 60 | this.bag.remove(value); 61 | } 62 | } 63 | } 64 | } 65 | 66 | void forEachWithOccurrences(final BiConsumer procedure) { 67 | this.bag.entrySet().stream() 68 | .sorted(Comparator.comparing((Function, Integer>) Map.Entry::getValue) 69 | .reversed()) 70 | .limit(this.maximumCardinalityToCount) 71 | .forEach(objectIntegerEntry -> 72 | procedure.accept(objectIntegerEntry.getKey(), objectIntegerEntry.getValue())); 73 | } 74 | 75 | boolean isFull() { 76 | return this.bag == null; 77 | } 78 | 79 | private void addOccurrences(Object value, int occurrences) { 80 | if (!this.isFull()) { 81 | Integer cardinality = this.bag.get(value); 82 | if (cardinality != null || this.bag.size() < this.maximumCardinalityToCount) { 83 | this.bag.put(value, (cardinality == null ? 0 : cardinality) + occurrences); 84 | } else { 85 | this.setFull(); 86 | } 87 | } 88 | } 89 | 90 | private void setFull() { 91 | this.bag = null; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tablasco-core/src/test/java/com/gs/tablasco/core/HtmlConfigTest.java: -------------------------------------------------------------------------------- 1 | package com.gs.tablasco.core; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class HtmlConfigTest { 8 | 9 | @Test 10 | void testDefaultConfiguration() { 11 | HtmlConfig config = new HtmlConfig(); 12 | 13 | assertFalse(config.isHideMatchedRows()); 14 | assertFalse(config.isHideMatchedTables()); 15 | assertFalse(config.isHideMatchedColumns()); 16 | assertFalse(config.isShowAssertionSummary()); 17 | assertFalse(config.isSummarizedResults()); 18 | assertTrue(config.getTablesToAlwaysShowMatchedRowsFor().isEmpty()); 19 | assertEquals(10000, config.getHtmlRowLimit()); 20 | } 21 | 22 | @Test 23 | void testHideMatchedRows() { 24 | HtmlConfig config = new HtmlConfig().withHideMatchedRows(true); 25 | 26 | assertTrue(config.isHideMatchedRows()); 27 | } 28 | 29 | @Test 30 | void testHideMatchedColumns() { 31 | HtmlConfig config = new HtmlConfig().withHideMatchedColumns(true); 32 | 33 | assertTrue(config.isHideMatchedColumns()); 34 | } 35 | 36 | @Test 37 | void testHideMatchedTables() { 38 | HtmlConfig config = new HtmlConfig().withHideMatchedTables(true); 39 | 40 | assertTrue(config.isHideMatchedTables()); 41 | } 42 | 43 | @Test 44 | void testHtmlRowLimit() { 45 | int limit = 5000; 46 | HtmlConfig config = new HtmlConfig().withHtmlRowLimit(limit); 47 | 48 | assertEquals(limit, config.getHtmlRowLimit()); 49 | } 50 | 51 | @Test 52 | void testAssertionSummary() { 53 | HtmlConfig config = new HtmlConfig().withAssertionSummary(true); 54 | 55 | assertTrue(config.isShowAssertionSummary()); 56 | } 57 | 58 | @Test 59 | void testSummarizedResults() { 60 | HtmlConfig config = new HtmlConfig().withSummarizedResults(true); 61 | 62 | assertTrue(config.isSummarizedResults()); 63 | } 64 | 65 | @Test 66 | void testAlwaysShowMatchedRowsFor() { 67 | String[] tables = {"table1", "table2"}; 68 | HtmlConfig config = new HtmlConfig().withAlwaysShowMatchedRowsFor(tables); 69 | 70 | assertEquals(2, config.getTablesToAlwaysShowMatchedRowsFor().size()); 71 | assertTrue(config.getTablesToAlwaysShowMatchedRowsFor().contains("table1")); 72 | assertTrue(config.getTablesToAlwaysShowMatchedRowsFor().contains("table2")); 73 | } 74 | 75 | @Test 76 | void testMethodChaining() { 77 | HtmlConfig config = new HtmlConfig() 78 | .withHideMatchedRows(true) 79 | .withHideMatchedColumns(true) 80 | .withHideMatchedTables(true) 81 | .withHtmlRowLimit(5000) 82 | .withAssertionSummary(true) 83 | .withSummarizedResults(true) 84 | .withAlwaysShowMatchedRowsFor("table1"); 85 | 86 | assertTrue(config.isHideMatchedRows()); 87 | assertTrue(config.isHideMatchedColumns()); 88 | assertTrue(config.isHideMatchedTables()); 89 | assertEquals(5000, config.getHtmlRowLimit()); 90 | assertTrue(config.isShowAssertionSummary()); 91 | assertTrue(config.isSummarizedResults()); 92 | assertEquals(1, config.getTablesToAlwaysShowMatchedRowsFor().size()); 93 | assertTrue(config.getTablesToAlwaysShowMatchedRowsFor().contains("table1")); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/verify/indexmap/PartialMatcherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertNull; 20 | import static org.junit.jupiter.api.Assertions.assertSame; 21 | 22 | import com.gs.tablasco.TestTable; 23 | import com.gs.tablasco.VerifiableTable; 24 | import com.gs.tablasco.verify.ColumnComparators; 25 | import java.util.ArrayList; 26 | import java.util.Arrays; 27 | import java.util.List; 28 | import org.junit.jupiter.api.BeforeEach; 29 | import org.junit.jupiter.api.Test; 30 | 31 | class PartialMatcherTest { 32 | private static final VerifiableTable MISSING = new TestTable( 33 | "Entity", "Null-1", "Null-2", "Account", "Net", "MV", "Quantity") 34 | .withRow("GSIL", null, "", "71000", 100.0, 1000.0, 10.0) 35 | .withRow("GSCO", null, "", "91001", 500.0, 5000.0, 50.0) 36 | .withRow("GSCO", null, "", "91001", 500.0, 5000.0, 58.0); 37 | private static final VerifiableTable SURPLUS = new TestTable( 38 | "Entity", "Null-1", "Null-2", "Account", "Net", "MV", "Quantity") 39 | .withRow("GSIL", "", null, "71000", 100.0, 9000.0, 90.0) 40 | .withRow("GSIL", "", null, "71000", 100.0, 9000.0, 10.0) 41 | .withRow("GSCO", "", "", "91001", 505.0, 5064.0, 58.0) 42 | .withRow("GSCO", "", null, "91001", 500.0, 5064.0, 58.0); 43 | private static final List COLUMNS = Arrays.asList( 44 | new IndexMap(0, 0), 45 | new IndexMap(1, 1), 46 | new IndexMap(2, 2), 47 | new IndexMap(3, 3), 48 | new IndexMap(4, 4), 49 | new IndexMap(5, 5), 50 | new IndexMap(6, 6)); 51 | 52 | private List missing; 53 | private List surplus; 54 | 55 | @BeforeEach 56 | void setUp() { 57 | this.missing = new ArrayList<>(); 58 | for (int i = 0; i < MISSING.getRowCount(); i++) { 59 | this.missing.add(new UnmatchedIndexMap(i, -1)); 60 | } 61 | this.surplus = new ArrayList<>(); 62 | for (int i = 0; i < SURPLUS.getRowCount(); i++) { 63 | this.surplus.add(new UnmatchedIndexMap(-1, i)); 64 | } 65 | } 66 | 67 | @Test 68 | void bestMatchPartialMatcher() { 69 | new BestMatchPartialMatcher(SURPLUS, MISSING, new ColumnComparators.Builder().build()) 70 | .match(this.missing, this.surplus, COLUMNS); 71 | assertNull(this.surplus.get(0).getBestMutualMatch()); 72 | assertSame(this.missing.get(0), this.surplus.get(1).getBestMutualMatch()); 73 | assertSame(this.missing.get(1), this.surplus.get(2).getBestMutualMatch()); 74 | assertSame(this.missing.get(2), this.surplus.get(3).getBestMutualMatch()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /CONTRIBUTOR_COVENANT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at gs-oss-abuse@gs.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/verify/CellFormatterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | import java.sql.Timestamp; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class CellFormatterTest { 25 | @Test 26 | void formatNumbers() { 27 | CellFormatter formatter = new CellFormatter(0.01, true); 28 | assertEquals("1", formatter.format(1.0d)); 29 | assertEquals("1.1", formatter.format(1.10d)); 30 | assertEquals("1.11", formatter.format(1.11d)); 31 | assertEquals("1.11", formatter.format(1.111d)); 32 | assertEquals("1.12", formatter.format(1.116d)); 33 | assertEquals("-1.12", formatter.format(-1.116d)); 34 | assertEquals("-1,000.12", formatter.format(-1000.116d)); 35 | 36 | assertEquals("-1,000.12", formatter.format(-1000.116f)); 37 | assertEquals("1,000", formatter.format(1000)); 38 | assertEquals("-1,000", formatter.format((long) -1000)); 39 | 40 | assertEquals("NaN", formatter.format(Double.NaN)); 41 | assertEquals("NaN", formatter.format(Float.NaN)); 42 | 43 | assertEquals("0", formatter.format(-0.0d)); 44 | assertEquals("0", formatter.format(-0)); 45 | assertEquals("0", formatter.format(-0.0001)); 46 | } 47 | 48 | @Test 49 | void formatNegativeZero() { 50 | CellFormatter formatter = new CellFormatter(0.0001, true); 51 | assertEquals("0", formatter.format(-0.0d)); 52 | assertEquals("0", formatter.format(-0)); 53 | assertEquals("0", formatter.format(-0.00001)); 54 | assertEquals("-0.0001", formatter.format(-0.0001)); 55 | 56 | assertFalse(CellFormatter.isNegativeZero("-01")); 57 | assertTrue(CellFormatter.isNegativeZero("-0.000000")); 58 | } 59 | 60 | @Test 61 | void formatDate() { 62 | CellFormatter formatter = new CellFormatter(0, false); 63 | assertEquals("2009-02-13 23:31:30", formatter.format(Timestamp.valueOf("2009-02-13 23:31:30.0001"))); 64 | } 65 | 66 | @Test 67 | void formatString() { 68 | CellFormatter formatter = new CellFormatter(0, false); 69 | assertEquals("", formatter.format("")); 70 | assertEquals("foo", formatter.format("foo")); 71 | assertEquals("foo", formatter.format(" foo ")); 72 | assertEquals("foo bar", formatter.format("foo bar")); 73 | assertEquals("foo bar", formatter.format("foo bar")); 74 | assertEquals("foo bar", formatter.format("foo\nbar ")); 75 | assertEquals("foo bar", formatter.format(" foo\rbar")); 76 | assertEquals("foo bar", formatter.format(" foo\r \nbar ")); 77 | assertEquals("foo", formatter.format("foo\r")); 78 | assertEquals("foo", formatter.format("\n foo")); 79 | assertEquals("", formatter.format("\n \r ")); 80 | assertEquals("", formatter.format(" \n \r")); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/results/parser/ExpectedResultsParserTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.results.parser; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | import com.gs.tablasco.TableTestUtils; 22 | import com.gs.tablasco.VerifiableTable; 23 | import com.gs.tablasco.results.ExpectedResults; 24 | import com.gs.tablasco.results.FileSystemExpectedResultsLoader; 25 | import java.io.File; 26 | import java.util.Collections; 27 | import java.util.IdentityHashMap; 28 | import java.util.Map; 29 | import org.junit.jupiter.api.Test; 30 | 31 | class ExpectedResultsParserTest { 32 | @Test 33 | void testParse() { 34 | File expected = new File( 35 | TableTestUtils.getExpectedDirectory(), ExpectedResultsParserTest.class.getSimpleName() + ".txt"); 36 | 37 | ExpectedResults results = new ExpectedResultsParser(new FileSystemExpectedResultsLoader(), expected).parse(); 38 | 39 | VerifiableTable summary = results.getTable("Summary"); 40 | assertEquals(6, summary.getColumnCount()); 41 | assertEquals(5, summary.getRowCount()); 42 | 43 | VerifiableTable drillDown = results.getTable("DrillDown"); 44 | assertEquals(6, drillDown.getColumnCount()); 45 | assertEquals(1, drillDown.getRowCount()); 46 | 47 | assertEquals(2, results.getMetadata().getData().size()); 48 | assertEquals( 49 | Collections.singletonMap("Recorded At", "2013-06-26 12:00:00") 50 | .entrySet() 51 | .iterator() 52 | .next(), 53 | results.getMetadata().getData().get(0)); 54 | assertEquals( 55 | Collections.singletonMap("App Server URL", "http://test") 56 | .entrySet() 57 | .iterator() 58 | .next(), 59 | results.getMetadata().getData().get(1)); 60 | } 61 | 62 | @Test 63 | void testCache() { 64 | File expected = new File( 65 | TableTestUtils.getExpectedDirectory(), ExpectedResultsParserTest.class.getSimpleName() + ".txt"); 66 | Map results = new IdentityHashMap<>(); 67 | for (int i = 0; i < 10; i++) { 68 | results.put(ExpectedResultsCache.getExpectedResults(new FileSystemExpectedResultsLoader(), expected), ""); 69 | } 70 | assertTrue(results.size() < 10, "cache was hit at least once"); 71 | } 72 | 73 | @Test 74 | void testMissingExpectedResultsFileResultsInClearErrorMessage() { 75 | String missingFileName = "missing-expected-results.txt"; 76 | try { 77 | new ExpectedResultsParser(new FileSystemExpectedResultsLoader(), new File(missingFileName)).parse(); 78 | fail("Should have failed looking for non-existent file"); 79 | } catch (IllegalStateException expected) { 80 | assertTrue(expected.getMessage().contains(missingFileName)); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/FileAndDirectoryStrategyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertTrue; 20 | 21 | import com.gs.tablasco.files.DirectoryStrategy; 22 | import com.gs.tablasco.files.FilePerClassStrategy; 23 | import java.io.File; 24 | import java.nio.file.Files; 25 | import org.junit.jupiter.api.BeforeEach; 26 | import org.junit.jupiter.api.Test; 27 | import org.junit.jupiter.api.extension.RegisterExtension; 28 | 29 | public class FileAndDirectoryStrategyTest { 30 | @RegisterExtension 31 | private final TableVerifier verifier = new TableVerifier() 32 | .withDirectoryStrategy(new DirectoryStrategy() { 33 | @Override 34 | public File getExpectedDirectory(Class testClass) { 35 | return TableTestUtils.getExpectedDirectory(); 36 | } 37 | 38 | @Override 39 | public File getOutputDirectory(Class testClass) { 40 | return TableTestUtils.getOutputDirectory(); 41 | } 42 | 43 | @Override 44 | public File getActualDirectory(Class testClass) { 45 | return new File(TableTestUtils.getOutputDirectory(), "actual"); 46 | } 47 | }) 48 | .withFileStrategy(new FilePerClassStrategy() { 49 | @Override 50 | public String getExpectedFilename(Class testClass, String methodName) { 51 | return "Custom" + super.getExpectedFilename(testClass, methodName); 52 | } 53 | 54 | @Override 55 | public String getOutputFilename(Class testClass, String methodName) { 56 | return "Custom" + super.getOutputFilename(testClass, methodName); 57 | } 58 | 59 | @Override 60 | public String getActualFilename(Class testClass, String methodName) { 61 | return "Custom" + super.getActualFilename(testClass, methodName); 62 | } 63 | }); 64 | 65 | @RegisterExtension 66 | public final TableTestUtils.TestExtensionContext extensionContext = new TableTestUtils.TestExtensionContext(); 67 | 68 | @BeforeEach 69 | void setUp() throws Exception { 70 | this.verifier.beforeEach(this.extensionContext.get()); 71 | Files.deleteIfExists(this.verifier.getActualFile().toPath()); 72 | } 73 | 74 | @Test 75 | void testFiles() { 76 | this.verifier.verify(TableTestUtils.TABLE_NAME, TableTestUtils.ACTUAL); 77 | this.verifier.afterEach(this.extensionContext.get()); 78 | assertTrue(new File( 79 | new File(TableTestUtils.getOutputDirectory().getPath(), "actual"), 80 | "CustomFileAndDirectoryStrategyTest.txt") 81 | .exists()); 82 | assertTrue(new File(TableTestUtils.getOutputDirectory().getPath(), "CustomFileAndDirectoryStrategyTest.html") 83 | .exists()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/adapters/TableAdaptersTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.adapters; 18 | 19 | import com.gs.tablasco.TableTestUtils; 20 | import com.gs.tablasco.TableVerifier; 21 | import com.gs.tablasco.VerifiableTable; 22 | import org.junit.jupiter.api.Test; 23 | import org.junit.jupiter.api.extension.RegisterExtension; 24 | 25 | public class TableAdaptersTest { 26 | 27 | @RegisterExtension 28 | private final TableVerifier verifier = new TableVerifier() 29 | .withExpectedDir(TableTestUtils.getExpectedDirectory()) 30 | .withOutputDir(TableTestUtils.getOutputDirectory()) 31 | .withFilePerClass() 32 | .withHideMatchedRows(true) 33 | .withHideMatchedTables(true); 34 | 35 | @Test 36 | void testAllRows() { 37 | this.verify( 38 | TableTestUtils.createTable(1, "C", 1, 2, 3, 4, 5), 39 | TableAdapters.withRows(TableTestUtils.createTable(1, "C", 1, 2, 3, 4, 5), i -> true)); 40 | } 41 | 42 | @Test 43 | void testNoRows() { 44 | VerifiableTable table = TableTestUtils.createTable(1, "C", 1, 2, 3, 4, 5); 45 | this.verify(TableTestUtils.createTable(1, "C"), TableAdapters.withRows(table, i -> false)); 46 | } 47 | 48 | @Test 49 | void testSomeRows() { 50 | VerifiableTable table = TableTestUtils.createTable(1, "C", 1, 2, 3, 4, 5); 51 | this.verify( 52 | TableTestUtils.createTable(1, "C", 2, 4), 53 | TableAdapters.withRows(table, i -> (Integer) table.getValueAt(i, 0) % 2 == 0)); 54 | } 55 | 56 | @Test 57 | void testAllColumns() { 58 | this.verify( 59 | TableTestUtils.createTable(5, "C1", "C2", "C3", "C4", "C5"), 60 | TableAdapters.withColumns(TableTestUtils.createTable(5, "C1", "C2", "C3", "C4", "C5"), name -> true)); 61 | } 62 | 63 | @Test 64 | void testSomeColumns() { 65 | this.verify( 66 | TableTestUtils.createTable(3, "C1", "C3", "C5"), 67 | TableAdapters.withColumns( 68 | TableTestUtils.createTable(5, "C1", "C2", "C3", "C4", "C5"), name -> name.matches("C[135]"))); 69 | } 70 | 71 | @Test 72 | void composition1() { 73 | VerifiableTable table = TableTestUtils.createTable(2, "C1", "C2", 1, 2, 3, 4); 74 | VerifiableTable rowFilter = 75 | TableAdapters.withRows(TableAdapters.withColumns(table, name -> name.equals("C2")), i -> i > 0); 76 | this.verify(TableTestUtils.createTable(1, "C2", 4), rowFilter); 77 | } 78 | 79 | @Test 80 | void composition2() { 81 | VerifiableTable table = TableTestUtils.createTable(2, "C1", "C2", 1, 2, 3, 4); 82 | VerifiableTable columnFilter = 83 | TableAdapters.withColumns(TableAdapters.withRows(table, i -> i > 0), name -> name.equals("C2")); 84 | this.verify(TableTestUtils.createTable(1, "C2", 4), columnFilter); 85 | } 86 | 87 | private void verify(VerifiableTable expected, VerifiableTable adaptedActual) { 88 | this.verifier.verify("table", adaptedActual, expected); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tablasco 2 | 3 | ## What is it? 4 | Tablasco is a snapshot testing utility for tabular datasets. It was initially developed to verify financial reports, but 5 | can be used to make assertions on any data that can be presented in a tabular format. 6 | 7 | Tablasco now supports JUnit 5 and Java 11+. Please use version 2.5.0 if you still need Tablasco for JUnit 4 or Java 8. 8 | 9 | ### 1. Fast and efficient table verification algorithm 10 | Tablasco's table verification algorithm can process tens of thousands of table rows out-of-the-box, and many more with 11 | custom configuration. Verification can be configured to control behavior such as verifying row order, ignoring headers 12 | and applying tolerance to floating-point values. 13 | 14 | ### 2. Human readable HTML break reports 15 | Each test produces a color-coded HTML report showing how the actual and expected tables differed helping developers 16 | quickly identify problems. Output can be configured to support large datasets by hiding matched rows and collapsing 17 | similar breaks. 18 | 19 | ### 3. Automatic baseline management 20 | Each Tablasco test is backed by a text file containing expected results; this _baseline_ file is normally saved with the 21 | code in version control. If a test fails because of a change that caused an expected difference the test can be 22 | _rebased_ to update the text file with the new results. 23 | 24 | ## Usage 25 | The two most important Tablasco classes to be familiar with are `TableVerifier` and `VerifiableTable`. 26 | 27 | ### TableVerifier 28 | `TableVerifier` is a [JUnit 5 Extension](https://docs.junit.org/current/user-guide/#extensions) that needs to be defined 29 | in each Tablasco test class: 30 | ``` 31 | @RegisterExtension 32 | public final TableVerifier tableVerifier = new TableVerifier() 33 | .withExpectedDir("src/test/resources") 34 | .withOutputDir("target"); 35 | ``` 36 | JUnit, by design, ensures that each test method has its own instance of `TableVerifier` and Tablasco takes advantage of 37 | this to offer a fluent API that allows configuration to cascade from the project, to the class, to the test level. To 38 | set project level configuration you can wrap the initialisation code above in a factory method: 39 | ``` 40 | @RegisterExtension 41 | public final TableVerifier tableVerifier = 42 | MyTableVerifierFactory.newTableVerifier(); 43 | ``` 44 | 45 | Configuration that you want to apply at a class level can be set when the field is initialised: 46 | ``` 47 | @RegisterExtension 48 | public final TableVerifier tableVerifier = 49 | MyTableVerifierFactory.newTableVerifier().withHideMatchedRows(true); 50 | ``` 51 | 52 | And finally, configuration required at the test level can be applied inline: 53 | ``` 54 | @Test 55 | public void myTest() 56 | { 57 | this.tableVerifier.withTolerance(0.1d).verify(tables); 58 | } 59 | ``` 60 | 61 | ### VerifiableTable 62 | The [`VerifiableTable`](https://github.com/goldmansachs/tablasco/blob/master/src/main/java/com/gs/tablasco/VerifiableTable.java) 63 | interface is an abstract definition of a table that Tablasco understands. Tests need to adapt the data they are verifying 64 | to an instance of `VerifiableTable`. These adapters are typically shared across tests or even across applications. 65 | 66 | ### Example Test 67 | `TableVerifier` compares instances of `VerifiableTable` with the baseline results file; if the tables match the test 68 | passes, otherwise the test fails. 69 | 70 | ``` 71 | @Test 72 | public void tableToBaselineTest() 73 | { 74 | VerifiableTable table = new MyResultsVerifiableTable(getResults()); 75 | this.tableVerifier.verify("results", table); 76 | } 77 | ``` 78 | 79 | ### Rebasing 80 | To rebase a test or suite of tests set the system property `rebase=true` or configure the verifier directly using 81 | `.withRebase()`. As a precaution to prevent developers accidentally leaving rebase switched on, rebasing tests always 82 | fail. 83 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/HtmlRowLimitTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco; 18 | 19 | import de.skuzzle.test.snapshots.Snapshot; 20 | import de.skuzzle.test.snapshots.junit5.EnableSnapshotTests; 21 | import java.io.IOException; 22 | import org.junit.jupiter.api.Test; 23 | import org.junit.jupiter.api.extension.RegisterExtension; 24 | 25 | @EnableSnapshotTests 26 | public class HtmlRowLimitTest { 27 | 28 | @RegisterExtension 29 | private final TableVerifier tableVerifier = 30 | new TableVerifier().withFilePerMethod().withMavenDirectoryStrategy().withHtmlRowLimit(3); 31 | 32 | @Test 33 | void tablesMatch(Snapshot snapshot) throws IOException { 34 | VerifiableTable table = TableTestUtils.createTable( 35 | 2, "Col 1", "Col 2", "A1", "A2", "B1", "B2", "C1", "C2", "D1", "D2", "E1", "E2"); 36 | this.tableVerifier.verify("name", table, table); 37 | TableTestUtils.getHtml(this.tableVerifier); 38 | snapshot.assertThat(TableTestUtils.getHtml(this.tableVerifier)).asText().matchesSnapshotText(); 39 | } 40 | 41 | @Test 42 | void tablesDoNotMatch(Snapshot snapshot) throws IOException { 43 | final VerifiableTable table1 = TableTestUtils.createTable( 44 | 2, "Col 1", "Col 2", "A1", "A2", "B1", "B2", "C1", "C2", "D1", "D2", "E1", "E2"); 45 | final VerifiableTable table2 = TableTestUtils.createTable( 46 | 2, "Col 1", "Col 2", "A1", "A2", "B1", "B2", "C1", "C2", "D1", "DX", "E1", "E2"); 47 | TableTestUtils.assertAssertionError(() -> tableVerifier.verify("name", table1, table2)); 48 | TableTestUtils.getHtml(this.tableVerifier); 49 | snapshot.assertThat(TableTestUtils.getHtml(this.tableVerifier)).asText().matchesSnapshotText(); 50 | } 51 | 52 | @Test 53 | void hideMatchedRows(Snapshot snapshot) throws IOException { 54 | final VerifiableTable table1 = TableTestUtils.createTable( 55 | 2, "Col 1", "Col 2", "A1", "A2", "B1", "B2", "C1", "C2", "D1", "D2", "E1", "E2"); 56 | final VerifiableTable table2 = TableTestUtils.createTable( 57 | 2, "Col 1", "Col 2", "A1", "AX", "B1", "B2", "C1", "C2", "D1", "DX", "E1", "E2"); 58 | TableTestUtils.assertAssertionError( 59 | () -> tableVerifier.withHideMatchedRows(true).verify("name", table1, table2)); 60 | TableTestUtils.getHtml(this.tableVerifier); 61 | snapshot.assertThat(TableTestUtils.getHtml(this.tableVerifier)).asText().matchesSnapshotText(); 62 | } 63 | 64 | @Test 65 | void hideMatchedRows2(Snapshot snapshot) throws IOException { 66 | final VerifiableTable table1 = 67 | TableTestUtils.createTable(2, "Col 1", "Col 2", "A1", "A2", "B1", "B2", "C1", "C2", "D1", "D2"); 68 | final VerifiableTable table2 = 69 | TableTestUtils.createTable(2, "Col 1", "Col 2", "A1", "A2", "B1", "B2", "C1", "CX", "D1", "DX"); 70 | TableTestUtils.assertAssertionError(() -> 71 | tableVerifier.withHtmlRowLimit(1).withHideMatchedRows(true).verify("name", table1, table2)); 72 | TableTestUtils.getHtml(this.tableVerifier); 73 | snapshot.assertThat(TableTestUtils.getHtml(this.tableVerifier)).asText().matchesSnapshotText(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tablasco-core/src/test/java/com/gs/tablasco/core/VerifierConfigTest.java: -------------------------------------------------------------------------------- 1 | package com.gs.tablasco.core; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import com.gs.tablasco.VerifiableTable; 6 | import java.util.function.Function; 7 | import java.util.function.Predicate; 8 | import org.junit.jupiter.api.Test; 9 | 10 | class VerifierConfigTest { 11 | 12 | @Test 13 | void testDefaultConfiguration() { 14 | VerifierConfig config = new VerifierConfig(); 15 | 16 | assertTrue(config.isVerifyRowOrder()); 17 | assertFalse(config.isIgnoreSurplusRows()); 18 | assertFalse(config.isIgnoreMissingRows()); 19 | assertFalse(config.isIgnoreSurplusColumns()); 20 | assertFalse(config.isIgnoreMissingColumns()); 21 | assertEquals(300000L, config.getPartialMatchTimeoutMillis()); // Default timeout 22 | } 23 | 24 | @Test 25 | void testRowOrderConfiguration() { 26 | VerifierConfig config = new VerifierConfig().withVerifyRowOrder(false); 27 | 28 | assertFalse(config.isVerifyRowOrder()); 29 | } 30 | 31 | @Test 32 | void testIgnoreRowsConfiguration() { 33 | VerifierConfig config = new VerifierConfig().withIgnoreSurplusRows().withIgnoreMissingRows(); 34 | 35 | assertTrue(config.isIgnoreSurplusRows()); 36 | assertTrue(config.isIgnoreMissingRows()); 37 | } 38 | 39 | @Test 40 | void testIgnoreColumnsConfiguration() { 41 | VerifierConfig config = new VerifierConfig().withIgnoreSurplusColumns().withIgnoreMissingColumns(); 42 | 43 | assertTrue(config.isIgnoreSurplusColumns()); 44 | assertTrue(config.isIgnoreMissingColumns()); 45 | } 46 | 47 | @Test 48 | void testPartialMatchTimeout() { 49 | long timeout = 5000L; 50 | VerifierConfig config = new VerifierConfig().withPartialMatchTimeoutMillis(timeout); 51 | 52 | assertEquals(timeout, config.getPartialMatchTimeoutMillis()); 53 | } 54 | 55 | @Test 56 | void testNoPartialMatchTimeout() { 57 | VerifierConfig config = new VerifierConfig().withoutPartialMatchTimeout(); 58 | 59 | assertEquals(0L, config.getPartialMatchTimeoutMillis()); 60 | } 61 | 62 | @Test 63 | void testColumnFilter() { 64 | Predicate columnFilter = column -> column.startsWith("test"); 65 | VerifierConfig config = new VerifierConfig().withColumnFilter(columnFilter); 66 | 67 | assertNotNull(config.getActualAdapter()); 68 | assertNotNull(config.getExpectedAdapter()); 69 | } 70 | 71 | @Test 72 | void testIgnoreSpecificColumns() { 73 | VerifierConfig config = new VerifierConfig().withIgnoreColumns("col1", "col2"); 74 | 75 | assertNotNull(config.getActualAdapter()); 76 | assertNotNull(config.getExpectedAdapter()); 77 | } 78 | 79 | @Test 80 | void testToleranceConfiguration() { 81 | double tolerance = 0.001; 82 | VerifierConfig config = new VerifierConfig().withTolerance(tolerance).withTolerance("column1", tolerance); 83 | 84 | assertNotNull(config.getColumnComparators()); 85 | } 86 | 87 | @Test 88 | void testVarianceThresholdConfiguration() { 89 | double threshold = 0.1; 90 | VerifierConfig config = 91 | new VerifierConfig().withVarianceThreshold(threshold).withVarianceThreshold("column1", threshold); 92 | 93 | assertNotNull(config.getColumnComparators()); 94 | } 95 | 96 | @Test 97 | void testCustomAdapters() { 98 | Function adapter = table -> table; 99 | VerifierConfig config = new VerifierConfig().withActualAdapter(adapter).withExpectedAdapter(adapter); 100 | 101 | assertSame(adapter, config.getActualAdapter()); 102 | assertSame(adapter, config.getExpectedAdapter()); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/MultiTableVerifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.core.VerifierConfig; 21 | import com.gs.tablasco.verify.indexmap.IndexMapTableVerifier; 22 | import java.util.ArrayList; 23 | import java.util.LinkedHashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | 27 | /** 28 | * Verifies that a set of grids (in form of VerifiedCell) matches expected results. Heavy lifting is delegated to 29 | * SingleTableVerification class: this one is mainly about pulling the pieces together. 30 | */ 31 | // todo: kp: test whether if i rename test methods I get expected 'surplus' tables error, is that confusing? 32 | 33 | public class MultiTableVerifier { 34 | private final VerifierConfig verifierConfig; 35 | 36 | public MultiTableVerifier(VerifierConfig verifierConfig) { 37 | this.verifierConfig = verifierConfig; 38 | } 39 | 40 | public Map verifyTables( 41 | Map expectedResults, 42 | Map actualResults) { 43 | SingleTableVerifier singleTableVerifier = new IndexMapTableVerifier( 44 | this.verifierConfig.getColumnComparators(), 45 | this.verifierConfig.isVerifyRowOrder(), 46 | IndexMapTableVerifier.DEFAULT_BEST_MATCH_THRESHOLD, 47 | this.verifierConfig.isIgnoreSurplusRows(), 48 | this.verifierConfig.isIgnoreMissingRows(), 49 | this.verifierConfig.isIgnoreSurplusColumns(), 50 | this.verifierConfig.isIgnoreMissingColumns(), 51 | this.verifierConfig.getPartialMatchTimeoutMillis()); 52 | Map results = new LinkedHashMap<>(); 53 | List allTableNames = new ArrayList<>(expectedResults.keySet()); 54 | for (String actualTable : actualResults.keySet()) { 55 | if (!expectedResults.containsKey(actualTable)) { 56 | allTableNames.add(actualTable); 57 | } 58 | } 59 | for (String tableName : allTableNames) { 60 | verifyTable(tableName, actualResults, expectedResults, results, singleTableVerifier); 61 | } 62 | return results; 63 | } 64 | 65 | private void verifyTable( 66 | String tableName, 67 | Map actualResults, 68 | Map expectedResults, 69 | Map resultsMap, 70 | SingleTableVerifier singleTableVerifier) { 71 | VerifiableTable actualData = actualResults.get(tableName); 72 | VerifiableTable expectedData = expectedResults.get(tableName); 73 | 74 | if (actualData != null && actualData.getColumnCount() == 0) { 75 | throw new IllegalStateException("Actual table '" + tableName + "' has no columns"); 76 | } 77 | if (expectedData != null && expectedData.getColumnCount() == 0) { 78 | throw new IllegalStateException("Expected table '" + tableName + "' has no columns"); 79 | } 80 | ResultTable results = singleTableVerifier.verify(actualData, expectedData); 81 | resultsMap.put(tableName, results); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tablasco-junit/src/test/java/com/gs/tablasco/verify/SummaryResultTableTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | import static org.junit.jupiter.api.Assertions.assertFalse; 21 | 22 | import java.io.*; 23 | import java.util.Arrays; 24 | import java.util.List; 25 | import org.junit.jupiter.api.Test; 26 | 27 | class SummaryResultTableTest { 28 | @Test 29 | void isSerializable() throws IOException, ClassNotFoundException { 30 | CellComparator cellComparator = new ToleranceCellComparator(new CellFormatter(1.0, false)); 31 | List row = Arrays.asList( 32 | ResultCell.createMatchedCell(cellComparator, "A", "A"), 33 | ResultCell.createMatchedCell(cellComparator, "A", "B"), 34 | ResultCell.createMissingCell(cellComparator.getFormatter(), "A"), 35 | ResultCell.createSurplusCell(cellComparator.getFormatter(), "A"), 36 | ResultCell.createOutOfOrderCell(cellComparator.getFormatter(), "A")); 37 | SummaryResultTable table = new SummaryResultTable(new ResultTable(new boolean[5], Arrays.asList(row, row))); 38 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 39 | try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(out)) { 40 | objectOutputStream.writeObject(table); 41 | } 42 | try (ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()))) { 43 | SummaryResultTable tableOverWire = (SummaryResultTable) objectInputStream.readObject(); 44 | assertFalse(tableOverWire.isSuccess()); 45 | assertEquals(10, tableOverWire.getTotalCellCount()); 46 | } 47 | } 48 | 49 | @Test 50 | void merge() { 51 | CellComparator cellComparator = new ToleranceCellComparator(new CellFormatter(1.0, false)); 52 | SummaryResultTable table1 = new SummaryResultTable(new ResultTable( 53 | new boolean[2], 54 | Arrays.asList( 55 | List.of(ResultCell.createMatchedCell(cellComparator, "Key", "Val")), 56 | List.of(ResultCell.createMatchedCell(cellComparator, "A", "A"))))); 57 | SummaryResultTable table2 = new SummaryResultTable(new ResultTable( 58 | new boolean[2], 59 | Arrays.asList( 60 | List.of(ResultCell.createMatchedCell(cellComparator, "Key", "Val")), 61 | List.of(ResultCell.createMatchedCell(cellComparator, "A", "B"))))); 62 | SummaryResultTable table3 = new SummaryResultTable(new ResultTable( 63 | new boolean[2], 64 | Arrays.asList( 65 | List.of(ResultCell.createMatchedCell(cellComparator, "Key", "Val")), 66 | List.of(ResultCell.createMatchedCell(cellComparator, "A", "B"))))); 67 | table1.merge(table2); 68 | table1.merge(table3); 69 | assertEquals("{0={firstFew=1, totalRows=1}, 31={firstFew=2, totalRows=2}}", asString(table1)); 70 | assertEquals("{31={firstFew=1, totalRows=1}}", asString(table2)); 71 | assertEquals("{31={firstFew=1, totalRows=1}}", asString(table3)); 72 | } 73 | 74 | private String asString(SummaryResultTable table) { 75 | return table.getResultsByKey().toString(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/ListVerifiableTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import java.util.ArrayList; 21 | import java.util.Iterator; 22 | import java.util.List; 23 | import java.util.function.Consumer; 24 | 25 | @SuppressWarnings({"rawtypes", "unchecked"}) 26 | public class ListVerifiableTable implements VerifiableTable { 27 | private final List headers; 28 | private final List> data; 29 | 30 | public ListVerifiableTable(List> headersAndData) { 31 | this(headersAndData.get(0), headersAndData.subList(1, headersAndData.size())); 32 | } 33 | 34 | public ListVerifiableTable(List headers, List> data) { 35 | this.headers = headers; 36 | this.data = data; 37 | } 38 | 39 | /** 40 | * Creates a {@link VerifiableTable} from an iterable containing headers and rows as lists. The 41 | * first must be the list of headers as strings, the remaining items are rows as lists of objects. The size of each 42 | * row must match the number of headers. 43 | * 44 | * @param headersAndRows iterable of headers and rows 45 | * @return verifiable table 46 | */ 47 | public static VerifiableTable create(Iterable headersAndRows) { 48 | Iterator iterator = headersAndRows.iterator(); 49 | List headers = iterator.next(); 50 | List rowList = new ArrayList(); 51 | iterator.forEachRemaining(verifyRowSize(headers).andThen(rowList::add)); 52 | return new ListVerifiableTable(headers, rowList); 53 | } 54 | 55 | /** 56 | * Creates a {@link VerifiableTable} from a list of headers and an iterable containing rows as 57 | * lists. The size of each row must match the number of headers. 58 | * 59 | * @param headers list of headers 60 | * @param rows iterable rows 61 | * @return the verifiable table 62 | */ 63 | public static VerifiableTable create(List headers, Iterable rows) { 64 | if (rows instanceof List) { 65 | rows.forEach(verifyRowSize(headers)); 66 | return new ListVerifiableTable(headers, (List) rows); 67 | } 68 | List rowList = new ArrayList(); 69 | rows.forEach(verifyRowSize(headers).andThen(rowList::add)); 70 | return new ListVerifiableTable(headers, rowList); 71 | } 72 | 73 | private static Consumer verifyRowSize(final List headers) { 74 | return row -> { 75 | if (row.size() != headers.size()) { 76 | throw new IllegalArgumentException( 77 | String.format("Row size %d does not match header size %s", row.size(), headers.size())); 78 | } 79 | }; 80 | } 81 | 82 | @Override 83 | public int getRowCount() { 84 | return this.data.size(); 85 | } 86 | 87 | @Override 88 | public int getColumnCount() { 89 | return this.headers.size(); 90 | } 91 | 92 | @Override 93 | public String getColumnName(int columnIndex) { 94 | return String.valueOf(this.headers.get(columnIndex)); 95 | } 96 | 97 | @Override 98 | public Object getValueAt(int rowIndex, int columnIndex) { 99 | return this.data.get(rowIndex).get(columnIndex); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/IndexMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | public class IndexMap implements Comparable { 20 | private final int expectedIndex; 21 | private final int actualIndex; 22 | private boolean isOutOfOrder; 23 | 24 | public IndexMap(int expectedIndex, int actualIndex) { 25 | this.expectedIndex = expectedIndex; 26 | this.actualIndex = actualIndex; 27 | this.isOutOfOrder = false; 28 | if (expectedIndex < 0 && actualIndex < 0) { 29 | throw new IllegalStateException("Only one index can be negative: " + this); 30 | } 31 | } 32 | 33 | void setOutOfOrder() { 34 | this.isOutOfOrder = true; 35 | } 36 | 37 | @Override 38 | public boolean equals(Object obj) { 39 | if (obj instanceof IndexMap) { 40 | IndexMap that = (IndexMap) obj; 41 | return this.expectedIndex == that.expectedIndex && this.actualIndex == that.actualIndex; 42 | } 43 | return false; 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | return this.isMissing() ? this.expectedIndex : this.actualIndex; 49 | } 50 | 51 | @Override 52 | public int compareTo(IndexMap that) { 53 | if (this.equals(that)) { 54 | return 0; 55 | } 56 | if (this.isMatched()) { 57 | if (that.actualIndex >= 0) { 58 | return compareUnequals(this.actualIndex, that.actualIndex, this.isSurplus()); 59 | } 60 | return compareUnequals(this.expectedIndex, that.expectedIndex, this.isSurplus()); 61 | } 62 | if (this.isSurplus()) { 63 | if (that.actualIndex >= 0) { 64 | return compareUnequals(this.actualIndex, that.actualIndex, this.isSurplus()); 65 | } 66 | return compareUnequals(this.actualIndex, that.expectedIndex, this.isSurplus()); 67 | } 68 | if (that.expectedIndex >= 0) { 69 | return compareUnequals(this.expectedIndex, that.expectedIndex, this.isSurplus()); 70 | } 71 | return compareUnequals(this.expectedIndex, that.actualIndex, this.isSurplus()); 72 | } 73 | 74 | public boolean isMissing() { 75 | return this.expectedIndex >= 0 && this.actualIndex < 0; 76 | } 77 | 78 | public boolean isSurplus() { 79 | return this.actualIndex >= 0 && this.expectedIndex < 0; 80 | } 81 | 82 | public boolean isMatched() { 83 | return this.actualIndex >= 0 && this.expectedIndex >= 0; 84 | } 85 | 86 | boolean isOutOfOrder() { 87 | return this.isOutOfOrder; 88 | } 89 | 90 | private static int compareUnequals(int thisIndex, int thatIndex, boolean thisIsSurplus) { 91 | if (thisIndex < thatIndex) { 92 | return -1; 93 | } 94 | if (thisIndex > thatIndex) { 95 | return 1; 96 | } 97 | if (thisIsSurplus) { 98 | return -1; 99 | } 100 | return 1; 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return "IndexMap{" + "expectedIndex=" 106 | + expectedIndex + ", actualIndex=" 107 | + actualIndex + ", isOutOfOrder=" 108 | + isOutOfOrder + '}'; 109 | } 110 | 111 | int getExpectedIndex() { 112 | return expectedIndex; 113 | } 114 | 115 | int getActualIndex() { 116 | return actualIndex; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/UnmatchedIndexMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import java.util.Comparator; 20 | import java.util.List; 21 | import java.util.SortedSet; 22 | import java.util.TreeSet; 23 | 24 | public class UnmatchedIndexMap extends IndexMap { 25 | private SortedSet partialMatches; 26 | private UnmatchedIndexMap bestMutualMatch; 27 | 28 | public UnmatchedIndexMap(int expectedIndex, int actualIndex) { 29 | super(expectedIndex, actualIndex); 30 | } 31 | 32 | static void linkBestMatches(List allMissingRows) { 33 | boolean keepMatching = true; 34 | while (keepMatching) { 35 | keepMatching = false; 36 | for (UnmatchedIndexMap expected : allMissingRows) { 37 | keepMatching |= expected.match(); 38 | } 39 | } 40 | } 41 | 42 | public void addMatch(int matchScore, UnmatchedIndexMap match) { 43 | if (this.equals(match)) { 44 | throw new IllegalArgumentException("Cannot add this as partial match"); 45 | } 46 | if (this.partialMatches == null) { 47 | this.partialMatches = new TreeSet<>(); 48 | } 49 | if (match.partialMatches == null) { 50 | match.partialMatches = new TreeSet<>(); 51 | } 52 | if (this.getExpectedIndex() < 0 || match.getActualIndex() < 0) { 53 | throw new IllegalStateException("Expecting this to be expected and that to be actual"); 54 | } 55 | 56 | this.partialMatches.add(new Match(matchScore, match)); 57 | match.partialMatches.add(new Match(matchScore, this)); 58 | } 59 | 60 | public boolean match() { 61 | UnmatchedIndexMap thisBest = this.getBestMatch(); 62 | if (thisBest != null) { 63 | UnmatchedIndexMap thatBest = thisBest.getBestMatch(); 64 | if (this.equals(thatBest)) { 65 | this.bestMutualMatch = thisBest; 66 | this.partialMatches = null; 67 | thisBest.bestMutualMatch = this; 68 | thisBest.partialMatches = null; 69 | return true; 70 | } 71 | } 72 | return false; 73 | } 74 | 75 | private UnmatchedIndexMap getBestMatch() { 76 | if (this.partialMatches != null) { 77 | for (Match match : this.partialMatches) { 78 | if (match.match.bestMutualMatch == null) { 79 | return match.match; 80 | } 81 | } 82 | } 83 | return null; 84 | } 85 | 86 | public UnmatchedIndexMap getBestMutualMatch() { 87 | return this.bestMutualMatch; 88 | } 89 | 90 | private static class Match implements Comparable { 91 | private static final Comparator MATCH_COMPARATOR = Comparator.comparing(Match::getMatchScore) 92 | .reversed() 93 | .thenComparing(match -> Math.max(match.match.getActualIndex(), match.match.getExpectedIndex())); 94 | private final int matchScore; 95 | private final UnmatchedIndexMap match; 96 | 97 | private Match(int matchScore, UnmatchedIndexMap match) { 98 | this.matchScore = matchScore; 99 | this.match = match; 100 | } 101 | 102 | @Override 103 | public int compareTo(Match that) { 104 | return MATCH_COMPARATOR.compare(this, that); 105 | } 106 | 107 | int getMatchScore() { 108 | return matchScore; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tablasco-core/src/main/java/com/gs/tablasco/verify/indexmap/KeyColumnPartialMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Goldman Sachs. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, 10 | * software distributed under the License is distributed on an 11 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 | * KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package com.gs.tablasco.verify.indexmap; 18 | 19 | import com.gs.tablasco.VerifiableTable; 20 | import com.gs.tablasco.verify.ColumnComparators; 21 | import com.gs.tablasco.verify.KeyedVerifiableTable; 22 | import java.util.ArrayList; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | public class KeyColumnPartialMatcher implements PartialMatcher { 30 | private static final Logger LOGGER = LoggerFactory.getLogger(KeyColumnPartialMatcher.class); 31 | private final KeyedVerifiableTable actualData; 32 | private final VerifiableTable expectedData; 33 | private final ColumnComparators columnComparators; 34 | private final PartialMatcher keyGroupPartialMatcher; 35 | 36 | KeyColumnPartialMatcher( 37 | KeyedVerifiableTable actualData, 38 | VerifiableTable expectedData, 39 | ColumnComparators columnComparators, 40 | PartialMatcher keyGroupPartialMatcher) { 41 | this.actualData = actualData; 42 | this.expectedData = expectedData; 43 | this.columnComparators = columnComparators; 44 | this.keyGroupPartialMatcher = keyGroupPartialMatcher; 45 | } 46 | 47 | @Override 48 | public void match( 49 | List allMissingRows, 50 | List allSurplusRows, 51 | List matchedColumns) { 52 | List keyColumnIndices = this.getKeyColumnIndexMaps(matchedColumns); 53 | if (keyColumnIndices.isEmpty()) { 54 | LOGGER.warn("No key columns found!"); 55 | return; 56 | } 57 | Map> missingByKey = new HashMap<>(allMissingRows.size()); 58 | for (UnmatchedIndexMap expected : allMissingRows) { 59 | ExpectedRowView expectedRowView = new ExpectedRowView( 60 | this.expectedData, keyColumnIndices, this.columnComparators, expected.getExpectedIndex()); 61 | missingByKey 62 | .computeIfAbsent(expectedRowView, rowView -> new ArrayList<>(4)) 63 | .add(expected); 64 | } 65 | Map> surplusByKey = new HashMap<>(allSurplusRows.size()); 66 | for (UnmatchedIndexMap actual : allSurplusRows) { 67 | ActualRowView actualRowView = new ActualRowView( 68 | this.actualData, keyColumnIndices, this.columnComparators, actual.getActualIndex()); 69 | surplusByKey 70 | .computeIfAbsent(actualRowView, rowView -> new ArrayList<>(4)) 71 | .add(actual); 72 | } 73 | missingByKey.forEach((rowView, unmatchedIndexMaps) -> { 74 | List missing = missingByKey.get(rowView); 75 | List surplus = surplusByKey.get(rowView); 76 | if (missing != null && !missing.isEmpty() && surplus != null && !surplus.isEmpty()) { 77 | keyGroupPartialMatcher.match(missing, surplus, matchedColumns); 78 | } 79 | }); 80 | } 81 | 82 | private List getKeyColumnIndexMaps(List columnIndices) { 83 | List keyColumns = new ArrayList<>(columnIndices.size()); 84 | for (IndexMap columnIndexMap : columnIndices) { 85 | if (columnIndexMap.isMatched() && this.actualData.isKeyColumn(columnIndexMap.getActualIndex())) { 86 | keyColumns.add(columnIndexMap); 87 | } 88 | } 89 | return keyColumns; 90 | } 91 | } 92 | --------------------------------------------------------------------------------