Skip to content

Commit

Permalink
[Doc] Add explainations on how to use take_object_by_id in tests (#1584)
Browse files Browse the repository at this point in the history
  • Loading branch information
lxfind committed Apr 26, 2022
1 parent aba09fc commit 59a491c
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 6 deletions.
3 changes: 0 additions & 3 deletions doc/src/build/programming-with-objects/ch1-object-basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,6 @@ TestScenario::next_tx(scenario, &owner);

`TestScenario::take_object` removes the object of given type from global storage that's owned by the current transaction sender (it also implicitly checks `can_take_object`). If this line of code succeeds, it means that `owner` indeed owns an object of type `ColorObject`.
We also check that the field values of the object match with what we set in creation. At the end, we must return the object back to the global storage by calling `TestScenario::return_object` so that it's back to the global storage. This also ensures that if any mutations happened to the object during the test, the global storage is aware of the changes.
You may have noticed that `take_object` picks the object only based on the type parameter. What if there are multiple objects of the same type owned by the account? How do we retrieve each of them? In fact, if you call `take_object` when there are more than one object of the same type in the same account, an assertion failure will be triggered. We are working on adding an API just for this. Update coming soon.

You may have noticed that `take_object` picks the object based solely on the type parameter. What if there are multiple objects of the same type owned by the account? How do we retrieve each of them? In fact, if you call `take_object` when there are more than one object of the same type in the same account, an assertion failure will be triggered. We are working on adding an API just for this. Update coming soon.

Again, you can find the full code in [ColorObject.move](../../../../sui_programmability/examples/objects_tutorial/sources/ColorObject.move).

Expand Down
42 changes: 41 additions & 1 deletion doc/src/build/programming-with-objects/ch2-using-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,47 @@ In the above function signature, `from_object` can be a read-only reference beca

> :bulb: Although `from_object` is a read-only reference in this transaction, it is still a mutable object in Sui storage--another transaction could be sent to mutate the object at the same time! To prevent this, Sui must lock any mutable object used as a transaction input, even when it's passed as a read-only reference. In addition, only an object's owner can send a transaction that locks the object.
We cannot write a unit test for the `copy_into` function just yet, as the support for retrieving multiple objects of the same type from the same account is still work in progress. This will be updated as soon as we have that.
Let's write a unit test to see how we could interact with multiple objects of the same type in tests.
In the previous chapter, we introduced the `take_object<T>` API, which takes an object of type `T` from the global storage created by previous transactions. However, what if there are multiple objects of the same type? `take_object<T>` will no longer be able to tell which one to return. To solve this problem, we need to use two new APIs. The first is `TxContext::last_created_object_id(ctx)`, which returns the ID of the most recent created object. The second is `TestScenario::take_object_by_id<T>`, which returns an object of type `T` with a specific object ID.
Now let's take a look at the test (`test_copy_into`):
```rust
let owner = @0x1;
let scenario = &mut TestScenario::begin(&owner);
// Create two ColorObjects owned by `owner`, and obtain their IDs.
let (id1, id2) = {
let ctx = TestScenario::ctx(scenario);
ColorObject::create(255, 255, 255, ctx);
let id1 = TxContext::last_created_object_id(ctx);
ColorObject::create(0, 0, 0, ctx);
let id2 = TxContext::last_created_object_id(ctx);
(id1, id2)
};
```
The above code created two objects. Note that right after each call, we make a call to `TxContext::last_created_object_id` to get the ID of the object just created. At the end we have `id1` and `id2` capturing the IDs of the two objects. Next we retrieve both of them and test the `copy_into` method:
```rust
TestScenario::next_tx(scenario, &owner);
{
let obj1 = TestScenario::take_object_by_id<ColorObject>(scenario, id1);
let obj2 = TestScenario::take_object_by_id<ColorObject>(scenario, id2);
let (red, green, blue) = ColorObject::get_color(&obj1);
assert!(red == 255 && green == 255 && blue == 255, 0);

let ctx = TestScenario::ctx(scenario);
ColorObject::copy_into(&obj2, &mut obj1, ctx);
TestScenario::return_object(scenario, obj1);
TestScenario::return_object(scenario, obj2);
};
```
We used `take_object_by_id` to take both objects using different IDs. We then used `copy_into` to update `obj1`'s value using `obj2`'s. We can verify that the mutation works:
```rust
TestScenario::next_tx(scenario, &owner);
{
let obj1 = TestScenario::take_object_by_id<ColorObject>(scenario, id1);
let (red, green, blue) = ColorObject::get_color(&obj1);
assert!(red == 0 && green == 0 && blue == 0, 0);
TestScenario::return_object(scenario, obj1);
}
```

### Pass objects by value
Objects can also be passed by value into an entry function. By doing so, the object is moved out of Sui storage (a.k.a. deleted). It is then up to the Move code to decide where this object should go.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ module Tutorial::ColorObject {
module Tutorial::ColorObjectTests {
use Sui::TestScenario;
use Tutorial::ColorObject::{Self, ColorObject};
use Sui::TxContext;

// == Tests covered in Chapter 1 ==

Expand Down Expand Up @@ -108,6 +109,40 @@ module Tutorial::ColorObjectTests {

// == Tests covered in Chapter 2 ==

#[test]
public(script) fun test_copy_into() {
let owner = @0x1;
let scenario = &mut TestScenario::begin(&owner);
// Create two ColorObjects owned by `owner`, and obtain their IDs.
let (id1, id2) = {
let ctx = TestScenario::ctx(scenario);
ColorObject::create(255, 255, 255, ctx);
let id1 = TxContext::last_created_object_id(ctx);
ColorObject::create(0, 0, 0, ctx);
let id2 = TxContext::last_created_object_id(ctx);
(id1, id2)
};
TestScenario::next_tx(scenario, &owner);
{
let obj1 = TestScenario::take_object_by_id<ColorObject>(scenario, id1);
let obj2 = TestScenario::take_object_by_id<ColorObject>(scenario, id2);
let (red, green, blue) = ColorObject::get_color(&obj1);
assert!(red == 255 && green == 255 && blue == 255, 0);

let ctx = TestScenario::ctx(scenario);
ColorObject::copy_into(&obj2, &mut obj1, ctx);
TestScenario::return_object(scenario, obj1);
TestScenario::return_object(scenario, obj2);
};
TestScenario::next_tx(scenario, &owner);
{
let obj1 = TestScenario::take_object_by_id<ColorObject>(scenario, id1);
let (red, green, blue) = ColorObject::get_color(&obj1);
assert!(red == 0 && green == 0 && blue == 0, 0);
TestScenario::return_object(scenario, obj1);
}
}

#[test]
public(script) fun test_delete() {
let owner = @0x1;
Expand Down
2 changes: 1 addition & 1 deletion sui_programmability/framework/sources/TxContext.move
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ module Sui::TxContext {

#[test_only]
/// Return the most recent created object ID.
public fun get_last_created_object_id(self: &TxContext): ID {
public fun last_created_object_id(self: &TxContext): ID {
let ids_created = self.ids_created;
assert!(ids_created > 0, ENO_IDS_CREATED);
ID::new(derive_id(*&self.tx_hash, ids_created - 1))
Expand Down
2 changes: 1 addition & 1 deletion sui_programmability/framework/tests/TestScenarioTests.move
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ module Sui::TestScenarioTests {
let obj = Object { id: versioned_id, value: 10 };
Transfer::transfer(obj, copy sender);
let ctx = TestScenario::ctx(&mut scenario);
assert!(id == TxContext::get_last_created_object_id(ctx), 0);
assert!(id == TxContext::last_created_object_id(ctx), 0);
};
}

Expand Down

1 comment on commit 59a491c

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bench results

�[0m�[0m�[1m�[32m Finished�[0m release [optimized] target(s) in 0.35s
�[0m�[0m�[1m�[32m Running�[0m target/release/bench microbench throughput
�[2m2022-04-26T18:07:25.842458Z�[0m �[32m INFO�[0m �[2msui::benchmark�[0m�[2m:�[0m benchmark : Benchmark { committee_size: 1, send_timeout_us: 40000000, recv_timeout_us: 40000000, buffer_size: 650000, tcp_connections: 0, db_cpus: 1, use_move: false, batch_size: 2000, running_mode: LocalSingleValidatorThread, working_dir: None, bench_type: MicroBenchmark { host: "127.0.0.1", port: 9555, type_: Throughput { num_transactions: 100000 } } }
�[2m2022-04-26T18:07:25.843020Z�[0m �[32m INFO�[0m �[2msui::benchmark::validator_preparer�[0m�[2m:�[0m authority address hex: 0435ABB91BED445E32BA0B8D232D234DEABBA58E
�[2m2022-04-26T18:07:25.843128Z�[0m �[32m INFO�[0m �[2msui::benchmark::validator_preparer�[0m�[2m:�[0m Open database on path: "/tmp/DB_A834C2BBF82DC3E9303B0D6BEBBFC3E8C68D6394"
�[2m2022-04-26T18:07:26.866605Z�[0m �[32m INFO�[0m �[2msui::benchmark::validator_preparer�[0m�[2m:�[0m Spawning a validator thread...
�[2m2022-04-26T18:07:26.867316Z�[0m �[32m INFO�[0m �[2msui_network::transport�[0m�[2m:�[0m Listening to TCP traffic on 127.0.0.1:9555
Throughout: 50163.43246296434 tps

Please sign in to comment.