Skip to content

Commit ab9714f

Browse files
authored
Merge pull request #27 from icon-project/feature-event
Feature event
2 parents 025ca11 + 6806667 commit ab9714f

16 files changed

+1427
-7
lines changed

unittest/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ dependencies {
1515
implementation 'org.ow2.asm:asm:7.0'
1616
implementation 'org.ow2.asm:asm-commons:7.0'
1717
implementation 'foundation.icon:blst-java:0.3.0'
18+
implementation 'foundation.icon:javaee-annotation-processor:0.9.0'
19+
implementation 'com.squareup:javapoet:1.12.1'
1820

1921
testImplementation 'foundation.icon:javaee-scorex:0.5.3'
2022
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
2123
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
24+
testAnnotationProcessor project
2225
}
2326

2427
test {
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2023 PARAMETA Corp.
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+
17+
package com.iconloop.score.test;
18+
19+
import score.Address;
20+
import score.impl.TypeConverter;
21+
22+
public class Event {
23+
private final Address contract;
24+
private final Object[] indexed;
25+
private final Object[] data;
26+
27+
public static final Object[] EMPTY = new Object[0];
28+
29+
public Event(Address contract, Object[] indexed, Object[] data) {
30+
if (contract != null && !contract.isContract()) {
31+
throw new IllegalArgumentException("invalid contract address");
32+
}
33+
if (indexed == null || indexed.length < 1 || indexed.length > 4) {
34+
throw new IllegalArgumentException("invalid indexed parameter: length=" +
35+
((indexed != null) ? indexed.length : "null"));
36+
}
37+
this.contract = contract;
38+
this.indexed = indexed;
39+
this.data = data == null ? EMPTY : data;
40+
for (var obj : this.indexed) {
41+
if (!TypeConverter.isValidEventValue(obj)) {
42+
throw new IllegalArgumentException("invalid value type " + obj.getClass());
43+
}
44+
}
45+
for (var obj : this.data) {
46+
if (!TypeConverter.isValidEventValue(obj)) {
47+
throw new IllegalArgumentException("invalid value type " + obj.getClass());
48+
}
49+
}
50+
}
51+
52+
public int numberOfIndexed() {
53+
return indexed.length;
54+
}
55+
56+
public int numberOfData() {
57+
return data.length;
58+
}
59+
60+
public Address getContract() {
61+
return this.contract;
62+
}
63+
64+
public Object getIndexed(int idx) {
65+
return this.indexed[idx];
66+
}
67+
public Object getData(int idx) {
68+
return this.data[idx];
69+
}
70+
71+
private boolean match(Event log, boolean ignoreNull) {
72+
if (this == log) {
73+
return true;
74+
}
75+
76+
if (indexed.length != log.indexed.length
77+
|| data.length != log.data.length
78+
) {
79+
return false;
80+
}
81+
if (contract != null) {
82+
if (!contract.equals(log.contract)) {
83+
return false;
84+
}
85+
} else {
86+
if (!ignoreNull && log.contract != null) {
87+
return false;
88+
}
89+
}
90+
for (int i=0 ; i<indexed.length ; i++) {
91+
var v1 = indexed[i];
92+
if (v1==null && ignoreNull) continue;
93+
var v2 = log.indexed[i];
94+
var mo = TypeConverter.toBytes(v1);
95+
var mv = TypeConverter.toBytes(v2);
96+
if (!java.util.Arrays.equals(mo, mv)) {
97+
return false;
98+
}
99+
}
100+
for (int i=0 ; i<data.length ; i++) {
101+
var v1 = data[i];
102+
if (v1==null && ignoreNull) continue;
103+
var v2 = log.data[i];
104+
var mo = TypeConverter.toBytes(v1);
105+
var mv = TypeConverter.toBytes(v2);
106+
if (!java.util.Arrays.equals(mo, mv)) {
107+
return false;
108+
}
109+
}
110+
return true;
111+
}
112+
113+
public boolean match(Event log) {
114+
return this.match(log, true);
115+
}
116+
117+
@Override
118+
public boolean equals(Object log) {
119+
if (log == null || this.getClass() != log.getClass()) {
120+
return false;
121+
}
122+
return this.match((Event)log, false);
123+
}
124+
125+
@Override
126+
public String toString() {
127+
return "Event(caller="+contract
128+
+",indexed="+java.util.Arrays.toString(indexed)
129+
+",data="+java.util.Arrays.toString(data)+")";
130+
}
131+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2023 PARAMETA Corp.
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+
17+
package com.iconloop.score.test;
18+
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Repeatable;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
/**
26+
* {@code @GenerateTScore} is used to generate a wrapper SCORE class for testing.
27+
* If you deploy the generated SCORE class using the unittest framework,
28+
* the following additional features are available.
29+
* <ul>
30+
* <li>Log a proper event on calling the method tagged with {@link score.annotation.EventLog}</li>
31+
* <li>Check if a method is properly tagged with {@link score.annotation.External} and {@link score.annotation.Payable}</li>
32+
* <li>Check if a parameter is properly tagged with {@link score.annotation.Optional}</li>
33+
* </ul>
34+
* For this, the class and its methods should be non-final in order to be extended by the generated SCORE class.
35+
*
36+
* <p>For example, the following is your contract implementation.
37+
* <pre>
38+
* public class MyContract {
39+
* public MyContract() { }
40+
*
41+
* &#64;External
42+
* public void setValue(String value) {
43+
* ...
44+
* }
45+
* }
46+
* </pre>
47+
*
48+
* Then you may deploy the contract with the generated SCORE class in the unittest.
49+
* Note that the default suffix for the generated class is <em>TS</em>.
50+
* <pre>
51+
* public class MyContractTest {
52+
* &#64;Test
53+
* &#64;GenerateTScore(MyContract.class)
54+
* public void testSetValue() {
55+
* var score = sm.deploy(MyContractTS.class);
56+
* }
57+
* }
58+
* </pre>
59+
*
60+
* Of course, you may use the class as is, without any additional features.
61+
*/
62+
@Target({ElementType.TYPE, ElementType.METHOD})
63+
@Retention(RetentionPolicy.SOURCE)
64+
@Repeatable(GenerateTScores.class)
65+
public @interface GenerateTScore {
66+
Class<?> value() default GenerateTScore.class;
67+
String suffix() default "TS";
68+
}

0 commit comments

Comments
 (0)