/tst/org/diffkit/diff/engine/tst/TestEngine.groovy

http://diffkit.googlecode.com/ · Groovy · 513 lines · 395 code · 81 blank · 37 comment · 78 complexity · 90fece3f754d8be658c602d206843be0 MD5 · raw file

  1. /**
  2. * Copyright 2010-2011 Joseph Panico
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.diffkit.diff.engine.tst
  17. import java.io.File;
  18. import org.apache.commons.lang.ClassUtils;
  19. import groovy.util.GroovyTestCase
  20. import org.diffkit.common.DKComparatorChain;
  21. import org.diffkit.common.DKMapKeyValueComparator;
  22. import org.diffkit.db.DKDBColumn
  23. import org.diffkit.db.DKDBConnectionInfo
  24. import org.diffkit.db.DKDatabase
  25. import org.diffkit.db.DKDBFlavor
  26. import org.diffkit.db.DKDBH2Loader
  27. import org.diffkit.db.DKDBPrimaryKey
  28. import org.diffkit.db.DKDBTable;
  29. import org.diffkit.db.DKDBTableDataAccess
  30. import org.diffkit.db.DKDBTableLoader
  31. import org.diffkit.diff.diffor.DKEqualsDiffor;
  32. import org.diffkit.diff.engine.DKColumnComparison
  33. import org.diffkit.diff.engine.DKColumnDiff
  34. import org.diffkit.diff.engine.DKColumnModel
  35. import org.diffkit.diff.engine.DKContext
  36. import org.diffkit.diff.engine.DKDiff;
  37. import org.diffkit.diff.engine.DKDiffEngine
  38. import org.diffkit.diff.engine.DKRowDiff
  39. import org.diffkit.diff.engine.DKSide;
  40. import org.diffkit.diff.engine.DKSource
  41. import org.diffkit.diff.engine.DKStandardTableComparison
  42. import org.diffkit.diff.engine.DKTableModel
  43. import org.diffkit.diff.sns.DKDBSink
  44. import org.diffkit.diff.sns.DKDBSource
  45. import org.diffkit.diff.sns.DKFileSink
  46. import org.diffkit.diff.sns.DKFileSource
  47. import org.diffkit.diff.sns.DKListSource
  48. import org.diffkit.diff.sns.DKListSink
  49. import org.diffkit.diff.sns.DKTableModelUtil;
  50. import org.diffkit.util.DKFileUtil;
  51. import org.diffkit.util.DKResourceUtil;
  52. import org.diffkit.util.DKStringUtil;
  53. /**
  54. * @author jpanico
  55. */
  56. public class TestEngine extends GroovyTestCase {
  57. /**
  58. * both sides use the same TableModel, but there are diffs of each sort; uses DBSources
  59. */
  60. public void testSameModelFromDBToDB(){
  61. def database = this.getDatabase()
  62. def connection = database.connection
  63. def lhsName = 'LHS2'
  64. def rhsName = 'RHS2'
  65. def lhsDBTable = this.createSimpleDBTableModel(lhsName)
  66. def rhsDBTable = this.createSimpleDBTableModel(rhsName)
  67. assert database.createTable( lhsDBTable)
  68. assert database.createTable( rhsDBTable)
  69. DKDBTableDataAccess tableDataAccess = [database]
  70. DKDBH2Loader loader = [database]
  71. lhsDBTable = tableDataAccess.getTable(lhsName.toUpperCase())
  72. rhsDBTable = tableDataAccess.getTable(rhsName.toUpperCase())
  73. assert lhsDBTable
  74. assert rhsDBTable
  75. assert this.load(lhsName, lhsDBTable, loader)
  76. assert this.load(rhsName, rhsDBTable, loader)
  77. def lhsSource = this.createDBSource(lhsDBTable, database)
  78. def rhsSource = this.createDBSource(rhsDBTable, database)
  79. def tableComparison = this.createComparison2( lhsDBTable, rhsDBTable, database)
  80. DKDBSink sink = new DKDBSink(database)
  81. DKDiffEngine engine = new DKDiffEngine()
  82. engine.diff(lhsSource, rhsSource, sink, tableComparison, null)
  83. assert lhsSource.lastIndex == 5
  84. assert rhsSource.lastIndex == 5
  85. assert database.dropTable( lhsDBTable)
  86. assert database.dropTable( rhsDBTable)
  87. def diffContextTable = sink.diffContextTable
  88. def diffTable = sink.diffTable
  89. assert diffContextTable
  90. assert diffTable
  91. def contexts = database.readAllRows( diffContextTable)
  92. assert contexts
  93. assert contexts.size() == 1
  94. println "context->${contexts[0]}"
  95. def diffs = database.readAllRows( diffTable)
  96. assert diffs
  97. assert diffs.size() == 4
  98. def rowStepComparator = new DKMapKeyValueComparator("ROW_STEP")
  99. def columnStepComparator = new DKMapKeyValueComparator("COLUMN_STEP")
  100. def comparatorChain = new DKComparatorChain(rowStepComparator, columnStepComparator)
  101. Collections.sort( diffs, comparatorChain)
  102. println "diffs[0]->${diffs[0]}"
  103. assert diffs[0]["CONTEXT_ID"] == contexts[0]["ID"]
  104. assert diffs[0]["KIND"] == DKDiff.Kind.COLUMN_DIFF.ordinal()
  105. assert diffs[0]["ROW_STEP"] == 2
  106. assert diffs[0]["COLUMN_STEP"] == 1
  107. assert diffs[0]["LHS"] == '1111'
  108. assert diffs[0]["RHS"] == 'xxxx'
  109. println "diffs[3]->${diffs[3]}"
  110. assert diffs[3]["CONTEXT_ID"] == contexts[0]["ID"]
  111. assert diffs[3]["KIND"] == DKDiff.Kind.COLUMN_DIFF.ordinal()
  112. assert diffs[3]["ROW_STEP"] == 6
  113. assert diffs[3]["COLUMN_STEP"] == 1
  114. assert diffs[3]["LHS"] == '6666'
  115. assert diffs[3]["RHS"] == 'xxxx'
  116. assert database.dropTable( diffContextTable)
  117. assert database.dropTable( diffTable)
  118. }
  119. /**
  120. * both sides use the same TableModel, but there are diffs of each sort; uses DBSources
  121. */
  122. public void testSameModelFromDB(){
  123. def database = this.getDatabase()
  124. def connection = database.connection
  125. def lhsName = 'LHS2'
  126. def rhsName = 'RHS2'
  127. def lhsDBTable = this.createSimpleDBTableModel(lhsName)
  128. def rhsDBTable = this.createSimpleDBTableModel(rhsName)
  129. assert database.createTable( lhsDBTable )
  130. assert database.createTable( rhsDBTable)
  131. DKDBTableDataAccess tableDataAccess = [database]
  132. DKDBH2Loader loader = [database]
  133. lhsDBTable = tableDataAccess.getTable(lhsName.toUpperCase())
  134. rhsDBTable = tableDataAccess.getTable(rhsName.toUpperCase())
  135. assert lhsDBTable
  136. assert rhsDBTable
  137. assert this.load(lhsName, lhsDBTable, loader)
  138. assert this.load(rhsName, rhsDBTable, loader)
  139. def lhsSource = this.createDBSource(lhsDBTable, database)
  140. def rhsSource = this.createDBSource(rhsDBTable, database)
  141. def tableComparison = this.createComparison2( lhsDBTable, rhsDBTable, database)
  142. def diffFileName = 'testSameModelFromDB.diff'
  143. DKFileSink sink = this.createSink2( diffFileName)
  144. DKDiffEngine engine = new DKDiffEngine()
  145. engine.diff(lhsSource, rhsSource, sink, tableComparison, null)
  146. assert lhsSource.lastIndex == 5
  147. assert rhsSource.lastIndex == 5
  148. assert database.dropTable( lhsDBTable)
  149. assert database.dropTable( rhsDBTable)
  150. def expectedFile = this.getExpectedFile(diffFileName)
  151. def actualFile = sink.file
  152. assert expectedFile
  153. assert actualFile
  154. assert DKFileUtil.readFullyAsString(expectedFile) == DKFileUtil.readFullyAsString(actualFile)
  155. }
  156. private File getExpectedFile(String filename_){
  157. String expectedFilePath = ClassUtils.getPackageName(this.getClass())
  158. expectedFilePath = DKStringUtil.packageNameToResourcePath(expectedFilePath) + filename_
  159. return DKResourceUtil.findResourceAsFile(expectedFilePath)
  160. }
  161. private DKFileSink createSink2(String sinkFileName_){
  162. File sinkFile = ['./'+sinkFileName_]
  163. if(sinkFile.exists())
  164. sinkFile.delete()
  165. return new DKFileSink(sinkFile, false)
  166. }
  167. private DKStandardTableComparison createComparison2(DKDBTable lhsDBTable_, DKDBTable rhsDBTable_, DKDatabase database_) {
  168. DKTableModel lhsTableModel = DKTableModelUtil.createDefaultTableModel(database_.flavor,lhsDBTable_, null)
  169. DKTableModel rhsTableModel = DKTableModelUtil.createDefaultTableModel(database_.flavor,rhsDBTable_, null)
  170. DKColumnComparison[] map = DKColumnComparison.createColumnPlans( lhsTableModel, rhsTableModel, (int[]) [1,2], DKEqualsDiffor.instance)
  171. // println "map->$map"
  172. DKStandardTableComparison comparison = new DKStandardTableComparison(lhsTableModel, rhsTableModel, DKDiff.Kind.BOTH, map, (int[])[0,1], (int[][])[[0],[0]], (long)100)
  173. // println "comparison->$comparison"
  174. return comparison
  175. }
  176. private DKDBSource createDBSource(DKDBTable table_, DKDatabase database_) {
  177. def tableModel = DKTableModelUtil.createDefaultTableModel(database.flavor,table_, null)
  178. assert tableModel
  179. return new DKDBSource(table_.tableName, null, database_, tableModel, null, null)
  180. }
  181. private boolean load(String name_, DKDBTable table_, DKDBTableLoader loader_){
  182. def csvFile = this.getCsvFile(name_)
  183. return loader_.load(table_, csvFile)
  184. }
  185. private DKDatabase getDatabase(){
  186. DKDBConnectionInfo connectionInfo = ['test', DKDBFlavor.H2,"mem:test", null, null, 'test', 'test']
  187. println "connectionInfo->$connectionInfo"
  188. return new DKDatabase(connectionInfo)
  189. }
  190. private File getCsvFile(String name_){
  191. def csvFile = DKResourceUtil.findResourceAsFile(String.format('org/diffkit/diff/engine/tst/%s.csv',name_))
  192. println "csvFile->$csvFile"
  193. assert csvFile
  194. return csvFile
  195. }
  196. private DKDBTable createSimpleDBTableModel(String tablename_){
  197. DKDBColumn column1 = ['column1', 1, 'VARCHAR', 20, true]
  198. DKDBColumn column2 = ['column2', 2, 'VARCHAR', -1, true]
  199. DKDBColumn column3 = ['column3', 2, 'VARCHAR', -1, true]
  200. DKDBColumn[] columns = [column1, column2, column3]
  201. String[] pkColNames = ['column1']
  202. DKDBPrimaryKey pk = ['pk_' + tablename_, pkColNames]
  203. DKDBTable table = [null, null, tablename_, columns, pk]
  204. return table
  205. }
  206. /**
  207. * both sides use the same TableModel, but there are diffs of each sort; uses FileSources
  208. */
  209. public void testSameModelFromFile(){
  210. def lhsFile = this.getTestFile('lhs1.csv')
  211. println "lhsFile->$lhsFile"
  212. assert lhsFile
  213. def rhsFile = this.getTestFile('rhs1.csv')
  214. println "rhsFile->$rhsFile"
  215. assert rhsFile
  216. DKTableModel tableModel = this.createSimpleTableModel()
  217. DKFileSource lhsSource = new DKFileSource(lhsFile.absolutePath, tableModel, null, null,'\\,', true, true)
  218. DKFileSource rhsSource = new DKFileSource(rhsFile.absolutePath, tableModel, null, null,'\\,', true, true)
  219. def filename = 'testSameModelFromFile.diff'
  220. File sinkFile = ['./'+filename]
  221. if(sinkFile.exists())
  222. sinkFile.delete()
  223. DKFileSink sink = [sinkFile, false]
  224. DKStandardTableComparison tableComparison = this.createSimpleComparison()
  225. println "tableComparison->$tableComparison"
  226. DKDiffEngine engine = new DKDiffEngine()
  227. engine.diff(lhsSource, rhsSource, sink, tableComparison, null)
  228. assert sink.diffCount == 6
  229. String expectedFilePath = ClassUtils.getPackageName(this.getClass())
  230. expectedFilePath = DKStringUtil.packageNameToResourcePath(expectedFilePath) + filename
  231. def expectedFile = DKResourceUtil.findResourceAsFile(expectedFilePath)
  232. String expected = DKFileUtil.readFullyAsString( expectedFile)
  233. assert expected
  234. String actual = DKFileUtil.readFullyAsString( sinkFile)
  235. assert actual
  236. assert expected == actual
  237. }
  238. private File getTestFile(String filename_){
  239. String sourceFilePath = ClassUtils.getPackageName(this.getClass())
  240. sourceFilePath = DKStringUtil.packageNameToResourcePath(sourceFilePath) + filename_
  241. return DKResourceUtil.findResourceAsFile(sourceFilePath)
  242. }
  243. public void testOneSided(){
  244. DKTableModel tableModel = this.createSimpleTableModel()
  245. println "tableModel->$tableModel"
  246. Object[] l1 = ['1111', '1111', 1]
  247. Object[] l2 = ['1111', '1111', 2]
  248. Object[] l3 = ['4444', '4444', 1]
  249. Object[] l4 = ['4444', '4444', 2]
  250. Object[] l5 = ['6666', '6666', 1]
  251. Object[] l6 = ['6666', '6666', 2]
  252. def rows= [l1,l2,l3,l4, l5, l6]
  253. DKListSource lSource = [tableModel, rows]
  254. println "lSource->$lSource"
  255. DKListSource rSource = [tableModel, []]
  256. println "rSource->$rSource"
  257. DKStandardTableComparison tableComparison = this.createSimpleComparison()
  258. println "tableComparison->$tableComparison"
  259. DKDiffEngine engine = new DKDiffEngine()
  260. DKListSink sink = new DKListSink()
  261. engine.diff(lSource, rSource, sink, tableComparison, null)
  262. assert sink.diffCount == 6
  263. for(diff in sink.diffs) {
  264. assert diff.kind == DKDiff.Kind.ROW_DIFF
  265. assert diff.side == DKSide.LEFT
  266. }
  267. lSource = [tableModel, []]
  268. println "lSource->$lSource"
  269. rSource = [tableModel, rows]
  270. println "rSource->$rSource"
  271. sink = new DKListSink()
  272. engine.diff(lSource, rSource, sink, tableComparison, null)
  273. assert sink.diffCount == 6
  274. for(diff in sink.diffs) {
  275. assert diff.kind == DKDiff.Kind.ROW_DIFF
  276. assert diff.side == DKSide.RIGHT
  277. }
  278. }
  279. /**
  280. * both sides use the same TableModel, but there are diffs of each sort
  281. */
  282. public void testSameModelSimpleDiffs(){
  283. DKTableModel tableModel = this.createSimpleTableModel()
  284. println "tableModel->$tableModel"
  285. Object[] l1 = ['1111', '1111', 1]
  286. Object[] l2 = ['1111', '1111', 2]
  287. Object[] l3 = ['4444', '4444', 1]
  288. Object[] l4 = ['4444', '4444', 2]
  289. Object[] l5 = ['6666', '6666', 1]
  290. Object[] l6 = ['6666', '6666', 2]
  291. def lRows= [l1,l2,l3,l4, l5, l6]
  292. DKListSource lSource = [tableModel, lRows]
  293. println "lSource->$lSource"
  294. Object[] r1 = ['1111', '1111', 1]
  295. Object[] r2 = ['1111', 'xxxx', 2]
  296. Object[] r3 = ['2222', '2222', 1]
  297. Object[] r4 = ['5555', '5555', 2]
  298. Object[] r5 = ['6666', 'xxxx', 1]
  299. Object[] r6 = ['6666', '6666', 2]
  300. def rRows= [r1,r2,r3,r4, r5, r6]
  301. DKListSource rSource = [tableModel, rRows]
  302. println "rSource->$rSource"
  303. DKStandardTableComparison tableComparison = this.createSimpleComparison()
  304. println "tableComparison->$tableComparison"
  305. DKDiffEngine engine = new DKDiffEngine()
  306. DKListSink sink = []
  307. engine.diff(lSource, rSource, sink, tableComparison, null)
  308. assert sink.diffCount == 6
  309. assert sink.diffs[0] instanceof DKColumnDiff
  310. assert sink.diffs[0].kind == DKDiff.Kind.COLUMN_DIFF
  311. assert sink.diffs[0].rowStep == 2
  312. assert sink.diffs[0].columnStep == 1
  313. assert sink.diffs[0].rowDisplayValues == [column2:'1111:xxxx', column3:'2']
  314. assert sink.diffs[0].lhs == '1111'
  315. assert sink.diffs[0].rhs == 'xxxx'
  316. assert sink.diffs[1] instanceof DKRowDiff
  317. assert sink.diffs[1].kind == DKDiff.Kind.ROW_DIFF
  318. assert sink.diffs[1].rowStep == 3
  319. assert sink.diffs[1].side == DKSide.RIGHT
  320. assert sink.diffs[1].rowKeyValues == ['2222',1]
  321. assert sink.diffs[1].rowDisplayValues == [column2:'2222', column3:'1']
  322. assert sink.diffs[2] instanceof DKRowDiff
  323. assert sink.diffs[2].kind == DKDiff.Kind.ROW_DIFF
  324. assert sink.diffs[2].rowStep == 4
  325. assert sink.diffs[2].side == DKSide.LEFT
  326. assert sink.diffs[2].rowKeyValues == ['4444',1]
  327. assert sink.diffs[2].rowDisplayValues == [column2:'4444', column3:'1']
  328. assert sink.diffs[3] instanceof DKRowDiff
  329. assert sink.diffs[3].kind == DKDiff.Kind.ROW_DIFF
  330. assert sink.diffs[3].rowStep == 5
  331. assert sink.diffs[3].side == DKSide.LEFT
  332. assert sink.diffs[3].rowKeyValues == ['4444',2]
  333. assert sink.diffs[3].rowDisplayValues == [column2:'4444', column3:'2']
  334. assert sink.diffs[4] instanceof DKRowDiff
  335. assert sink.diffs[4].kind == DKDiff.Kind.ROW_DIFF
  336. assert sink.diffs[4].rowStep == 6
  337. assert sink.diffs[4].side == DKSide.RIGHT
  338. assert sink.diffs[4].rowKeyValues == ['5555',2]
  339. assert sink.diffs[4].rowDisplayValues == [column2:'5555', column3:'2']
  340. assert sink.diffs[5] instanceof DKColumnDiff
  341. assert sink.diffs[5].kind == DKDiff.Kind.COLUMN_DIFF
  342. assert sink.diffs[5].rowStep == 7
  343. assert sink.diffs[5].columnStep == 1
  344. assert sink.diffs[5].rowDisplayValues == [column2:'6666:xxxx', column3:'1']
  345. assert sink.diffs[5].lhs == '6666'
  346. assert sink.diffs[5].rhs == 'xxxx'
  347. }
  348. /**
  349. * both sides are identical, so there should be no diffs
  350. */
  351. public void testIdenticalSources(){
  352. DKDiffEngine engine = new DKDiffEngine()
  353. DKSource lhSource = this.createSimpleSource()
  354. DKSource rhSource = this.createSimpleSource()
  355. DKStandardTableComparison tableComparison = this.createSimpleComparison()
  356. DKListSink sink = []
  357. println "tableComparison->$tableComparison"
  358. engine.diff(lhSource, rhSource, sink, tableComparison, null)
  359. assert sink.diffCount == 0
  360. }
  361. public void testDiffRow(){
  362. DKDiffEngine engine = new DKDiffEngine()
  363. DKContext context = this.createSimpleContext()
  364. println "tableComparison->$context.tableComparison.description"
  365. DKSource rhSource = context.rhs
  366. Object[] rhRow = rhSource.getNextRow()
  367. def sink = context.sink
  368. assert sink.getDiffCount() == 0
  369. sink.open();
  370. engine.diffRow( rhRow, rhRow, context, sink)
  371. assert sink.getDiffCount() == 0
  372. DKSource lhSource = context.lhs
  373. lhSource.getNextRow()
  374. Object[] lhRow = lhSource.getNextRow()
  375. engine.diffRow( lhRow, rhRow, context, sink)
  376. assert sink.getDiffCount() == 1
  377. def diff = sink.diffs[0]
  378. assert diff
  379. assert diff instanceof DKColumnDiff
  380. assert diff.kind == DKDiff.Kind.COLUMN_DIFF
  381. assert diff.lhs == 'zzzz'
  382. assert diff.rhs == 'aaaa'
  383. }
  384. public void testRecordRowDiff(){
  385. DKDiffEngine engine = new DKDiffEngine()
  386. DKContext context = this.createSimpleContext()
  387. DKSource rhSource = context.rhs
  388. Object[] row = rhSource.getNextRow()
  389. def sink = context.sink
  390. assert sink.getDiffCount() == 0
  391. sink.open();
  392. engine.recordRowDiff( row, DKSide.LEFT_INDEX, context, sink)
  393. assert sink.getDiffCount() == 1
  394. def diff = sink.diffs[0]
  395. assert diff
  396. assert diff instanceof DKRowDiff
  397. assert diff.rowStep == 0
  398. assert diff.rowKeyValues == ['zzzz', 1]
  399. assert diff.rowDisplayValues == [column2:'aaaa',column3:'1']
  400. assert diff.kind == DKDiff.Kind.ROW_DIFF
  401. assert diff.side == DKSide.LEFT
  402. }
  403. private DKContext createSimpleContext() {
  404. return new DKContext(this.createSimpleSource(), this.createSimpleSource(), new DKListSink(),this.createSimpleComparison(), null)
  405. }
  406. private DKStandardTableComparison createSimpleComparison() {
  407. DKTableModel tableModel = this.createSimpleTableModel();
  408. DKColumnComparison[] map = DKColumnComparison.createColumnPlans( tableModel, tableModel, (int[]) [1], DKEqualsDiffor.instance)
  409. // println "map->$map"
  410. DKStandardTableComparison comparison = new DKStandardTableComparison(tableModel, tableModel, DKDiff.Kind.BOTH, map, (int[])[0], (int[][])[[1,2],[1,2]], (long)100)
  411. // println "comparison->$comparison"
  412. return comparison
  413. }
  414. private DKListSource createSimpleSource(){
  415. return new DKListSource(this.createSimpleTableModel(), this.createSimpleRows())
  416. }
  417. private List<Object[]> createSimpleRows(){
  418. Object[] l1 = ['zzzz', 'aaaa', 1]
  419. Object[] l2 = ['zzzz', 'zzzz', 2]
  420. Object[] l3 = ['bbbb', 'zzzz', 1]
  421. Object[] l4 = ['aaaa', 'bbbb', 2]
  422. return [l1,l2,l3,l4]
  423. }
  424. private DKTableModel createSimpleTableModel(){
  425. DKColumnModel column1 = [0, 'column1', DKColumnModel.Type.STRING]
  426. DKColumnModel column2 = [1, 'column2', DKColumnModel.Type.STRING]
  427. DKColumnModel column3 = [2, 'column3', DKColumnModel.Type.INTEGER]
  428. DKColumnModel[] columns = [column1, column2, column3]
  429. int[] key = [0,2]
  430. return new DKTableModel("simple_table_model",columns, key)
  431. }
  432. }