Skip to content

Commit bee80ad

Browse files
committed
Bug 36572422 - Validate serialization of 4-byte UTF-8 sequences in C++ client
[git-p4: depot-paths = "//dev/main.cpp/": change = 110856]
1 parent c779885 commit bee80ad

File tree

4 files changed

+255
-14
lines changed

4 files changed

+255
-14
lines changed

prj/tests/functional/defaultconfig/server-pof-config.xml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0"?>
22
<!--
3-
Copyright (c) 2000, 2020, Oracle and/or its affiliates.
3+
Copyright (c) 2000, 2024, Oracle and/or its affiliates.
44
55
Licensed under the Universal Permissive License v 1.0 as shown at
6-
http://oss.oracle.com/licenses/upl.
6+
https://oss.oracle.com/licenses/upl.
77
-->
88

99
<!DOCTYPE pof-config SYSTEM "pof-config.dtd">
@@ -59,6 +59,12 @@
5959
<type-id>1512</type-id>
6060
<class-name>coherence.tests.ProcessorPrintUUIDTimestamp</class-name>
6161
</user-type>
62+
63+
<user-type>
64+
<type-id>1513</type-id>
65+
<class-name>coherence.tests.TestUtf8Processor</class-name>
66+
</user-type>
67+
6268
<user-type>
6369
<type-id>1600</type-id>
6470
<class-name>coherence.tests.ProxyControlInvocable</class-name>
@@ -119,7 +125,7 @@
119125
<class-name>coherence.tests.TestTransformer</class-name>
120126
</user-type>
121127

122-
<!-- KeyAssociate-related test classes -->
128+
<!-- KeyAssociation-related test classes -->
123129

124130
<user-type>
125131
<type-id>4001</type-id>
@@ -150,7 +156,6 @@
150156
<type-id>4006</type-id>
151157
<class-name>coherence.tests.Part</class-name>
152158
</user-type>
153-
154159
</user-type-list>
155160
<enable-references>true</enable-references>
156161
</pof-config>

tests/common/include/common/TestClasses.hpp

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
2-
* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
2+
* Copyright (c) 2000, 2024, Oracle and/or its affiliates.
33
*
44
* Licensed under the Universal Permissive License v 1.0 as shown at
5-
* http://oss.oracle.com/licenses/upl.
5+
* https://oss.oracle.com/licenses/upl.
66
*/
77
#ifndef COH_TEST_CLASSES_HPP
88
#define COH_TEST_CLASSES_HPP
@@ -18,11 +18,18 @@
1818
#include "coherence/net/DefaultConfigurableCacheFactory.hpp"
1919
#include "coherence/net/AbstractInvocable.hpp"
2020
#include "coherence/net/InvocationService.hpp"
21+
2122
#include "coherence/net/cache/KeyAssociation.hpp"
2223

24+
#include "coherence/util/Filter.hpp"
25+
#include "coherence/util/Iterator.hpp"
2326
#include "coherence/util/QueryMap.hpp"
2427
#include "coherence/util/ValueExtractor.hpp"
2528

29+
#include "coherence/util/processor/AbstractProcessor.hpp"
30+
31+
#include "common/TestUtils.hpp"
32+
2633
COH_OPEN_NAMESPACE2(common,test)
2734

2835
using coherence::io::pof::PofReader;
@@ -33,8 +40,11 @@ using coherence::net::CacheFactory;
3340
using coherence::net::DefaultConfigurableCacheFactory;
3441
using coherence::net::InvocationService;
3542
using coherence::net::cache::KeyAssociation;
43+
using coherence::util::Filter;
44+
using coherence::util::Iterator;
3645
using coherence::util::QueryMap;
3746
using coherence::util::ValueExtractor;
47+
using coherence::util::processor::AbstractProcessor;
3848

3949
class TestQueryMapEntry :
4050
public class_spec<TestQueryMapEntry,
@@ -516,16 +526,16 @@ class ExampleAddress
516526
* @param vsState State name
517527
* @param vsZip Zip (postal) code
518528
*/
519-
ExampleAddress(String::View vsStreet1 = String::null_string,
520-
String::View vsStreet2 = String::null_string,
529+
ExampleAddress(String::View vsStreet1 = String::null_string,
530+
String::View vsStreet2 = String::null_string,
521531
String::View vsCity = String::null_string,
522532
String::View vsState = String::null_string,
523533
String::View vsZip = String::null_string,
524534
String::View vsCountry = String::null_string)
525535
: m_vsStreet1(self(), vsStreet1),
526536
m_vsStreet2(self(), vsStreet2),
527537
m_vsCity(self(), vsCity),
528-
m_vsState(self(), vsState),
538+
m_vsState(self(), vsState),
529539
m_vsZip(self(), vsZip),
530540
m_vsCountry(self(), vsCountry)
531541
{
@@ -860,7 +870,7 @@ class ExampleAddress
860870
* @since Coherence 3.7.1.10
861871
* @author par 7/24/13
862872
*/
863-
class TestContact
873+
class TestContact
864874
: public class_spec<TestContact,
865875
extends<Object>,
866876
implements<PortableObject, Comparable> >
@@ -879,7 +889,7 @@ class TestContact
879889
* @param sState the state where the person lives
880890
* @param sZip the zip code of the city where the person lives
881891
*/
882-
TestContact(String::View vsFirstName,
892+
TestContact(String::View vsFirstName,
883893
String::View vsLastName,
884894
ExampleAddress::View vHomeAddress)
885895
: m_vsFirstName(self(), vsFirstName),
@@ -1117,7 +1127,7 @@ class FilterFetcher
11171127
* @inheritDoc
11181128
*/
11191129
void readExternal(PofReader::Handle hIn)
1120-
{
1130+
{
11211131
m_fFetchExtractor = hIn->readBoolean(0);
11221132
m_vQuery = hIn->readString(1);
11231133
}
@@ -1156,7 +1166,7 @@ class FilterFetcher
11561166
* @since Coherence 3.7.1.10
11571167
* @par 7/24/13
11581168
*/
1159-
class FilterFactory
1169+
class FilterFactory
11601170
: public class_spec<FilterFactory,
11611171
extends<Object> >
11621172
{
@@ -1202,7 +1212,7 @@ class FilterFactory
12021212
*
12031213
* @param sQuery the query string.
12041214
*/
1205-
static ValueExtractor::View createExtractor(String::View vsQuery, String::View vsServiceName)
1215+
static ValueExtractor::View createExtractor(String::View vsQuery, String::View vsServiceName)
12061216
{
12071217
InvocationService::Handle hService = cast<InvocationService::Handle>
12081218
(CacheFactory::getService(vsServiceName));
@@ -1221,6 +1231,48 @@ class FilterFactory
12211231
}
12221232
};
12231233

1234+
/**
1235+
* The TestUtf8Processor is used to validate UTF-8 serialization.
1236+
* <p>
1237+
* The validation logic is in the corresponding Java class.
1238+
*
1239+
* @since 14.1.2.0.0
1240+
**/
1241+
class TestUtf8Processor
1242+
: public class_spec<TestUtf8Processor,
1243+
extends<AbstractProcessor>,
1244+
implements<PortableObject> >
1245+
{
1246+
friend class factory<TestUtf8Processor>;
1247+
1248+
// ----- constructors ---------------------------------------------------
1249+
1250+
public:
1251+
/**
1252+
* Default constructor.
1253+
*/
1254+
TestUtf8Processor()
1255+
{ }
1256+
1257+
// ----- PortableObject interface -------------------------------------------
1258+
1259+
public:
1260+
/**
1261+
* @inheritDoc
1262+
*/
1263+
void readExternal(PofReader::Handle /* hIn */)
1264+
{
1265+
}
1266+
1267+
/**
1268+
* @inheritDoc
1269+
*/
1270+
void writeExternal(PofWriter::Handle /* hOut */) const
1271+
{
1272+
}
1273+
};
1274+
COH_REGISTER_PORTABLE_CLASS(1513, TestUtf8Processor);
1275+
12241276
COH_CLOSE_NAMESPACE2
12251277

12261278
#endif
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Universal Permissive License v 1.0 as shown at
5+
* https://oss.oracle.com/licenses/upl.
6+
*/
7+
package coherence.tests;
8+
9+
import com.tangosol.io.pof.PofReader;
10+
import com.tangosol.io.pof.PofWriter;
11+
import com.tangosol.io.pof.PortableObject;
12+
13+
import com.tangosol.net.CacheFactory;
14+
15+
import com.tangosol.util.Binary;
16+
import com.tangosol.util.BinaryEntry;
17+
import com.tangosol.util.ExternalizableHelper;
18+
import com.tangosol.util.InvocableMap;
19+
20+
import com.tangosol.util.processor.AbstractProcessor;
21+
22+
import java.io.IOException;
23+
24+
import java.nio.ByteBuffer;
25+
26+
import java.nio.charset.StandardCharsets;
27+
28+
/**
29+
* The TestUtf8Processor is used to validate UTF-8 serialization.
30+
*
31+
* @author phf 2024.08.27
32+
* @since 14.1.2.0.0
33+
*/
34+
public class TestUtf8Processor
35+
extends AbstractProcessor
36+
implements PortableObject
37+
{
38+
// ----- constructors ---------------------------------------------------
39+
40+
public TestUtf8Processor()
41+
{
42+
}
43+
44+
// ----- EntryProcessor implementation ----------------------------------
45+
46+
@Override
47+
public Object process(InvocableMap.Entry entry)
48+
{
49+
// string initialization copied from Java ExternalizableHelperTest
50+
String sExpected = new String(toBytes(new int[] {0xf0938080, 0xf09f8ebf, 0xf09f8f80, 0xf09f8e89, 0xf09f9294}),
51+
StandardCharsets.UTF_8);
52+
BinaryEntry binEntry = (BinaryEntry) entry;
53+
Binary binValue = ExternalizableHelper.getUndecorated(binEntry.getBinaryValue());
54+
Binary binExpected = ExternalizableHelper.getUndecorated((Binary) binEntry.getContext()
55+
.getValueToInternalConverter()
56+
.convert(sExpected));
57+
58+
// use CacheFactory instead of Logger to be able to test with older Coherence versions
59+
CacheFactory.log("TestUtf8Processor: this entry's binary value: " + binValue, CacheFactory.LOG_INFO);
60+
CacheFactory.log("TestUtf8Processor: serialized sExpected : " + binExpected, CacheFactory.LOG_INFO);
61+
62+
// Logger.info("*** this entry's deserialized value: " + entry.getValue());
63+
// Logger.info("*** sExpected : " + sExpected);
64+
65+
// Coherence versions prior to 14.1.2 will use the old encoding
66+
boolean fVersionCompatible = binEntry.getContext().getCacheService().isVersionCompatible(14,1,2,0,0);
67+
CacheFactory.log("TestUtf8Processor: isVersionCompatible(14,1,2,0,0)=" + fVersionCompatible);
68+
69+
return Boolean.valueOf(fVersionCompatible ? binExpected.equals(binValue) : true);
70+
}
71+
72+
// ----- PortableObject implementation ----------------------------------
73+
74+
@Override
75+
public void readExternal(PofReader in)
76+
throws IOException
77+
{
78+
}
79+
80+
@Override
81+
public void writeExternal(PofWriter out)
82+
throws IOException
83+
{
84+
}
85+
86+
// ----- helper methods -------------------------------------------------
87+
88+
private static byte[] toBytes(int[] ai)
89+
{
90+
ByteBuffer buf = ByteBuffer.allocate(4 * ai.length);
91+
for (int n : ai)
92+
{
93+
buf.putInt(n);
94+
}
95+
return buf.array();
96+
}
97+
98+
// ----- data members ---------------------------------------------------
99+
100+
private int m_nId;
101+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Universal Permissive License v 1.0 as shown at
5+
* https://oss.oracle.com/licenses/upl.
6+
*/
7+
#include "cxxtest/TestSuite.h"
8+
9+
#include "coherence/lang.ns"
10+
11+
#include "coherence/net/CacheFactory.hpp"
12+
#include "coherence/net/NamedCache.hpp"
13+
14+
#include "common/TestClasses.hpp"
15+
16+
using coherence::net::CacheFactory;
17+
using coherence::net::NamedCache;
18+
using common::test::TestUtf8Processor;
19+
20+
/**
21+
* Verify that UTF-8 POF serialization matches that of Java.
22+
*/
23+
class Utf8Test : public CxxTest::TestSuite
24+
{
25+
public:
26+
/**
27+
* Insert a String with 4 byte UTF-8 encoded characters and then compare the entry
28+
* using Java POF encoding.
29+
*/
30+
void test4ByteUtf8()
31+
{
32+
NamedCache::Handle hCache = ensureCleanCache("dist-cache");
33+
34+
// create UTF-8 4-byte characters: 0xf0938080, 0xf09f8ebf, 0xf09f8f80, 0xf09f8e89, 0xf09f9294
35+
char five[21];
36+
// 0xf0938080
37+
five[0] = (char) 0xf0;
38+
five[1] = (char) 0x93;
39+
five[2] = (char) 0x80;
40+
five[3] = (char) 0x80;
41+
// 0xf09f8ebf
42+
five[4] = (char) 0xf0;
43+
five[5] = (char) 0x9f;
44+
five[6] = (char) 0x8e;
45+
five[7] = (char) 0xbf;
46+
// 0xf09f8f80
47+
five[8] = (char) 0xf0;
48+
five[9] = (char) 0x9f;
49+
five[10] = (char) 0x8f;
50+
five[11] = (char) 0x80;
51+
// 0xf09f8e89
52+
five[12] = (char) 0xf0;
53+
five[13] = (char) 0x9f;
54+
five[14] = (char) 0x8e;
55+
five[15] = (char) 0x89;
56+
// 0xf09f9294
57+
five[16] = (char) 0xf0;
58+
five[17] = (char) 0x9f;
59+
five[18] = (char) 0x92;
60+
five[19] = (char) 0x94;
61+
five[20] = '\0';
62+
std::string data(five);
63+
64+
Integer32::View vNid = Integer32::valueOf(1);
65+
String::View vsTest = String::create(data);;
66+
67+
hCache->put(vNid, vsTest);
68+
69+
// compare the Java serialized value to the C++ serialized value
70+
Object::View vResult = hCache->invoke(vNid, TestUtf8Processor::create());
71+
72+
TS_ASSERT(vResult->equals(Boolean::valueOf(true)));
73+
}
74+
75+
/**
76+
* Clean up after the tests - Sunpro compiler does not like cxxtest
77+
* createSuite() and destroySuite() methods so need to do it this way
78+
*/
79+
void testCleanup()
80+
{
81+
CacheFactory::shutdown();
82+
}
83+
};

0 commit comments

Comments
 (0)