Skip to content

Commit

Permalink
Merge pull request #2960 from onflow/supun/container-mutation-test
Browse files Browse the repository at this point in the history
Add test for container mutation while iterating
  • Loading branch information
SupunS authored Dec 4, 2023
2 parents b48fd6b + 019b35d commit 6dfc8ee
Showing 1 changed file with 191 additions and 0 deletions.
191 changes: 191 additions & 0 deletions runtime/tests/interpreter/container_mutation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,197 @@ func TestInterpretContainerMutationAfterNilCoalescing(t *testing.T) {
)
}

func TestInterpretContainerMutationWhileIterating(t *testing.T) {

t.Run("array, append", func(t *testing.T) {
t.Parallel()

inter := parseCheckAndInterpret(t, `
fun test(): [String] {
let array: [String] = ["foo", "bar"]
var i = 0
for element in array {
if i == 0 {
array.append("baz")
}
array[i] = "hello"
i = i + 1
}
return array
}
`)

result, err := inter.Invoke("test")
require.NoError(t, err)

RequireValuesEqual(
t,
inter,
interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeString,
},
common.ZeroAddress,
interpreter.NewUnmeteredStringValue("hello"), // updated
interpreter.NewUnmeteredStringValue("hello"), // updated
interpreter.NewUnmeteredStringValue("baz"), // NOT updated
),
result,
)
})

t.Run("array, remove", func(t *testing.T) {
t.Parallel()

inter := parseCheckAndInterpret(t, `
fun test() {
let array: [String] = ["foo", "bar", "baz"]
var i = 0
for element in array {
if i == 0 {
array.remove(at: 1)
}
}
}
`)

_, err := inter.Invoke("test")
RequireError(t, err)
assert.ErrorAs(t, err, &interpreter.ArrayIndexOutOfBoundsError{})
})

t.Run("dictionary, add", func(t *testing.T) {
t.Parallel()

inter := parseCheckAndInterpret(t, `
fun test(): {String: String} {
let dictionary: {String: String} = {"a": "foo", "b": "bar"}
var i = 0
dictionary.forEachKey(fun (key: String): Bool {
if i == 0 {
dictionary["c"] = "baz"
}
dictionary[key] = "hello"
return true
})
return dictionary
}
`)

result, err := inter.Invoke("test")
require.NoError(t, err)

require.IsType(t, &interpreter.DictionaryValue{}, result)
dictionary := result.(*interpreter.DictionaryValue)

require.Equal(t, 3, dictionary.Count())

val, present := dictionary.Get(
inter,
interpreter.EmptyLocationRange,
interpreter.NewUnmeteredStringValue("a"),
)
assert.True(t, present)
assert.Equal(t, interpreter.NewUnmeteredStringValue("hello"), val) // Updated

val, present = dictionary.Get(
inter,
interpreter.EmptyLocationRange,
interpreter.NewUnmeteredStringValue("b"),
)
assert.True(t, present)
assert.Equal(t, interpreter.NewUnmeteredStringValue("hello"), val) // Updated

val, present = dictionary.Get(
inter,
interpreter.EmptyLocationRange,
interpreter.NewUnmeteredStringValue("c"),
)
assert.True(t, present)
assert.Equal(t, interpreter.NewUnmeteredStringValue("baz"), val) // Not Updated
})

t.Run("dictionary, remove", func(t *testing.T) {
t.Parallel()

inter := parseCheckAndInterpret(t, `
fun test(): {String: String} {
let dictionary: {String: String} = {"a": "foo", "b": "bar", "c": "baz"}
var i = 0
dictionary.forEachKey(fun (key: String): Bool {
if i == 0 {
dictionary.remove(key: "b")
}
return true
})
return dictionary
}
`)

result, err := inter.Invoke("test")
require.NoError(t, err)

require.IsType(t, &interpreter.DictionaryValue{}, result)
dictionary := result.(*interpreter.DictionaryValue)

require.Equal(t, 2, dictionary.Count())

val, present := dictionary.Get(
inter,
interpreter.EmptyLocationRange,
interpreter.NewUnmeteredStringValue("a"),
)
assert.True(t, present)
assert.Equal(t, interpreter.NewUnmeteredStringValue("foo"), val)

val, present = dictionary.Get(
inter,
interpreter.EmptyLocationRange,
interpreter.NewUnmeteredStringValue("c"),
)
assert.True(t, present)
assert.Equal(t, interpreter.NewUnmeteredStringValue("baz"), val)
})

t.Run("resource dictionary, remove", func(t *testing.T) {
t.Parallel()

inter := parseCheckAndInterpret(t, `
resource Foo {}
fun test(): @{String: Foo} {
let dictionary: @{String: Foo} <- {"a": <- create Foo(), "b": <- create Foo(), "c": <- create Foo()}
var dictionaryRef = &dictionary as &{String: Foo}
var i = 0
dictionary.forEachKey(fun (key: String): Bool {
if i == 0 {
destroy dictionaryRef.remove(key: "b")
}
return true
})
return <- dictionary
}
`)

_, err := inter.Invoke("test")
RequireError(t, err)
assert.ErrorAs(t, err, &interpreter.ContainerMutatedDuringIterationError{})
})
}

func TestInterpretInnerContainerMutationWhileIteratingOuter(t *testing.T) {

t.Parallel()
Expand Down

0 comments on commit 6dfc8ee

Please sign in to comment.