This is an automated email from the ASF dual-hosted git repository.

mmartell pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-native.git


The following commit(s) were added to refs/heads/develop by this push:
     new 18f88e863 GEODE-10218: Allow server debug (#954)
18f88e863 is described below

commit 18f88e8636ec2248a2e8819077ea5b5343b37fb0
Author: Michael Martell <[email protected]>
AuthorDate: Thu May 12 07:08:20 2022 -0700

    GEODE-10218: Allow server debug (#954)
    
    * Support DebugAgent in clicache tests
    * Support DebugAgent in cppcache tests
    * Fix StartLocators bug
    * Use builder pattern in Cluster, Locator and Server classes
---
 clicache/integration-test2/Cluster.cs         |  71 +++++++++++++++--
 clicache/integration-test2/Gfsh.cs            |  14 +++-
 clicache/integration-test2/GfshTest.cs        |  40 +++++++---
 cppcache/integration/framework/Cluster.cpp    |  55 +++++++++-----
 cppcache/integration/framework/Cluster.h      |   7 ++
 cppcache/integration/framework/Gfsh.cpp       |  21 ++++++
 cppcache/integration/framework/Gfsh.h         |   6 ++
 cppcache/integration/test/AllowAttachTest.cpp | 105 ++++++++++++++++++++++++++
 cppcache/integration/test/CMakeLists.txt      |   1 +
 9 files changed, 284 insertions(+), 36 deletions(-)

diff --git a/clicache/integration-test2/Cluster.cs 
b/clicache/integration-test2/Cluster.cs
index 7d2639ccc..5e24e711f 100644
--- a/clicache/integration-test2/Cluster.cs
+++ b/clicache/integration-test2/Cluster.cs
@@ -33,6 +33,7 @@ namespace Apache.Geode.Client.IntegrationTests
         private bool started_;
         private List<Locator> locators_;
         private string name_;
+        private bool useDebugAgent_ = false;
         internal int jmxManagerPort = Framework.FreeTcpPort();
         internal string keyStore_ = Config.SslServerKeyPath + 
"/server_keystore_chained.jks";
         internal string keyStorePassword_ = "apachegeode";
@@ -62,6 +63,13 @@ namespace Apache.Geode.Client.IntegrationTests
             locatorCount_ = locatorCount;
             serverCount_ = serverCount;
             locators_ = new List<Locator>();
+            useDebugAgent_ = false;
+        }
+
+        public Cluster withDebugAgent()
+        {
+          useDebugAgent_ = true;
+          return this;
         }
 
         private bool StartLocators()
@@ -70,8 +78,19 @@ namespace Apache.Geode.Client.IntegrationTests
 
             for (var i = 0; i < locatorCount_; i++)
             {
-                var locator = new Locator(this, new List<Locator>(),
-                    name_ + "/locator/" + i.ToString(), i == 0);
+                var locator = new Locator(this, locators_,
+                    name_ + "/locator/" + i.ToString());
+
+                if (i == 0)
+                {
+                  locator.withJmxManager();
+                }
+
+                if (useDebugAgent_)
+                {
+                    locator.withDebugAgent();
+                }
+
                 locators_.Add(locator);
                 if (locator.Start() != 0 ) {
                     success = false;
@@ -89,6 +108,12 @@ namespace Apache.Geode.Client.IntegrationTests
             {
                 var server = new Server(this, locators_,
                     name_ + "/server/" + i.ToString());
+
+                if (useDebugAgent_)
+                {
+                  server.withDebugAgent();
+                }
+
                 var localResult = server.Start();
                 if (localResult != 0)
                 {
@@ -170,17 +195,31 @@ namespace Apache.Geode.Client.IntegrationTests
         private List<Locator> locators_;
         private bool started_;
         private bool startJmxManager_;
+        private bool useDebugAgent_;
 
-        public Locator(Cluster cluster, List<Locator> locators, string name, 
bool startJmxManager)
+        public Locator(Cluster cluster, List<Locator> locators, string name)
         {
             cluster_ = cluster;
             locators_ = locators;
-            name_ = name;
-            startJmxManager_ = startJmxManager;
             var address = new Address();
             address.address = "localhost";
             address.port = Framework.FreeTcpPort();
             Address = address;
+            name_ = name;
+            startJmxManager_ = false;
+            useDebugAgent_ = false;
+        }
+
+        public Locator withJmxManager()
+        {
+            startJmxManager_ = true;
+            return this;
+        }
+
+        public Locator withDebugAgent()
+        {
+            useDebugAgent_ = true;
+            return this;
         }
 
         public Address Address { get; private set; }
@@ -201,6 +240,12 @@ namespace Apache.Geode.Client.IntegrationTests
                     .withJmxManagerPort(cluster_.jmxManagerPort)
                     .withJmxManagerStart(startJmxManager_)
                     .withHttpServicePort(0);
+
+                if (useDebugAgent_)
+                {
+                    locator.withDebugAgent(Address.address);
+                }
+
                 if (cluster_.UseSSL)
                 {
                    locator
@@ -249,6 +294,7 @@ namespace Apache.Geode.Client.IntegrationTests
         private string name_;
         private List<Locator> locators_;
         private bool started_;
+        private bool useDebugAgent_;
 
         public Server(Cluster cluster, List<Locator> locators, string name)
         {
@@ -259,9 +305,16 @@ namespace Apache.Geode.Client.IntegrationTests
             address.address = "localhost";
             address.port = 0;
             Address = address;
+            useDebugAgent_ = false;
         }
 
-        public Address Address { get; private set; }
+        public Server withDebugAgent()
+        {
+            useDebugAgent_ = true;
+            return this;
+        }
+
+          public Address Address { get; private set; }
 
         public int Start()
         {
@@ -276,6 +329,12 @@ namespace Apache.Geode.Client.IntegrationTests
                     .withBindAddress(Address.address)
                     .withPort(Address.port)
                     .withMaxHeap("1g");
+
+                if (useDebugAgent_)
+                {
+                    server.withDebugAgent(Address.address);
+                }
+
                 if (cluster_.UseSSL)
                 {
                     server
diff --git a/clicache/integration-test2/Gfsh.cs 
b/clicache/integration-test2/Gfsh.cs
index 826c1fda3..b036aebd1 100644
--- a/clicache/integration-test2/Gfsh.cs
+++ b/clicache/integration-test2/Gfsh.cs
@@ -139,6 +139,12 @@ namespace Apache.Geode.Client.IntegrationTests
                     command_ += " --J=-Dgemfire.ssl-enabled-components=" + 
components;
                     return this;
                 }
+
+                public Server withDebugAgent(string bindAddress)
+                {
+                    command_ += " 
--J=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=" + 
bindAddress + ":" + Framework.FreeTcpPort();
+                    return this;
+                }
             }
 
             public Server server()
@@ -187,7 +193,13 @@ namespace Apache.Geode.Client.IntegrationTests
                     command_ += " --J=-Dgemfire.jmx-manager-start=" + (start ? 
"true" : "false");
                     return this;
                 }
-                
+
+                public Locator withDebugAgent(string bindAddress)
+                {
+                    command_ += " 
--J=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=" + 
bindAddress + ":" + Framework.FreeTcpPort();
+                    return this;
+                }
+
                 public Locator withHttpServicePort(int httpServicePort)
                 {
                     command_ += " --http-service-port=" + httpServicePort;
diff --git a/clicache/integration-test2/GfshTest.cs 
b/clicache/integration-test2/GfshTest.cs
index 819bf081f..868beef47 100644
--- a/clicache/integration-test2/GfshTest.cs
+++ b/clicache/integration-test2/GfshTest.cs
@@ -77,14 +77,19 @@ namespace Apache.Geode.Client.IntegrationTests
                 .withSslKeyStore("some/path/keystore.jks")
                 .withSslKeyStorePassword("password1")
                 .withSslTrustStore("some/path/truststore.jks")
-                .withSslTrustStorePassword("password2");
+                .withSslTrustStorePassword("password2")
+                .withDebugAgent("someAddress");
             s = locator.ToString();
+            var startLocatorCommandWithoutDebugAgentPort =
+                s.Substring(0, s.LastIndexOf(":", s.Length - 1, 6));
             Assert.Equal("start locator --name=name --dir=dir 
--bind-address=address --port=420 " +
-                "--J=-Dgemfire.jmx-manager-port=1111 --http-service-port=2222 
--log-level=fine --max-heap=someHugeAmount " +
-                "--connect=false 
--J=-Dgemfire.ssl-enabled-components=locator,jmx " +
-                "--J=-Dgemfire.ssl-keystore=some/path/keystore.jks 
--J=-Dgemfire.ssl-keystore-password=password1 " +
-                "--J=-Dgemfire.ssl-truststore=some/path/truststore.jks 
--J=-Dgemfire.ssl-truststore-password=password2", s);
-        }
+              "--J=-Dgemfire.jmx-manager-port=1111 --http-service-port=2222 
--log-level=fine --max-heap=someHugeAmount " +
+              "--connect=false 
--J=-Dgemfire.ssl-enabled-components=locator,jmx " +
+              "--J=-Dgemfire.ssl-keystore=some/path/keystore.jks 
--J=-Dgemfire.ssl-keystore-password=password1 " +
+              "--J=-Dgemfire.ssl-truststore=some/path/truststore.jks 
--J=-Dgemfire.ssl-truststore-password=password2 " +
+              
"--J=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=someAddress",
+              startLocatorCommandWithoutDebugAgentPort);
+    }
 
         [Fact]
         public void StartServerStringsTest()
@@ -113,13 +118,26 @@ namespace Apache.Geode.Client.IntegrationTests
                 .withSslKeyStore("some/path/keystore.jks")
                 .withSslKeyStorePassword("password1")
                 .withSslTrustStore("some/path/truststore.jks")
-                .withSslTrustStorePassword("password2");
+                .withSslTrustStorePassword("password2")
+                .withDebugAgent("someAddress");
             s = server.ToString();
+            var startServerCommandWithoutDebugAgentPort =
+              s.Substring(0, s.LastIndexOf(":", s.Length-1, 6));
             Assert.Equal("start server --name=server " +
-                "--dir=someDir --bind-address=someAddress --server-port=1234 
--locators=someLocator --log-level=debug " +
-                "--max-heap=1.21gigabytes 
--J=-Dgemfire.ssl-enabled-components=server,locator,jmx " +
-                "--J=-Dgemfire.ssl-keystore=some/path/keystore.jks 
--J=-Dgemfire.ssl-keystore-password=password1 " +
-                "--J=-Dgemfire.ssl-truststore=some/path/truststore.jks 
--J=-Dgemfire.ssl-truststore-password=password2", s);
+                "--dir=someDir " +
+                "--bind-address=someAddress " +
+                "--server-port=1234 " +
+                "--locators=someLocator " +
+                "--log-level=debug " +
+                "--max-heap=1.21gigabytes " +
+                "--J=-Dgemfire.ssl-enabled-components=server,locator,jmx " +
+                "--J=-Dgemfire.ssl-keystore=some/path/keystore.jks " +
+                "--J=-Dgemfire.ssl-keystore-password=password1 " +
+                "--J=-Dgemfire.ssl-truststore=some/path/truststore.jks " +
+                "--J=-Dgemfire.ssl-truststore-password=password2 " +
+                "--J=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n," +
+                    "address=someAddress",
+                startServerCommandWithoutDebugAgentPort);
         }
 
         [Fact]
diff --git a/cppcache/integration/framework/Cluster.cpp 
b/cppcache/integration/framework/Cluster.cpp
index 7a69b7b39..b96ef7d1b 100644
--- a/cppcache/integration/framework/Cluster.cpp
+++ b/cppcache/integration/framework/Cluster.cpp
@@ -93,23 +93,25 @@ void Locator::start(bool startJmxManager) {
                                     std::to_string(locatorAddress.port) + "]";
                  });
 
-  auto locator = cluster_.getGfsh()
-                     .start()
-                     .locator()
-                     .withLogLevel("INFO")
-                     .withDir(name_)
-                     .withName(safeName)
-                     .withBindAddress(locatorAddress_.address)
-                     .withPort(locatorAddress_.port)
-                     .withRemoteLocators(remoteLocators)
-                     .withDistributedSystemId(distributedSystemId_)
-                     .withMaxHeap("256m")
-                     .withJmxManagerPort(jmxManagerPort_)
-                     .withHttpServicePort(0)
-                     .withClasspath(cluster_.getClasspath())
-                     .withSecurityManager(cluster_.getSecurityManager())
-                     .withPreferIPv6(cluster_.getUseIPv6())
-                     .withJmxManagerStart(startJmxManager);
+  auto locator =
+      cluster_.getGfsh()
+          .start()
+          .locator()
+          .withLogLevel("INFO")
+          .withDir(name_)
+          .withName(safeName)
+          .withBindAddress(locatorAddress_.address)
+          .withPort(locatorAddress_.port)
+          .withRemoteLocators(remoteLocators)
+          .withDistributedSystemId(distributedSystemId_)
+          .withMaxHeap("256m")
+          .withJmxManagerPort(jmxManagerPort_)
+          .withHttpServicePort(0)
+          .withClasspath(cluster_.getClasspath())
+          .withSecurityManager(cluster_.getSecurityManager())
+          .withPreferIPv6(cluster_.getUseIPv6())
+          .withDebugAgent(cluster_.getUseDebugAgent(), locatorAddress_.address)
+          .withJmxManagerStart(startJmxManager);
 
   if (cluster_.useSsl()) {
     locator.withConnect(false)
@@ -207,7 +209,8 @@ void Server::start() {
           .withSecurityManager(cluster_.getSecurityManager())
           .withCacheXMLFile(getCacheXMLFile())
           .withConserveSockets(cluster_.getConserveSockets())
-          .withPreferIPv6(cluster_.getUseIPv6());
+          .withPreferIPv6(cluster_.getUseIPv6())
+          .withDebugAgent(cluster_.getUseDebugAgent(), serverAddress_.address);
 
   if (!cluster_.getUser().empty()) {
     server.withUser(cluster_.getUser()).withPassword(cluster_.getPassword());
@@ -279,6 +282,20 @@ Cluster::Cluster(InitialLocators initialLocators, 
InitialServers initialServers,
   removeServerDirectory();
 }
 
+Cluster::Cluster(InitialLocators initialLocators, InitialServers 
initialServers,
+                 UseDebugAgent useDebugAgent)
+    : name_(std::string(::testing::UnitTest::GetInstance()
+                            ->current_test_info()
+                            ->test_suite_name()) +
+            "/" +
+            ::testing::UnitTest::GetInstance()->current_test_info()->name()),
+      initialLocators_(initialLocators.get()),
+      initialServers_(initialServers.get()),
+      jmxManagerPort_(Framework::getAvailablePort()),
+      useDebugAgent_(useDebugAgent.get()) {
+  removeServerDirectory();
+}
+
 Cluster::Cluster(LocatorCount initialLocators, ServerCount initialServers)
     : Cluster(
           InitialLocators{std::vector<LocatorAddress>(initialLocators.get())},
@@ -425,6 +442,8 @@ std::vector<std::string> &Cluster::getCacheXMLFiles() { 
return cacheXMLFiles_; }
 
 bool Cluster::getUseIPv6() { return useIPv6_; }
 
+bool Cluster::getUseDebugAgent() { return useDebugAgent_; }
+
 bool Cluster::getConserveSockets() { return conserveSockets_; }
 
 void Cluster::start() { start(std::function<void()>()); }
diff --git a/cppcache/integration/framework/Cluster.h 
b/cppcache/integration/framework/Cluster.h
index 333297ddd..23103966a 100644
--- a/cppcache/integration/framework/Cluster.h
+++ b/cppcache/integration/framework/Cluster.h
@@ -124,6 +124,7 @@ using Password = NamedType<std::string, struct 
PasswordParameter>;
 using CacheXMLFiles =
     NamedType<std::vector<std::string>, struct CacheXMLFilesParameter>;
 using UseIpv6 = NamedType<bool, struct UseIpv6Parameter>;
+using UseDebugAgent = NamedType<bool, struct UseDebugAgentParameter>;
 using ConserveSockets = NamedType<bool, struct ConserveSocketsParameter>;
 using InitialLocators =
     NamedType<std::vector<LocatorAddress>, struct InitialLocatorsParameter>;
@@ -141,6 +142,9 @@ class Cluster {
   Cluster(InitialLocators initialLocators, InitialServers initialServers,
           UseIpv6 useIpv6 = UseIpv6{false});
 
+  Cluster(InitialLocators initialLocators, InitialServers initialServers,
+          UseDebugAgent useDebugAgent);
+
   Cluster(InitialLocators initialLocators, InitialServers initialServers,
           RemoteLocators remoteLocators,
           DistributedSystemId distributedSystemId);
@@ -238,6 +242,8 @@ class Cluster {
 
   bool getUseIPv6();
 
+  bool getUseDebugAgent();
+
   bool getConserveSockets();
 
  private:
@@ -274,6 +280,7 @@ class Cluster {
   std::string hostName_;
 
   bool useIPv6_ = false;
+  bool useDebugAgent_ = false;
   bool conserveSockets_ = false;
 
   int16_t distributedSystemId_ = 0;
diff --git a/cppcache/integration/framework/Gfsh.cpp 
b/cppcache/integration/framework/Gfsh.cpp
index 4d1280e2a..9b438186b 100644
--- a/cppcache/integration/framework/Gfsh.cpp
+++ b/cppcache/integration/framework/Gfsh.cpp
@@ -20,6 +20,8 @@
 #include <iterator>
 #include <sstream>
 
+#include "Framework.h"
+
 Gfsh::Start Gfsh::start() { return Start{*this}; }
 
 Gfsh::Stop Gfsh::stop() { return Stop{*this}; }
@@ -83,6 +85,16 @@ Gfsh::Start::Locator 
&Gfsh::Start::Locator::withRemoteLocators(
   return *this;
 }
 
+Gfsh::Start::Locator &Gfsh::Start::Locator::withDebugAgent(
+    bool allowAttach, const std::string &bindAddress) {
+  if (allowAttach) {
+    command_ +=
+        " --J=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=" +
+        bindAddress + ":" + std::to_string(Framework::getAvailablePort());
+  }
+  return *this;
+}
+
 Gfsh::Start::Locator &Gfsh::Start::Locator::withDistributedSystemId(
     const uint16_t &dsId) {
   if (dsId != 0) {
@@ -294,6 +306,15 @@ Gfsh::Start::Server 
&Gfsh::Start::Server::withPreferIPv6(bool useIPv6) {
   return *this;
 }
 
+Gfsh::Start::Server &Gfsh::Start::Server::withDebugAgent(
+    bool allowAttach, const std::string &bindAddress) {
+  if (allowAttach) {
+    command_ +=
+        " --J=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=" +
+        bindAddress + ":" + std::to_string(Framework::getAvailablePort());
+  }
+  return *this;
+}
 Gfsh::Start::Server &Gfsh::Start::Server::withSslEnabledComponents(
     const std::string &components) {
   command_ += " --J=-Dgemfire.ssl-enabled-components=" + components;
diff --git a/cppcache/integration/framework/Gfsh.h 
b/cppcache/integration/framework/Gfsh.h
index 02735970f..591e23913 100644
--- a/cppcache/integration/framework/Gfsh.h
+++ b/cppcache/integration/framework/Gfsh.h
@@ -111,6 +111,9 @@ class Gfsh {
       Locator &withRemoteLocators(
           const std::vector<std::string> &remoteLocators);
 
+      Locator &withDebugAgent(const bool allowAttach,
+                              const std::string &bindAddress);
+
       Locator &withDistributedSystemId(const uint16_t &dsId);
 
       Locator &withJmxManagerPort(const uint16_t &jmxManagerPort);
@@ -180,6 +183,9 @@ class Gfsh {
 
       Server &withPreferIPv6(bool useIPv6);
 
+      Server &withDebugAgent(const bool allowAttach,
+                             const std::string &bindAddress);
+
       Server &withSslEnabledComponents(const std::string &components);
 
       Server &withSslKeystore(const std::string &keystore);
diff --git a/cppcache/integration/test/AllowAttachTest.cpp 
b/cppcache/integration/test/AllowAttachTest.cpp
new file mode 100644
index 000000000..7cabc26bb
--- /dev/null
+++ b/cppcache/integration/test/AllowAttachTest.cpp
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <framework/Cluster.h>
+#include <framework/Framework.h>
+#include <framework/Gfsh.h>
+#include <hacks/range.h>
+
+#include <iostream>
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+#include <geode/Cache.hpp>
+#include <geode/PoolManager.hpp>
+#include <geode/QueryService.hpp>
+#include <geode/RegionFactory.hpp>
+#include <geode/RegionShortcut.hpp>
+#include <geode/Struct.hpp>
+
+namespace {
+
+using apache::geode::client::Cache;
+using apache::geode::client::CacheableInt32;
+using apache::geode::client::CacheableString;
+using apache::geode::client::Region;
+using apache::geode::client::RegionShortcut;
+using apache::geode::client::Struct;
+
+std::shared_ptr<Region> setupRegion(Cache& cache) {
+  auto region = cache.createRegionFactory(RegionShortcut::PROXY)
+                    .setPoolName("default")
+                    .create("region");
+
+  return region;
+}
+
+/**
+ * Allow attaching to cluster
+ */
+TEST(AllowAttach, attachToLocatorAndServer) {
+  Cluster cluster{InitialLocators{{{"localhost", 0}}},
+                  InitialServers{{{"localhost", 0}}}, UseDebugAgent(true)};
+  cluster.start();
+  cluster.getGfsh()
+      .create()
+      .region()
+      .withName("region")
+      .withType("REPLICATE")
+      .execute();
+
+  auto cache = cluster.createCache();
+  auto region = setupRegion(cache);
+
+  std::unordered_map<int, std::string> values = {
+      {1, "one"}, {2, "two"}, {3, "three"}};
+
+  for (auto&& value : values) {
+    region->put(value.first, value.second);
+  }
+
+  auto&& queryResult =
+      cache.getQueryService()
+          ->newQuery("SELECT e.key, e.value FROM /region.entries e")
+          ->execute();
+  EXPECT_EQ(3, queryResult->size());
+
+  for (auto&& row : hacks::range(*queryResult)) {
+    auto rowStruct = std::dynamic_pointer_cast<Struct>(row);
+    ASSERT_NE(nullptr, rowStruct);
+    EXPECT_EQ(2, rowStruct->size());
+
+    auto key = -1;
+    for (auto&& column : *rowStruct) {
+      // Expect to read: key:int, value:string
+      if (auto cacheableInt32ColumnValue =
+              std::dynamic_pointer_cast<CacheableInt32>(column)) {
+        key = cacheableInt32ColumnValue->value();
+        EXPECT_NE(values.end(), values.find(key));
+      } else if (auto cacheableStringColumnValue =
+                     std::dynamic_pointer_cast<CacheableString>(column)) {
+        auto value = cacheableStringColumnValue->value();
+        EXPECT_EQ(values.find(key)->second, value);
+      } else {
+        FAIL() << "Column is not int or string.";
+      }
+    }
+  }
+}
+
+}  // namespace
diff --git a/cppcache/integration/test/CMakeLists.txt 
b/cppcache/integration/test/CMakeLists.txt
index ab05a0a06..32dee9c11 100644
--- a/cppcache/integration/test/CMakeLists.txt
+++ b/cppcache/integration/test/CMakeLists.txt
@@ -14,6 +14,7 @@
 # limitations under the License.
 
 add_executable(cpp-integration-test
+  AllowAttachTest.cpp
   AuthInitializeTest.cpp
   BasicIPv6Test.cpp
   CacheWriterTest.cpp

Reply via email to