From c0c74641f29221e700c21a6ee6065f58dd57a6d7 Mon Sep 17 00:00:00 2001
From: Hans Moog <hm@mkjc.net>
Date: Fri, 12 Jun 2020 19:12:28 +0200
Subject: [PATCH] Feat: added more reliable fails in test case

---
 .../valuetransfers/packages/tangle/tangle.go  |  19 +-
 .../tangle/tangle_concurrency_test.go         | 262 +++++++++---------
 .../packages/tangle/tangle_test.go            |  29 +-
 3 files changed, 148 insertions(+), 162 deletions(-)

diff --git a/dapps/valuetransfers/packages/tangle/tangle.go b/dapps/valuetransfers/packages/tangle/tangle.go
index 36ff6ad3..dae0647a 100644
--- a/dapps/valuetransfers/packages/tangle/tangle.go
+++ b/dapps/valuetransfers/packages/tangle/tangle.go
@@ -1151,7 +1151,7 @@ func (tangle *Tangle) processSolidificationStackEntry(solidificationStack *list.
 	}
 
 	// abort if the payload is not solid or invalid
-	payloadSolid, payloadSolidityErr := tangle.checkPayloadSolidity(currentPayload, currentPayloadMetadata, consumedBranches)
+	payloadSolid, payloadSolidityErr := tangle.payloadBecameNewlySolid(currentPayload, currentPayloadMetadata, consumedBranches)
 	if payloadSolidityErr != nil {
 		tangle.Events.PayloadInvalid.Trigger(solidificationStackEntry.CachedPayload, solidificationStackEntry.CachedPayloadMetadata, payloadSolidityErr)
 
@@ -1164,7 +1164,7 @@ func (tangle *Tangle) processSolidificationStackEntry(solidificationStack *list.
 	}
 
 	// book the solid entities
-	transactionBooked, payloadBooked, decisionPending, bookingErr := tangle.book(solidificationStackEntry.Retain())
+	transactionBooked, _, decisionPending, bookingErr := tangle.book(solidificationStackEntry.Retain())
 	if bookingErr != nil {
 		tangle.Events.Error.Trigger(bookingErr)
 
@@ -1177,12 +1177,9 @@ func (tangle *Tangle) processSolidificationStackEntry(solidificationStack *list.
 	// trigger events and schedule check of approvers / consumers
 	if transactionBooked {
 		tangle.Events.TransactionBooked.Trigger(solidificationStackEntry.CachedTransaction, solidificationStackEntry.CachedTransactionMetadata, decisionPending)
-
-		tangle.ForEachConsumers(currentTransaction, tangle.createValuePayloadFutureConeIterator(solidificationStack, processedPayloads))
-	}
-	if payloadBooked {
-		tangle.ForeachApprovers(currentPayload.ID(), tangle.createValuePayloadFutureConeIterator(solidificationStack, processedPayloads))
 	}
+	tangle.ForEachConsumers(currentTransaction, tangle.createValuePayloadFutureConeIterator(solidificationStack, processedPayloads))
+	tangle.ForeachApprovers(currentPayload.ID(), tangle.createValuePayloadFutureConeIterator(solidificationStack, processedPayloads))
 }
 
 func (tangle *Tangle) book(entitiesToBook *valuePayloadPropagationStackEntry) (transactionBooked bool, payloadBooked bool, decisionPending bool, err error) {
@@ -1396,15 +1393,15 @@ func (tangle *Tangle) payloadBranchID(payloadID payload.ID) branchmanager.Branch
 	return payloadMetadata.BranchID()
 }
 
-// checkPayloadSolidity returns true if the given payload is solid. A payload is considered to be solid, if it is either
-// already marked as solid or if its referenced payloads are marked as solid.
-func (tangle *Tangle) checkPayloadSolidity(p *payload.Payload, payloadMetadata *PayloadMetadata, transactionBranches []branchmanager.BranchID) (solid bool, err error) {
+// payloadBecameNewlySolid returns true if the given payload is solid but was not marked as solid. yet.
+func (tangle *Tangle) payloadBecameNewlySolid(p *payload.Payload, payloadMetadata *PayloadMetadata, transactionBranches []branchmanager.BranchID) (solid bool, err error) {
+	// abort if the payload was deleted
 	if p == nil || p.IsDeleted() || payloadMetadata == nil || payloadMetadata.IsDeleted() {
 		return
 	}
 
+	// abort if the payload was marked as solid already
 	if payloadMetadata.IsSolid() {
-		solid = payloadMetadata.BranchID() != branchmanager.UndefinedBranchID
 		return
 	}
 
diff --git a/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go b/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go
index f9b8402d..3155f447 100644
--- a/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go
+++ b/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go
@@ -5,16 +5,17 @@ import (
 	"sync"
 	"testing"
 
+	"github.com/iotaledger/hive.go/events"
+	"github.com/iotaledger/hive.go/kvstore/mapdb"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tipmanager"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/hive.go/events"
-	"github.com/iotaledger/hive.go/kvstore/mapdb"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
 )
 
 func TestConcurrency(t *testing.T) {
@@ -214,143 +215,146 @@ func TestReverseValueObjectSolidification(t *testing.T) {
 }
 
 func TestReverseTransactionSolidification(t *testing.T) {
-	// img/reverse-transaction-solidification.png
-	// Builds a UTXO-DAG with `txChains` spending outputs from the corresponding chain.
-	// All value objects reference the previous value object, effectively creating a chain.
-	// The test attaches the prepared value objects concurrently in reverse order.
-	//name, err := ioutil.TempDir("", "example")
-	//require.NoError(t, err)
-	//bDB, err := database.NewDB(name)
-	//require.NoError(t, err)
-	//
-	//tangle := New(bDB.NewStore())
-	tangle := New(mapdb.NewMapDB())
-
-	tangle.Events.Error.Attach(events.NewClosure(func(err error) {
-		fmt.Println(err)
-	}))
-
-	tangle.Events.TransactionInvalid.Attach(events.NewClosure(func(_ *transaction.CachedTransaction, _ *CachedTransactionMetadata, err error) {
-		panic(err)
-	}))
-	tangle.Events.PayloadInvalid.Attach(events.NewClosure(func(_ *payload.CachedPayload, _ *CachedPayloadMetadata, err error) {
-		panic(err)
-	}))
-
-	tipManager := tipmanager.New()
-
-	txChains := 1
-	count := 100
-	threads := 10
-	countTotal := txChains * threads * count
+	testIterations := 500
+
+	// repeat the test a few times
+	for k := 0; k < testIterations; k++ {
+		// img/reverse-transaction-solidification.png
+		// Builds a UTXO-DAG with `txChains` spending outputs from the corresponding chain.
+		// All value objects reference the previous value object, effectively creating a chain.
+		// The test attaches the prepared value objects concurrently in reverse order.
+		//name, err := ioutil.TempDir("", "example")
+		//require.NoError(t, err)
+		//bDB, err := database.NewDB(name)
+		//require.NoError(t, err)
+		//
+		//tangle := New(bDB.NewStore())
+		tangle := New(mapdb.NewMapDB())
+
+		tangle.Events.Error.Attach(events.NewClosure(func(err error) {
+			fmt.Println(err)
+		}))
 
-	// initialize tangle with genesis block
-	outputs := make(map[address.Address][]*balance.Balance)
-	for i := 0; i < txChains; i++ {
-		outputs[address.Random()] = []*balance.Balance{
-			balance.New(balance.ColorIOTA, 1),
-		}
-	}
-	inputIDs := loadSnapshotFromOutputs(tangle, outputs)
+		tangle.Events.TransactionInvalid.Attach(events.NewClosure(func(_ *transaction.CachedTransaction, _ *CachedTransactionMetadata, err error) {
+			panic(err)
+		}))
+		tangle.Events.PayloadInvalid.Attach(events.NewClosure(func(_ *payload.CachedPayload, _ *CachedPayloadMetadata, err error) {
+			panic(err)
+		}))
 
-	transactions := make([]*transaction.Transaction, countTotal)
-	valueObjects := make([]*payload.Payload, countTotal)
+		tipManager := tipmanager.New()
 
-	// create chains of transactions
-	for i := 0; i < count*threads; i++ {
-		for j := 0; j < txChains; j++ {
-			var tx *transaction.Transaction
+		txChains := 1
+		count := 10
+		threads := 2
+		countTotal := txChains * threads * count
 
-			// transferring from genesis
-			if i == 0 {
-				tx = transaction.New(
-					transaction.NewInputs(inputIDs[j]),
-					transaction.NewOutputs(
-						map[address.Address][]*balance.Balance{
-							address.Random(): {
-								balance.New(balance.ColorIOTA, 1),
-							},
-						}),
-				)
-			} else {
-				// create chains in UTXO dag
-				tx = transaction.New(
-					getTxOutputsAsInputs(transactions[i*txChains-txChains+j]),
-					transaction.NewOutputs(
-						map[address.Address][]*balance.Balance{
-							address.Random(): {
-								balance.New(balance.ColorIOTA, 1),
-							},
-						}),
-				)
+		// initialize tangle with genesis block
+		outputs := make(map[address.Address][]*balance.Balance)
+		for i := 0; i < txChains; i++ {
+			outputs[address.Random()] = []*balance.Balance{
+				balance.New(balance.ColorIOTA, 1),
 			}
-
-			transactions[i*txChains+j] = tx
 		}
-	}
-
-	// prepare value objects (simple chain)
-	for i := 0; i < countTotal; i++ {
-		parent1, parent2 := tipManager.Tips()
-		valueObject := payload.New(parent1, parent2, transactions[i])
-
-		tipManager.AddTip(valueObject)
-		valueObjects[i] = valueObject
-	}
-
-	//TODO:
-	//for i := 0; i < countTotal; i++ {
-	//	fmt.Println(i, "ValueObject", valueObjects[i].ID())
-	//	fmt.Println(i, "Transaction", valueObjects[i].Transaction().ID())
-	//}
-
-	// attach value objects in reverse order
-	var wg sync.WaitGroup
-	for thread := 0; thread < threads; thread++ {
-		wg.Add(1)
-		go func(threadNo int) {
-			defer wg.Done()
+		inputIDs := loadSnapshotFromOutputs(tangle, outputs)
+
+		transactions := make([]*transaction.Transaction, countTotal)
+		valueObjects := make([]*payload.Payload, countTotal)
+
+		// create chains of transactions
+		for i := 0; i < count*threads; i++ {
+			for j := 0; j < txChains; j++ {
+				var tx *transaction.Transaction
+
+				// transferring from genesis
+				if i == 0 {
+					tx = transaction.New(
+						transaction.NewInputs(inputIDs[j]),
+						transaction.NewOutputs(
+							map[address.Address][]*balance.Balance{
+								address.Random(): {
+									balance.New(balance.ColorIOTA, 1),
+								},
+							}),
+					)
+				} else {
+					// create chains in UTXO dag
+					tx = transaction.New(
+						getTxOutputsAsInputs(transactions[i*txChains-txChains+j]),
+						transaction.NewOutputs(
+							map[address.Address][]*balance.Balance{
+								address.Random(): {
+									balance.New(balance.ColorIOTA, 1),
+								},
+							}),
+					)
+				}
 
-			for i := countTotal - 1 - threadNo; i >= 0; i -= threads {
-				valueObject := valueObjects[i]
-				tangle.AttachPayloadSync(valueObject)
+				transactions[i*txChains+j] = tx
 			}
-		}(thread)
-	}
-	wg.Wait()
+		}
 
-	fmt.Println("finished attaching")
+		// prepare value objects (simple chain)
+		for i := 0; i < countTotal; i++ {
+			parent1, parent2 := tipManager.Tips()
+			valueObject := payload.New(parent1, parent2, transactions[i])
 
-	// verify correctness
-	for i := 0; i < countTotal; i++ {
-		// check if transaction metadata is found in database
-		require.Truef(t, tangle.TransactionMetadata(transactions[i].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
-			assert.Truef(t, transactionMetadata.Solid(), "the transaction %s is not solid", transactions[i].ID().String())
-			assert.Equalf(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
-		}), "transaction metadata %s not found in database", transactions[i].ID())
-
-		// check if value object metadata is found in database
-		require.Truef(t, tangle.PayloadMetadata(valueObjects[i].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
-			assert.Truef(t, payloadMetadata.IsSolid(), "the payload %s is not solid", valueObjects[i].ID())
-			assert.Equalf(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
-		}), "value object metadata %s not found in database", valueObjects[i].ID())
+			tipManager.AddTip(valueObject)
+			valueObjects[i] = valueObject
+		}
 
-		// check if outputs are found in database
-		transactions[i].Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool {
-			cachedOutput := tangle.TransactionOutput(transaction.NewOutputID(address, transactions[i].ID()))
-			assert.Truef(t, cachedOutput.Consume(func(output *Output) {
-				// only the last outputs in chain should not be spent
-				if i+txChains >= countTotal {
-					assert.Equalf(t, 0, output.ConsumerCount(), "the output should not be spent")
-				} else {
-					assert.Equalf(t, 1, output.ConsumerCount(), "the output should be spent")
+		//TODO:
+		//for i := 0; i < countTotal; i++ {
+		//	fmt.Println(i, "ValueObject", valueObjects[i].ID())
+		//	fmt.Println(i, "Transaction", valueObjects[i].Transaction().ID())
+		//}
+
+		// attach value objects in reverse order
+		var wg sync.WaitGroup
+		for thread := 0; thread < threads; thread++ {
+			wg.Add(1)
+			go func(threadNo int) {
+				defer wg.Done()
+
+				for i := countTotal - 1 - threadNo; i >= 0; i -= threads {
+					valueObject := valueObjects[i]
+					tangle.AttachPayloadSync(valueObject)
 				}
-				assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances())
-				assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
-				assert.Truef(t, output.Solid(), "the output is not solid")
-			}), "output not found in database for tx %s", transactions[i])
-			return true
-		})
+			}(thread)
+		}
+		wg.Wait()
+
+		// verify correctness
+		for i := 0; i < countTotal; i++ {
+			// check if transaction metadata is found in database
+			require.Truef(t, tangle.TransactionMetadata(transactions[i].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
+				require.Truef(t, transactionMetadata.Solid(), "the transaction %s is not solid", transactions[i].ID().String())
+				require.Equalf(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
+			}), "transaction metadata %s not found in database", transactions[i].ID())
+
+			// check if value object metadata is found in database
+			require.Truef(t, tangle.PayloadMetadata(valueObjects[i].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
+				require.Truef(t, payloadMetadata.IsSolid(), "the payload %s is not solid", valueObjects[i].ID())
+				require.Equalf(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
+			}), "value object metadata %s not found in database", valueObjects[i].ID())
+
+			// check if outputs are found in database
+			transactions[i].Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool {
+				cachedOutput := tangle.TransactionOutput(transaction.NewOutputID(address, transactions[i].ID()))
+				assert.Truef(t, cachedOutput.Consume(func(output *Output) {
+					// only the last outputs in chain should not be spent
+					if i+txChains >= countTotal {
+						assert.Equalf(t, 0, output.ConsumerCount(), "the output should not be spent")
+					} else {
+						assert.Equalf(t, 1, output.ConsumerCount(), "the output should be spent")
+					}
+					assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances())
+					assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
+					assert.Truef(t, output.Solid(), "the output is not solid")
+				}), "output not found in database for tx %s", transactions[i])
+				return true
+			})
+		}
 	}
 }
 
diff --git a/dapps/valuetransfers/packages/tangle/tangle_test.go b/dapps/valuetransfers/packages/tangle/tangle_test.go
index e423e4ba..dab6589c 100644
--- a/dapps/valuetransfers/packages/tangle/tangle_test.go
+++ b/dapps/valuetransfers/packages/tangle/tangle_test.go
@@ -1248,8 +1248,8 @@ func TestCheckPayloadSolidity(t *testing.T) {
 		metadata.SetBranchID(branchmanager.MasterBranchID)
 
 		transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID}
-		solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches)
-		assert.True(t, solid)
+		solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches)
+		assert.False(t, solid)
 		assert.NoError(t, err)
 	}
 
@@ -1259,7 +1259,7 @@ func TestCheckPayloadSolidity(t *testing.T) {
 		metadata := NewPayloadMetadata(valueObject.ID())
 
 		transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID}
-		solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches)
+		solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches)
 		assert.True(t, solid)
 		assert.NoError(t, err)
 	}
@@ -1275,7 +1275,7 @@ func TestCheckPayloadSolidity(t *testing.T) {
 		metadata := NewPayloadMetadata(valueObject.ID())
 
 		transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID}
-		solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches)
+		solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches)
 		assert.True(t, solid)
 		assert.NoError(t, err)
 	}
@@ -1290,22 +1290,7 @@ func TestCheckPayloadSolidity(t *testing.T) {
 		metadata := NewPayloadMetadata(valueObject.ID())
 
 		transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID}
-		solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches)
-		assert.False(t, solid)
-		assert.NoError(t, err)
-	}
-
-	// check with non-solid parents but branch set -> should not be solid
-	{
-		setParent := func(payloadMetadata *PayloadMetadata) {
-			payloadMetadata.SetBranchID(branchmanager.MasterBranchID)
-		}
-
-		valueObject := payload.New(storeParentPayloadWithMetadataFunc(t, tangle, setParent), storeParentPayloadWithMetadataFunc(t, tangle, setParent), createDummyTransaction())
-		metadata := NewPayloadMetadata(valueObject.ID())
-
-		transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID}
-		solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches)
+		solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches)
 		assert.False(t, solid)
 		assert.NoError(t, err)
 	}
@@ -1330,7 +1315,7 @@ func TestCheckPayloadSolidity(t *testing.T) {
 		metadata := NewPayloadMetadata(valueObject.ID())
 
 		transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID}
-		solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches)
+		solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches)
 		assert.False(t, solid)
 		assert.Error(t, err)
 	}
@@ -1355,7 +1340,7 @@ func TestCheckPayloadSolidity(t *testing.T) {
 		metadata := NewPayloadMetadata(valueObject.ID())
 
 		transactionBranches := []branchmanager.BranchID{{2}}
-		solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches)
+		solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches)
 		assert.False(t, solid)
 		assert.Error(t, err)
 	}
-- 
GitLab