() {
+
+ @Override
+ protected int compareParameters(MediaType mediaType1, MediaType mediaType2) {
+ double quality1 = mediaType1.getQualityValue();
+ double quality2 = mediaType2.getQualityValue();
+ int qualityComparison = Double.compare(quality2, quality1);
+ if (qualityComparison != 0) {
+ return qualityComparison; // audio/*;q=0.7 < audio/*;q=0.3
+ }
+ return super.compareParameters(mediaType1, mediaType2);
+ }
+ };
+
+ static {
+ // Not using "valueOf' to avoid static init cost
+ ALL = new MediaType("*", "*");
+ APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
+ APPLICATION_CBOR = new MediaType("application", "cbor");
+ APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
+ APPLICATION_GRAPHQL = new MediaType("application", "graphql+json");
+ APPLICATION_JSON = new MediaType("application", "json");
+ APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
+ APPLICATION_NDJSON = new MediaType("application", "x-ndjson");
+ APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream");
+ APPLICATION_PDF = new MediaType("application", "pdf");
+ APPLICATION_PROBLEM_JSON = new MediaType("application", "problem+json");
+ APPLICATION_PROBLEM_JSON_UTF8 = new MediaType("application", "problem+json",
+ StandardCharsets.UTF_8);
+ APPLICATION_PROBLEM_XML = new MediaType("application", "problem+xml");
+ APPLICATION_RSS_XML = new MediaType("application", "rss+xml");
+ APPLICATION_STREAM_JSON = new MediaType("application", "stream+json");
+ APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml");
+ APPLICATION_XML = new MediaType("application", "xml");
+ IMAGE_GIF = new MediaType("image", "gif");
+ IMAGE_JPEG = new MediaType("image", "jpeg");
+ IMAGE_PNG = new MediaType("image", "png");
+ MULTIPART_FORM_DATA = new MediaType("multipart", "form-data");
+ MULTIPART_MIXED = new MediaType("multipart", "mixed");
+ MULTIPART_RELATED = new MediaType("multipart", "related");
+ TEXT_EVENT_STREAM = new MediaType("text", "event-stream");
+ TEXT_HTML = new MediaType("text", "html");
+ TEXT_MARKDOWN = new MediaType("text", "markdown");
+ TEXT_PLAIN = new MediaType("text", "plain");
+ TEXT_XML = new MediaType("text", "xml");
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given primary type.
+ * The {@linkplain #getSubtype() subtype} is set to "*", parameters empty.
+ *
+ * @param type the primary type
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type) {
+ super(type);
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given primary type and subtype.
+ *
The parameters are empty.
+ *
+ * @param type the primary type
+ * @param subtype the subtype
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type, String subtype) {
+ super(type, subtype, Collections.emptyMap());
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given type, subtype, and character set.
+ *
+ * @param type the primary type
+ * @param subtype the subtype
+ * @param charset the character set
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type, String subtype, Charset charset) {
+ super(type, subtype, charset);
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given type, subtype, and quality value.
+ *
+ * @param type the primary type
+ * @param subtype the subtype
+ * @param qualityValue the quality value
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type, String subtype, double qualityValue) {
+ this(type, subtype, Collections.singletonMap(PARAM_QUALITY_FACTOR,
+ Double.toString(qualityValue)));
+ }
+
+ /**
+ * Copy-constructor that copies the type, subtype and parameters of the given
+ * {@code MediaType}, and allows to set the specified character set.
+ *
+ * @param other the other media type
+ * @param charset the character set
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ * @since 4.3
+ */
+ public MediaType(MediaType other, Charset charset) {
+ super(other, charset);
+ }
+
+ /**
+ * Copy-constructor that copies the type and subtype of the given {@code MediaType},
+ * and allows for different parameters.
+ *
+ * @param other the other media type
+ * @param parameters the parameters, may be {@code null}
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(MediaType other, @Nullable Map parameters) {
+ super(other.getType(), other.getSubtype(), parameters);
+ }
+
+
+ /**
+ * Create a new {@code MediaType} for the given type, subtype, and parameters.
+ *
+ * @param type the primary type
+ * @param subtype the subtype
+ * @param parameters the parameters, may be {@code null}
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type, String subtype, @Nullable Map parameters) {
+ super(type, subtype, parameters);
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given {@link MimeType}.
+ * The type, subtype and parameters information is copied and {@code MediaType}-specific
+ * checks on parameters are performed.
+ *
+ * @param mimeType the MIME type
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ * @since 5.3
+ */
+ public MediaType(MimeType mimeType) {
+ super(mimeType);
+ getParameters().forEach(this::checkParameters);
+ }
+
+ /**
+ * Parse the given String value into a {@code MediaType} object,
+ * with this method name following the 'valueOf' naming convention
+ * (as supported by {@link org.springframework.core.convert.ConversionService}.
+ *
+ * @param value the string to parse
+ * @throws InvalidMediaTypeException if the media type value cannot be parsed
+ * @see #parseMediaType(String)
+ */
+ public static MediaType valueOf(String value) {
+ return parseMediaType(value);
+ }
+
+ /**
+ * Parse the given String into a single {@code MediaType}.
+ *
+ * @param mediaType the string to parse
+ * @return the media type
+ * @throws InvalidMediaTypeException if the media type value cannot be parsed
+ */
+ public static MediaType parseMediaType(String mediaType) {
+ MimeType type;
+ try {
+ type = MimeTypeUtils.parseMimeType(mediaType);
+ } catch (InvalidMimeTypeException ex) {
+ throw new InvalidMediaTypeException(ex);
+ }
+ try {
+ return new MediaType(type);
+ } catch (IllegalArgumentException ex) {
+ throw new InvalidMediaTypeException(mediaType, ex.getMessage());
+ }
+ }
+
+ /**
+ * Parse the comma-separated string into a list of {@code MediaType} objects.
+ * This method can be used to parse an Accept or Content-Type header.
+ *
+ * @param mediaTypes the string to parse
+ * @return the list of media types
+ * @throws InvalidMediaTypeException if the media type value cannot be parsed
+ */
+ public static List parseMediaTypes(@Nullable String mediaTypes) {
+ if (!StringUtils.hasLength(mediaTypes)) {
+ return Collections.emptyList();
+ }
+ // Avoid using java.util.stream.Stream in hot paths
+ List tokenizedTypes = MimeTypeUtils.tokenize(mediaTypes);
+ List result = new ArrayList<>(tokenizedTypes.size());
+ for (String type : tokenizedTypes) {
+ if (StringUtils.hasText(type)) {
+ result.add(parseMediaType(type));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Parse the given list of (potentially) comma-separated strings into a
+ * list of {@code MediaType} objects.
+ * This method can be used to parse an Accept or Content-Type header.
+ *
+ * @param mediaTypes the string to parse
+ * @return the list of media types
+ * @throws InvalidMediaTypeException if the media type value cannot be parsed
+ * @since 4.3.2
+ */
+ public static List parseMediaTypes(@Nullable List mediaTypes) {
+ if (CollectionUtils.isEmpty(mediaTypes)) {
+ return Collections.emptyList();
+ } else if (mediaTypes.size() == 1) {
+ return parseMediaTypes(mediaTypes.get(0));
+ } else {
+ List result = new ArrayList<>(8);
+ for (String mediaType : mediaTypes) {
+ result.addAll(parseMediaTypes(mediaType));
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Re-create the given mime types as media types.
+ *
+ * @since 5.0
+ */
+ public static List asMediaTypes(List mimeTypes) {
+ List mediaTypes = new ArrayList<>(mimeTypes.size());
+ for (MimeType mimeType : mimeTypes) {
+ mediaTypes.add(MediaType.asMediaType(mimeType));
+ }
+ return mediaTypes;
+ }
+
+ /**
+ * Re-create the given mime type as a media type.
+ *
+ * @since 5.0
+ */
+ public static MediaType asMediaType(MimeType mimeType) {
+ if (mimeType instanceof MediaType) {
+ return (MediaType) mimeType;
+ }
+ return new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters());
+ }
+
+ /**
+ * Return a string representation of the given list of {@code MediaType} objects.
+ * This method can be used to for an {@code Accept} or {@code Content-Type} header.
+ *
+ * @param mediaTypes the media types to create a string representation for
+ * @return the string representation
+ */
+ public static String toString(Collection mediaTypes) {
+ return MimeTypeUtils.toString(mediaTypes);
+ }
+
+ /**
+ * Sorts the given list of {@code MediaType} objects by specificity.
+ * Given two media types:
+ *
+ * - if either media type has a {@linkplain #isWildcardType() wildcard type},
+ * then the media type without the wildcard is ordered before the other.
+ * - if the two media types have different {@linkplain #getType() types},
+ * then they are considered equal and remain their current order.
+ * - if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype},
+ * then the media type without the wildcard is sorted before the other.
+ * - if the two media types have different {@linkplain #getSubtype() subtypes},
+ * then they are considered equal and remain their current order.
+ * - if the two media types have different {@linkplain #getQualityValue() quality value},
+ * then the media type with the highest quality value is ordered before the other.
+ * - if the two media types have a different amount of
+ * {@linkplain #getParameter(String) parameters}, then the
+ * media type with the most parameters is ordered before the other.
+ *
+ * For example:
+ *
audio/basic < audio/* < */*
+ * audio/* < audio/*;q=0.7; audio/*;q=0.3
+ * audio/basic;level=1 < audio/basic
+ * audio/basic == text/html
+ * audio/basic == audio/wave
+ *
+ * @param mediaTypes the list of media types to be sorted
+ * @see HTTP 1.1: Semantics
+ * and Content, section 5.3.2
+ */
+ public static void sortBySpecificity(List mediaTypes) {
+ Assert.notNull(mediaTypes, "'mediaTypes' must not be null");
+ if (mediaTypes.size() > 1) {
+ mediaTypes.sort(SPECIFICITY_COMPARATOR);
+ }
+ }
+
+ /**
+ * Sorts the given list of {@code MediaType} objects by quality value.
+ * Given two media types:
+ *
+ * - if the two media types have different {@linkplain #getQualityValue() quality value},
+ * then the media type with the highest quality value is ordered before the other.
+ * - if either media type has a {@linkplain #isWildcardType() wildcard type},
+ * then the media type without the wildcard is ordered before the other.
+ * - if the two media types have different {@linkplain #getType() types},
+ * then they are considered equal and remain their current order.
+ * - if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype},
+ * then the media type without the wildcard is sorted before the other.
+ * - if the two media types have different {@linkplain #getSubtype() subtypes},
+ * then they are considered equal and remain their current order.
+ * - if the two media types have a different amount of
+ * {@linkplain #getParameter(String) parameters}, then the
+ * media type with the most parameters is ordered before the other.
+ *
+ *
+ * @param mediaTypes the list of media types to be sorted
+ * @see #getQualityValue()
+ */
+ public static void sortByQualityValue(List mediaTypes) {
+ Assert.notNull(mediaTypes, "'mediaTypes' must not be null");
+ if (mediaTypes.size() > 1) {
+ mediaTypes.sort(QUALITY_VALUE_COMPARATOR);
+ }
+ }
+
+ /**
+ * Sorts the given list of {@code MediaType} objects by specificity as the
+ * primary criteria and quality value the secondary.
+ *
+ * @see MediaType#sortBySpecificity(List)
+ * @see MediaType#sortByQualityValue(List)
+ */
+ public static void sortBySpecificityAndQuality(List mediaTypes) {
+ Assert.notNull(mediaTypes, "'mediaTypes' must not be null");
+ if (mediaTypes.size() > 1) {
+ mediaTypes.sort(
+ MediaType.SPECIFICITY_COMPARATOR.thenComparing(MediaType.QUALITY_VALUE_COMPARATOR));
+ }
+ }
+
+ @Override
+ protected void checkParameters(String parameter, String value) {
+ super.checkParameters(parameter, value);
+ if (PARAM_QUALITY_FACTOR.equals(parameter)) {
+ String unquotedValue = unquote(value);
+ double d = Double.parseDouble(unquotedValue);
+ Assert.isTrue(d >= 0D && d <= 1D,
+ () -> "Invalid quality value \"" + unquotedValue + "\": should be between 0.0 and 1.0");
+ }
+ }
+
+ /**
+ * Return the quality factor, as indicated by a {@code q} parameter, if any.
+ * Defaults to {@code 1.0}.
+ *
+ * @return the quality factor as double value
+ */
+ public double getQualityValue() {
+ String qualityFactor = getParameter(PARAM_QUALITY_FACTOR);
+ return (qualityFactor != null ? Double.parseDouble(unquote(qualityFactor)) : 1D);
+ }
+
+ /**
+ * Indicate whether this {@code MediaType} includes the given media type.
+ * For instance, {@code text/*} includes {@code text/plain} and {@code text/html},
+ * and {@code application/*+xml} includes {@code application/soap+xml}, etc.
+ * This method is not symmetric.
+ *
Simply calls {@link MimeType#includes(MimeType)} but declared with a
+ * {@code MediaType} parameter for binary backwards compatibility.
+ *
+ * @param other the reference media type with which to compare
+ * @return {@code true} if this media type includes the given media type;
+ * {@code false} otherwise
+ */
+ public boolean includes(@Nullable MediaType other) {
+ return super.includes(other);
+ }
+
+ /**
+ * Indicate whether this {@code MediaType} is compatible with the given media type.
+ *
For instance, {@code text/*} is compatible with {@code text/plain},
+ * {@code text/html}, and vice versa. In effect, this method is similar to
+ * {@link #includes}, except that it is symmetric.
+ *
Simply calls {@link MimeType#isCompatibleWith(MimeType)} but declared with a
+ * {@code MediaType} parameter for binary backwards compatibility.
+ *
+ * @param other the reference media type with which to compare
+ * @return {@code true} if this media type is compatible with the given media type;
+ * {@code false} otherwise
+ */
+ public boolean isCompatibleWith(@Nullable MediaType other) {
+ return super.isCompatibleWith(other);
+ }
+
+ /**
+ * Return a replica of this instance with the quality value of the given {@code MediaType}.
+ *
+ * @return the same instance if the given MediaType doesn't have a quality value,
+ * or a new one otherwise
+ */
+ public MediaType copyQualityValue(MediaType mediaType) {
+ if (!mediaType.getParameters().containsKey(PARAM_QUALITY_FACTOR)) {
+ return this;
+ }
+ Map params = new LinkedHashMap<>(getParameters());
+ params.put(PARAM_QUALITY_FACTOR, mediaType.getParameters().get(PARAM_QUALITY_FACTOR));
+ return new MediaType(this, params);
+ }
+
+ /**
+ * Return a replica of this instance with its quality value removed.
+ *
+ * @return the same instance if the media type doesn't contain a quality value,
+ * or a new one otherwise
+ */
+ public MediaType removeQualityValue() {
+ if (!getParameters().containsKey(PARAM_QUALITY_FACTOR)) {
+ return this;
+ }
+ Map params = new LinkedHashMap<>(getParameters());
+ params.remove(PARAM_QUALITY_FACTOR);
+ return new MediaType(this, params);
+ }
+
+}
\ No newline at end of file
diff --git a/framework/src/test/java/org/tron/common/BaseTest.java b/framework/src/test/java/org/tron/common/BaseTest.java
index 552808b842c..dd4400e10f2 100644
--- a/framework/src/test/java/org/tron/common/BaseTest.java
+++ b/framework/src/test/java/org/tron/common/BaseTest.java
@@ -25,6 +25,8 @@
import org.tron.core.config.args.Args;
import org.tron.core.db.Manager;
import org.tron.core.exception.BalanceInsufficientException;
+import org.tron.core.net.peer.PeerConnection;
+import org.tron.core.net.peer.PeerManager;
import org.tron.core.store.AccountStore;
import org.tron.protos.Protocol;
@@ -68,6 +70,12 @@ public static void destroy() {
Args.clearParam();
}
+ public void closePeer() {
+ for (PeerConnection p : PeerManager.getPeers()) {
+ PeerManager.remove(p.getChannel());
+ }
+ }
+
public Protocol.Block getSignedBlock(ByteString witness, long time, byte[] privateKey) {
long blockTime = System.currentTimeMillis() / 3000 * 3000;
if (time != 0) {
diff --git a/framework/src/test/java/org/tron/common/EntityTest.java b/framework/src/test/java/org/tron/common/EntityTest.java
index 483475a453b..bbdc8631225 100644
--- a/framework/src/test/java/org/tron/common/EntityTest.java
+++ b/framework/src/test/java/org/tron/common/EntityTest.java
@@ -5,13 +5,16 @@
import static org.junit.Assert.assertTrue;
import com.google.common.collect.Lists;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import org.apache.commons.collections4.CollectionUtils;
import org.junit.Before;
import org.junit.Test;
import org.tron.common.entity.NodeInfo;
import org.tron.common.entity.NodeInfo.MachineInfo;
import org.tron.common.entity.NodeInfo.MachineInfo.DeadLockThreadInfo;
+import org.tron.common.entity.PeerInfo;
public class EntityTest {
@@ -54,6 +57,9 @@ public void testDeadLockThreadInfo() {
@Test
public void testNodeInfo() {
+ List peerInfoList = new ArrayList<>();
+ peerInfoList.add(getDefaultPeerInfo());
+
NodeInfo nodeInfo = new NodeInfo();
nodeInfo.setTotalFlow(1L);
nodeInfo.setCheatWitnessInfoMap(new HashMap<>());
@@ -62,6 +68,39 @@ public void testNodeInfo() {
nodeInfo.setMachineInfo(machineInfo);
nodeInfo.setBlock("block");
nodeInfo.setSolidityBlock("solidityBlock");
+ nodeInfo.setPeerList(peerInfoList);
nodeInfo.transferToProtoEntity();
}
+
+ private PeerInfo getDefaultPeerInfo() {
+ PeerInfo peerInfo = new PeerInfo();
+ peerInfo.setAvgLatency(peerInfo.getAvgLatency());
+ peerInfo.setBlockInPorcSize(peerInfo.getBlockInPorcSize());
+ peerInfo.setConnectTime(peerInfo.getConnectTime());
+ peerInfo.setDisconnectTimes(peerInfo.getDisconnectTimes());
+ peerInfo.setHeadBlockTimeWeBothHave(peerInfo.getHeadBlockTimeWeBothHave());
+ peerInfo.setHeadBlockWeBothHave(peerInfo.getHeadBlockWeBothHave());
+ peerInfo.setHost("host");
+ peerInfo.setInFlow(peerInfo.getInFlow());
+ peerInfo.setLastBlockUpdateTime(peerInfo.getLastBlockUpdateTime());
+ peerInfo.setLastSyncBlock("last");
+ peerInfo.setLocalDisconnectReason("localDisconnectReason");
+ peerInfo.setNodeCount(peerInfo.getNodeCount());
+ peerInfo.setNodeId("nodeId");
+ peerInfo.setHeadBlockWeBothHave("headBlockWeBothHave");
+ peerInfo.setRemainNum(peerInfo.getRemainNum());
+ peerInfo.setRemoteDisconnectReason("remoteDisconnectReason");
+ peerInfo.setScore(peerInfo.getScore());
+ peerInfo.setPort(peerInfo.getPort());
+ peerInfo.setSyncFlag(peerInfo.isSyncFlag());
+ peerInfo.setNeedSyncFromPeer(peerInfo.isNeedSyncFromPeer());
+ peerInfo.setNeedSyncFromUs(peerInfo.isNeedSyncFromUs());
+ peerInfo.setSyncToFetchSize(peerInfo.getSyncToFetchSize());
+ peerInfo.setSyncToFetchSizePeekNum(peerInfo.getSyncToFetchSizePeekNum());
+ peerInfo.setSyncBlockRequestedSize(peerInfo.getSyncBlockRequestedSize());
+ peerInfo.setUnFetchSynNum(peerInfo.getUnFetchSynNum());
+ peerInfo.setActive(peerInfo.isActive());
+
+ return peerInfo;
+ }
}
diff --git a/framework/src/test/java/org/tron/common/ParameterTest.java b/framework/src/test/java/org/tron/common/ParameterTest.java
index b16be405f61..2f65189ac1c 100644
--- a/framework/src/test/java/org/tron/common/ParameterTest.java
+++ b/framework/src/test/java/org/tron/common/ParameterTest.java
@@ -129,6 +129,12 @@ public void testCommonParameter() {
assertEquals(10, parameter.getMaxConcurrentCallsPerConnection());
parameter.setFlowControlWindow(20);
assertEquals(20, parameter.getFlowControlWindow());
+ assertEquals(0, parameter.getRpcMaxRstStream());
+ parameter.setRpcMaxRstStream(10);
+ assertEquals(10, parameter.getRpcMaxRstStream());
+ assertEquals(0, parameter.getRpcSecondsPerWindow());
+ parameter.setRpcSecondsPerWindow(5);
+ assertEquals(5, parameter.getRpcSecondsPerWindow());
parameter.setMaxConnectionIdleInMillis(1000);
assertEquals(1000, parameter.getMaxConnectionIdleInMillis());
parameter.setBlockProducedTimeOut(500);
diff --git a/framework/src/test/java/org/tron/common/backup/BackupServerTest.java b/framework/src/test/java/org/tron/common/backup/BackupServerTest.java
index c40aca7e17a..18e264eead2 100644
--- a/framework/src/test/java/org/tron/common/backup/BackupServerTest.java
+++ b/framework/src/test/java/org/tron/common/backup/BackupServerTest.java
@@ -10,6 +10,7 @@
import org.junit.rules.Timeout;
import org.tron.common.backup.socket.BackupServer;
import org.tron.common.parameter.CommonParameter;
+import org.tron.common.utils.PublicMethod;
import org.tron.core.Constant;
import org.tron.core.config.args.Args;
@@ -26,7 +27,7 @@ public class BackupServerTest {
@Before
public void setUp() throws Exception {
Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF);
- CommonParameter.getInstance().setBackupPort(80);
+ CommonParameter.getInstance().setBackupPort(PublicMethod.chooseRandomPort());
List members = new ArrayList<>();
members.add("127.0.0.2");
CommonParameter.getInstance().setBackupMembers(members);
diff --git a/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java b/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java
index 4e7d45ee8d7..273672e8342 100644
--- a/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java
+++ b/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.tron.common.utils.client.utils.AbiUtil.generateOccupationConstantPrivateKey;
@@ -67,6 +68,11 @@ public void testFromPrivateKey() {
assertTrue(key.isPubKeyCanonical());
assertTrue(key.hasPrivKey());
assertArrayEquals(pubKey, key.getPubKey());
+
+ key = ECKey.fromPrivate((byte[]) null);
+ assertNull(key);
+ key = ECKey.fromPrivate(new byte[0]);
+ assertNull(key);
}
@Test(expected = IllegalArgumentException.class)
diff --git a/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java b/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java
index b84026d2085..87e4e14698c 100644
--- a/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java
+++ b/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java
@@ -4,6 +4,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.tron.common.utils.client.utils.AbiUtil.generateOccupationConstantPrivateKey;
@@ -64,6 +65,11 @@ public void testFromPrivateKey() {
assertTrue(key.isPubKeyCanonical());
assertTrue(key.hasPrivKey());
assertArrayEquals(pubKey, key.getPubKey());
+
+ key = SM2.fromPrivate((byte[]) null);
+ assertNull(key);
+ key = SM2.fromPrivate(new byte[0]);
+ assertNull(key);
}
@Test(expected = IllegalArgumentException.class)
diff --git a/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java b/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java
index e6bb407bb53..d356e43d66c 100644
--- a/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java
+++ b/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java
@@ -55,18 +55,19 @@ public void publishTrigger() {
public void startSubscribeThread() {
Thread thread = new Thread(() -> {
- ZContext context = new ZContext();
- ZMQ.Socket subscriber = context.createSocket(SocketType.SUB);
+ try (ZContext context = new ZContext()) {
+ ZMQ.Socket subscriber = context.createSocket(SocketType.SUB);
- Assert.assertEquals(true, subscriber.connect(String.format("tcp://localhost:%d", bindPort)));
- Assert.assertEquals(true, subscriber.subscribe(topic));
+ Assert.assertTrue(subscriber.connect(String.format("tcp://localhost:%d", bindPort)));
+ Assert.assertTrue(subscriber.subscribe(topic));
- while (!Thread.currentThread().isInterrupted()) {
- byte[] message = subscriber.recv();
- String triggerMsg = new String(message);
-
- Assert.assertEquals(true, triggerMsg.contains(dataToSend) || triggerMsg.contains(topic));
+ while (!Thread.currentThread().isInterrupted()) {
+ byte[] message = subscriber.recv();
+ String triggerMsg = new String(message);
+ Assert.assertTrue(triggerMsg.contains(dataToSend) || triggerMsg.contains(topic));
+ }
+ // ZMQ.Socket will be automatically closed when ZContext is closed
}
});
thread.start();
diff --git a/framework/src/test/java/org/tron/common/runtime/vm/BatchValidateSignContractTest.java b/framework/src/test/java/org/tron/common/runtime/vm/BatchValidateSignContractTest.java
index fc60d8c0648..c18eb396546 100644
--- a/framework/src/test/java/org/tron/common/runtime/vm/BatchValidateSignContractTest.java
+++ b/framework/src/test/java/org/tron/common/runtime/vm/BatchValidateSignContractTest.java
@@ -15,6 +15,7 @@
import org.tron.core.db.TransactionTrace;
import org.tron.core.vm.PrecompiledContracts;
import org.tron.core.vm.PrecompiledContracts.BatchValidateSign;
+import org.tron.core.vm.config.VMConfig;
@Slf4j
@@ -74,6 +75,13 @@ public void staticCallTest() {
ret = validateMultiSign(hash, signatures, addresses);
Assert.assertEquals(ret.getValue().length, 32);
Assert.assertArrayEquals(ret.getValue(), new byte[32]);
+
+ //after optimized
+ VMConfig.initAllowTvmSelfdestructRestriction(1);
+ ret = validateMultiSign(hash, signatures, addresses);
+ Assert.assertEquals(ret.getValue().length, 32);
+ Assert.assertArrayEquals(ret.getValue(), new byte[32]);
+ VMConfig.initAllowTvmSelfdestructRestriction(0);
System.gc(); // force triggering full gc to avoid timeout for next test
}
diff --git a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java
index f400b3215ee..6fa2801c51f 100644
--- a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java
+++ b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java
@@ -19,9 +19,9 @@
import org.tron.core.Wallet;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
-import org.tron.core.exception.JsonRpcInvalidParamsException;
import org.tron.core.exception.ReceiptCheckErrException;
import org.tron.core.exception.VMIllegalException;
+import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException;
import org.tron.core.services.NodeInfoService;
import org.tron.core.services.jsonrpc.TronJsonRpcImpl;
import org.tron.core.vm.config.ConfigLoader;
diff --git a/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java b/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java
index 3315005b7d2..d6bbdddc854 100644
--- a/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java
+++ b/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java
@@ -1,6 +1,8 @@
package org.tron.common.runtime.vm;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.tron.core.config.Parameter.ChainConstant.FROZEN_PERIOD;
import java.util.List;
import java.util.Random;
@@ -13,25 +15,33 @@
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.tron.common.BaseTest;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.runtime.InternalTransaction;
import org.tron.common.utils.DecodeUtil;
import org.tron.core.Constant;
+import org.tron.core.Wallet;
+import org.tron.core.capsule.AccountCapsule;
import org.tron.core.config.args.Args;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.store.StoreFactory;
import org.tron.core.vm.EnergyCost;
import org.tron.core.vm.JumpTable;
+import org.tron.core.vm.MessageCall;
import org.tron.core.vm.Op;
import org.tron.core.vm.Operation;
+import org.tron.core.vm.OperationActions;
import org.tron.core.vm.OperationRegistry;
+import org.tron.core.vm.PrecompiledContracts;
import org.tron.core.vm.VM;
import org.tron.core.vm.config.ConfigLoader;
import org.tron.core.vm.config.VMConfig;
import org.tron.core.vm.program.Program;
import org.tron.core.vm.program.invoke.ProgramInvokeMockImpl;
+import org.tron.core.vm.repository.Repository;
import org.tron.protos.Protocol;
@Slf4j
@@ -40,6 +50,8 @@ public class OperationsTest extends BaseTest {
private ProgramInvokeMockImpl invoke;
private Program program;
private final JumpTable jumpTable = OperationRegistry.getTable();
+ @Autowired
+ private Wallet wallet;
@BeforeClass
public static void init() {
@@ -749,6 +761,30 @@ public void testPushDupSwapAndLogOperations() throws ContractValidateException {
Assert.assertEquals(2158, program.getResult().getEnergyUsed());
}
+ @Test
+ public void testCallOperations() throws ContractValidateException {
+ invoke = new ProgramInvokeMockImpl();
+ Protocol.Transaction trx = Protocol.Transaction.getDefaultInstance();
+ InternalTransaction interTrx =
+ new InternalTransaction(trx, InternalTransaction.TrxType.TRX_UNKNOWN_TYPE);
+
+ byte prePrefixByte = DecodeUtil.addressPreFixByte;
+ DecodeUtil.addressPreFixByte = Constant.ADD_PRE_FIX_BYTE_MAINNET;
+ VMConfig.initAllowTvmSelfdestructRestriction(1);
+
+ program = new Program(new byte[0], new byte[0], invoke, interTrx);
+ MessageCall messageCall = new MessageCall(
+ Op.CALL, new DataWord(10000),
+ DataWord.ZERO(), DataWord.ZERO(),
+ DataWord.ZERO(), DataWord.ZERO(),
+ DataWord.ZERO(), DataWord.ZERO(),
+ DataWord.ZERO(), false);
+ program.callToPrecompiledAddress(messageCall, new PrecompiledContracts.ECRecover());
+
+ DecodeUtil.addressPreFixByte = prePrefixByte;
+ VMConfig.initAllowTvmSelfdestructRestriction(0);
+ }
+
@Test
public void testOtherOperations() throws ContractValidateException {
invoke = new ProgramInvokeMockImpl();
@@ -886,6 +922,12 @@ public void testSuicideCost() throws ContractValidateException {
Assert.assertEquals(25000, EnergyCost.getSuicideCost2(program));
invoke.getDeposit().createAccount(receiver2, Protocol.AccountType.Normal);
Assert.assertEquals(0, EnergyCost.getSuicideCost2(program));
+
+ byte[] receiver3 = generateRandomAddress();
+ program.stackPush(new DataWord(receiver3));
+ Assert.assertEquals(30000, EnergyCost.getSuicideCost3(program));
+ invoke.getDeposit().createAccount(receiver3, Protocol.AccountType.Normal);
+ Assert.assertEquals(5000, EnergyCost.getSuicideCost3(program));
}
@Test
@@ -911,6 +953,85 @@ public void testSuicideAction() throws ContractValidateException {
VMConfig.initAllowEnergyAdjustment(0);
}
+ @Test
+ public void testCanSuicide2() throws ContractValidateException {
+ VMConfig.initAllowTvmFreeze(1);
+ VMConfig.initAllowTvmFreezeV2(1);
+
+ byte[] contractAddr = Hex.decode("41471fd3ad3e9eeadeec4608b92d16ce6b500704cc");
+ invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], contractAddr);
+
+ program = new Program(null, null, invoke,
+ new InternalTransaction(
+ Protocol.Transaction.getDefaultInstance(),
+ InternalTransaction.TrxType.TRX_UNKNOWN_TYPE));
+ program.getContractState().createAccount(
+ program.getContextAddress(), Protocol.AccountType.Contract);
+ Assert.assertTrue(program.canSuicide2());
+
+ long nowInMs =
+ program.getContractState().getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ long expireTime = nowInMs + FROZEN_PERIOD;
+ AccountCapsule owner = program.getContractState().getAccount(program.getContextAddress());
+ owner.setFrozenForEnergy(1000000, expireTime);
+ program.getContractState().updateAccount(program.getContextAddress(), owner);
+ Assert.assertFalse(program.canSuicide2());
+
+ VMConfig.initAllowTvmFreeze(0);
+ VMConfig.initAllowTvmFreezeV2(0);
+ }
+
+ @Test
+ public void testSuicideAction2() throws ContractValidateException {
+ byte[] contractAddr = Hex.decode("41471fd3ad3e9eeadeec4608b92d16ce6b500704cc");
+ invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], contractAddr);
+ Assert.assertTrue(invoke.getDeposit().isNewContract(contractAddr));
+
+ program = new Program(null, null, invoke,
+ new InternalTransaction(
+ Protocol.Transaction.getDefaultInstance(),
+ InternalTransaction.TrxType.TRX_UNKNOWN_TYPE));
+
+ VMConfig.initAllowEnergyAdjustment(1);
+ VMConfig.initAllowTvmSelfdestructRestriction(1);
+ VMConfig.initAllowTvmFreeze(1);
+ VMConfig.initAllowTvmFreezeV2(1);
+ VMConfig.initAllowTvmCompatibleEvm(1);
+ VMConfig.initAllowTvmVote(1);
+ byte prePrefixByte = DecodeUtil.addressPreFixByte;
+ DecodeUtil.addressPreFixByte = Constant.ADD_PRE_FIX_BYTE_MAINNET;
+
+ program.stackPush(new DataWord(
+ dbManager.getAccountStore().getBlackhole().getAddress().toByteArray()));
+ OperationActions.suicideAction2(program);
+
+ Assert.assertEquals(1, program.getResult().getDeleteAccounts().size());
+
+
+ invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], contractAddr);
+ program = new Program(null, null, invoke,
+ new InternalTransaction(
+ Protocol.Transaction.getDefaultInstance(),
+ InternalTransaction.TrxType.TRX_UNKNOWN_TYPE));
+ Program spyProgram = Mockito.spy(program);
+ Repository realContractState = program.getContractState();
+ Repository spyContractState = Mockito.spy(realContractState);
+ Mockito.when(spyContractState.isNewContract(any(byte[].class))).thenReturn(false);
+ Mockito.when(spyProgram.getContractState()).thenReturn(spyContractState);
+ spyProgram.suicide2(new DataWord(
+ dbManager.getAccountStore().getBlackhole().getAddress().toByteArray()));
+
+ Assert.assertEquals(0, spyProgram.getResult().getDeleteAccounts().size());
+
+ DecodeUtil.addressPreFixByte = prePrefixByte;
+ VMConfig.initAllowEnergyAdjustment(0);
+ VMConfig.initAllowTvmSelfdestructRestriction(0);
+ VMConfig.initAllowTvmFreeze(0);
+ VMConfig.initAllowTvmFreezeV2(0);
+ VMConfig.initAllowTvmCompatibleEvm(0);
+ VMConfig.initAllowTvmVote(0);
+ }
+
@Test
public void testVoteWitnessCost() throws ContractValidateException {
// Build stack environment, the stack from top to bottom is 0x00, 0x80, 0x00, 0x80
diff --git a/framework/src/test/java/org/tron/common/runtime/vm/ValidateMultiSignContractTest.java b/framework/src/test/java/org/tron/common/runtime/vm/ValidateMultiSignContractTest.java
index a688f5f9a29..894022fcea1 100644
--- a/framework/src/test/java/org/tron/common/runtime/vm/ValidateMultiSignContractTest.java
+++ b/framework/src/test/java/org/tron/common/runtime/vm/ValidateMultiSignContractTest.java
@@ -25,6 +25,7 @@
import org.tron.core.config.args.Args;
import org.tron.core.store.StoreFactory;
import org.tron.core.vm.PrecompiledContracts.ValidateMultiSign;
+import org.tron.core.vm.config.VMConfig;
import org.tron.core.vm.repository.Repository;
import org.tron.core.vm.repository.RepositoryImpl;
import org.tron.protos.Protocol;
@@ -123,6 +124,13 @@ public void testDifferentCase() {
validateMultiSign(StringUtil.encode58Check(key.getAddress()), permissionId, data, signs)
.getValue(), DataWord.ONE().getData());
+ //after optimized
+ VMConfig.initAllowTvmSelfdestructRestriction(1);
+ Assert.assertArrayEquals(
+ validateMultiSign(StringUtil.encode58Check(key.getAddress()), permissionId, data, signs)
+ .getValue(), DataWord.ONE().getData());
+ VMConfig.initAllowTvmSelfdestructRestriction(0);
+
//weight not enough
signs = new ArrayList<>();
signs.add(Hex.toHexString(key1.sign(toSign).toByteArray()));
diff --git a/framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java b/framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java
new file mode 100644
index 00000000000..90aac10c0b6
--- /dev/null
+++ b/framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java
@@ -0,0 +1,263 @@
+package org.tron.common.storage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mockStatic;
+import static org.tron.core.db.common.DbSourceInter.ENGINE_FILE;
+import static org.tron.core.db.common.DbSourceInter.ENGINE_KEY;
+import static org.tron.core.db.common.DbSourceInter.LEVELDB;
+import static org.tron.core.db.common.DbSourceInter.ROCKSDB;
+import static org.tron.core.db.common.DbSourceInter.checkOrInitEngine;
+
+import com.google.common.base.Strings;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.tron.common.utils.FileUtil;
+import org.tron.common.utils.PropUtil;
+import org.tron.core.exception.TronError;
+
+
+public class CheckOrInitEngineTest {
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private static final String ACCOUNT = "account";
+
+ @After
+ public void clearMocks() {
+ Mockito.clearAllCaches();
+ }
+
+ @Test
+ public void testLevelDbDetectedWhenExpectingRocksDb() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ File currentFile = new File(dir, "CURRENT");
+ assertTrue(currentFile.createNewFile());
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(ROCKSDB, dir, errCode));
+ assertEquals("Cannot open LEVELDB database with ROCKSDB engine.",
+ exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+ }
+
+ @Test
+ public void testCannotCreateDir() {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class)) {
+ String dir = "/invalid/path/that/cannot/be/created";
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(false);
+ TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT;
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(LEVELDB, dir, errCode));
+ assertEquals("Cannot create dir: " + dir + ".", exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+ }
+
+ @Test
+ public void testCannotCreateEngineFile() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class)) {
+ String dir = temporaryFolder.newFolder().toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(false);
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(ROCKSDB, dir, errCode));
+
+ assertEquals("Cannot create file: " + engineFile + ".", exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+ }
+
+ @Test
+ public void testCannotWritePropertyFile() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder().toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(null);
+ strings.when(() -> Strings.isNullOrEmpty(null)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.writeProperty(engineFile, ENGINE_KEY, ROCKSDB))
+ .thenReturn(false);
+
+ TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT;
+
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(ROCKSDB, dir, errCode));
+
+ assertEquals("Cannot write file: " + engineFile + ".", exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+
+ }
+
+ @Test
+ public void testEngineMismatch() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(LEVELDB);
+ strings.when(() -> Strings.isNullOrEmpty(LEVELDB)).thenReturn(false);
+
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(ROCKSDB, dir, errCode));
+
+ assertEquals("Cannot open LEVELDB database with ROCKSDB engine.",
+ exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+ }
+
+ @Test
+ public void testSuccessfulFirstTimeInit() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY))
+ .thenReturn(null)
+ .thenReturn(LEVELDB);
+ strings.when(() -> Strings.isNullOrEmpty(null)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.writeProperty(engineFile, ENGINE_KEY, LEVELDB))
+ .thenReturn(true);
+
+ TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT;
+ checkOrInitEngine(LEVELDB, dir, errCode);
+ }
+ }
+
+ @Test
+ public void testSuccessfulExistingEngine() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(ROCKSDB);
+ strings.when(() -> Strings.isNullOrEmpty(ROCKSDB)).thenReturn(false);
+
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+ checkOrInitEngine(ROCKSDB, dir, errCode);
+ }
+ }
+
+ @Test
+ /**
+ * 000003.log CURRENT LOCK MANIFEST-000002
+ */
+ public void testCurrentFileExistsWithNoEngineFile() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic