Skip to content

Commit f7849a3

Browse files
committed
Add unit tests for IRC2Basic
1 parent cf29ed0 commit f7849a3

File tree

14 files changed

+1022
-1
lines changed

14 files changed

+1022
-1
lines changed

settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
rootProject.name = 'javaee-tokens'
2-
include 'tokens'
2+
include 'tokens', 'testsvc'

testsvc/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
plugins {
2+
id 'java-library'
3+
}
4+
5+
dependencies {
6+
compileOnly 'foundation.icon:javaee-api:0.8.6'
7+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2020 ICONLOOP Inc.
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.testsvc;
18+
19+
import score.Address;
20+
21+
import java.math.BigInteger;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
25+
public class Account {
26+
private static final Map<Address, Account> accounts = new HashMap<>();
27+
28+
private final Address address;
29+
private final Map<String, BigInteger> balances = new HashMap<>();
30+
31+
public static Account newExternalAccount(int seed) {
32+
var acct = new Account(0, seed);
33+
accounts.put(acct.getAddress(), acct);
34+
return acct;
35+
}
36+
37+
public static Account newScoreAccount(int seed) {
38+
var acct = new Account(1, seed);
39+
accounts.put(acct.getAddress(), acct);
40+
return acct;
41+
}
42+
43+
public static Account getAccount(Address address) {
44+
return accounts.get(address);
45+
}
46+
47+
private Account(int type, int seed) {
48+
var ba = new byte[Address.LENGTH];
49+
ba[0] = (byte) type;
50+
var index = ba.length - 1;
51+
ba[index--] = (byte) seed;
52+
ba[index--] = (byte) (seed >> 8);
53+
ba[index--] = (byte) (seed >> 16);
54+
ba[index] = (byte) (seed >> 24);
55+
this.address = new Address(ba);
56+
}
57+
58+
public Address getAddress() {
59+
return address;
60+
}
61+
62+
public void addBalance(String symbol, BigInteger value) {
63+
balances.put(symbol, getBalance(symbol).add(value));
64+
}
65+
66+
public void subtractBalance(String symbol, BigInteger value) {
67+
balances.put(symbol, getBalance(symbol).subtract(value));
68+
}
69+
70+
public BigInteger getBalance(String symbol) {
71+
return balances.getOrDefault(symbol, BigInteger.ZERO);
72+
}
73+
74+
public BigInteger getBalance() {
75+
return getBalance("ICX");
76+
}
77+
78+
@Override
79+
public String toString() {
80+
return "Account{" +
81+
"address=" + address +
82+
", balances=" + balances +
83+
'}';
84+
}
85+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2020 ICONLOOP Inc.
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.testsvc;
18+
19+
import score.Address;
20+
21+
import java.lang.reflect.InvocationTargetException;
22+
import java.math.BigInteger;
23+
24+
public class Score extends TestBase {
25+
private static final ServiceManager sm = getServiceManager();
26+
27+
private final Account score;
28+
private final Account owner;
29+
private Object instance;
30+
31+
public Score(Account score, Account owner) {
32+
this.score = score;
33+
this.owner = owner;
34+
}
35+
36+
public Account getAccount() {
37+
return this.score;
38+
}
39+
40+
public Address getAddress() {
41+
return this.score.getAddress();
42+
}
43+
44+
public Account getOwner() {
45+
return this.owner;
46+
}
47+
48+
public void setInstance(Object newInstance) {
49+
this.instance = newInstance;
50+
}
51+
52+
public Object getInstance() {
53+
return this.instance;
54+
}
55+
56+
public Object call(String method, Object... params) {
57+
return call(null, true, BigInteger.ZERO, method, params);
58+
}
59+
60+
public void invoke(Account from, String method, Object... params) {
61+
sm.getBlock().increase();
62+
call(from, false, BigInteger.ZERO, method, params);
63+
}
64+
65+
Object call(Account from, boolean readonly, BigInteger value, String method, Object... params) {
66+
sm.pushFrame(from, this.score, readonly, method, value);
67+
Class<?>[] paramClasses = new Class<?>[params.length];
68+
for (int i = 0; i < params.length; i++) {
69+
Class<?> type = params[i].getClass();
70+
paramClasses[i] = (type == Boolean.class) ? Boolean.TYPE : type;
71+
}
72+
try {
73+
Class<?> clazz = instance.getClass();
74+
var m = clazz.getMethod(method, paramClasses);
75+
return m.invoke(instance, params);
76+
} catch (NoSuchMethodException | IllegalAccessException e) {
77+
e.printStackTrace();
78+
throw new RuntimeException(e.getMessage());
79+
} catch (InvocationTargetException e) {
80+
e.printStackTrace();
81+
throw new AssertionError(e.getTargetException().getMessage());
82+
} finally {
83+
sm.popFrame();
84+
}
85+
}
86+
}
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/*
2+
* Copyright 2020 ICONLOOP Inc.
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.testsvc;
18+
19+
import score.Address;
20+
21+
import java.lang.reflect.Constructor;
22+
import java.lang.reflect.InvocationTargetException;
23+
import java.math.BigInteger;
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
import java.util.Random;
27+
import java.util.Stack;
28+
29+
public class ServiceManager {
30+
private static final BigInteger ICX = BigInteger.TEN.pow(18);
31+
32+
private final Stack<Frame> contexts = new Stack<>();
33+
private final Map<Class<?>, Score> classScoreMap = new HashMap<>();
34+
private final Map<Address, Score> addressScoreMap = new HashMap<>();
35+
private int nextCount = 1;
36+
37+
public Score deploy(Account owner, Class<?> mainClass, Object... params) throws Exception {
38+
getBlock().increase();
39+
var score = new Score(Account.newScoreAccount(nextCount++), owner);
40+
classScoreMap.put(mainClass, score);
41+
addressScoreMap.put(score.getAddress(), score);
42+
pushFrame(owner, score.getAccount(), false, "<init>", BigInteger.ZERO);
43+
try {
44+
Class<?>[] paramClasses = new Class<?>[params.length];
45+
for (int i = 0; i < params.length; i++) {
46+
paramClasses[i] = params[i].getClass();
47+
}
48+
Constructor<?> ctor = mainClass.getConstructor(paramClasses);
49+
score.setInstance(ctor.newInstance(params));
50+
} catch (InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
51+
e.printStackTrace();
52+
throw e;
53+
} finally {
54+
popFrame();
55+
}
56+
return score;
57+
}
58+
59+
public Account createAccount() {
60+
return createAccount(0);
61+
}
62+
63+
public Account createAccount(int initialIcx) {
64+
var acct = Account.newExternalAccount(nextCount++);
65+
acct.addBalance("ICX", ICX.multiply(BigInteger.valueOf(initialIcx)));
66+
return acct;
67+
}
68+
69+
public Address getOwner() {
70+
var address = getCurrentFrame().to.getAddress();
71+
return getScoreFromAddress(address).getOwner().getAddress();
72+
}
73+
74+
public Address getOrigin() {
75+
return getFirstFrame().from.getAddress();
76+
}
77+
78+
public Address getCaller() {
79+
return getCurrentFrame().from.getAddress();
80+
}
81+
82+
public Address getAddress() {
83+
return getCurrentFrame().to.getAddress();
84+
}
85+
86+
private Score getScoreFromClass(Class<?> caller) {
87+
var score = classScoreMap.get(caller);
88+
if (score == null) {
89+
throw new IllegalStateException(caller.getName() + " not found");
90+
}
91+
return score;
92+
}
93+
94+
private Score getScoreFromAddress(Address target) {
95+
var score = addressScoreMap.get(target);
96+
if (score == null) {
97+
throw new IllegalStateException("ScoreNotFound");
98+
}
99+
return score;
100+
}
101+
102+
public Object call(Account from, BigInteger value, Address targetAddress, String method, Object... params) {
103+
Score score = getScoreFromAddress(targetAddress);
104+
return score.call(from, false, value, method, params);
105+
}
106+
107+
public Object call(Class<?> caller, BigInteger value, Address targetAddress, String method, Object... params) {
108+
Score from = getScoreFromClass(caller);
109+
if ("fallback".equals(method) || "".equals(method)) {
110+
transfer(from.getAccount(), targetAddress, value);
111+
return null;
112+
} else {
113+
return call(from.getAccount(), value, targetAddress, method, params);
114+
}
115+
}
116+
117+
public void transfer(Account from, Address targetAddress, BigInteger value) {
118+
getBlock().increase();
119+
var fromBalance = from.getBalance();
120+
if (fromBalance.compareTo(value) < 0) {
121+
throw new IllegalStateException("OutOfBalance");
122+
}
123+
var to = Account.getAccount(targetAddress);
124+
if (to == null) {
125+
throw new IllegalStateException("NoAccount");
126+
}
127+
from.subtractBalance("ICX", value);
128+
to.addBalance("ICX", value);
129+
if (targetAddress.isContract()) {
130+
call(from, value, targetAddress, "fallback");
131+
}
132+
}
133+
134+
public static class Block {
135+
private static Block sInstance;
136+
137+
private long height;
138+
private long timestamp;
139+
140+
public Block(long height, long timestamp) {
141+
this.height = height;
142+
this.timestamp = timestamp;
143+
}
144+
145+
public static Block getInstance() {
146+
if (sInstance == null) {
147+
Random rand = new Random();
148+
sInstance = new Block(rand.nextInt(1000), System.nanoTime() * 1000);
149+
}
150+
return sInstance;
151+
}
152+
153+
public long getHeight() {
154+
return height;
155+
}
156+
157+
public long getTimestamp() {
158+
return timestamp;
159+
}
160+
161+
public void increase() {
162+
increase(1);
163+
}
164+
165+
public void increase(long delta) {
166+
height += delta;
167+
timestamp = System.nanoTime() * 1000;
168+
}
169+
}
170+
171+
public Block getBlock() {
172+
return Block.getInstance();
173+
}
174+
175+
public static class Frame {
176+
Account from;
177+
Account to;
178+
String method;
179+
boolean readonly;
180+
BigInteger value;
181+
182+
public Frame(Account from, Account to, boolean readonly, String method, BigInteger value) {
183+
this.from = from;
184+
this.to = to;
185+
this.readonly = readonly;
186+
this.method = method;
187+
this.value = value;
188+
}
189+
190+
public boolean isReadonly() {
191+
return readonly;
192+
}
193+
194+
public BigInteger getValue() {
195+
return value;
196+
}
197+
}
198+
199+
protected void pushFrame(Account from, Account to, boolean readonly, String method, BigInteger value) {
200+
contexts.push(new Frame(from, to, readonly, method, value));
201+
}
202+
203+
protected void popFrame() {
204+
contexts.pop();
205+
}
206+
207+
public Frame getCurrentFrame() {
208+
return contexts.peek();
209+
}
210+
211+
public Frame getFirstFrame() {
212+
return contexts.firstElement();
213+
}
214+
}

0 commit comments

Comments
 (0)