18228 - create a test for Asynchronous RAD Requests and Events.
18229 - create a test for Synchronous RAD requests and Events.
--- a/usr/src/apis/test.xml Tue May 17 10:07:43 2011 -0400
+++ b/usr/src/apis/test.xml Wed May 18 12:42:36 2011 -0400
@@ -196,4 +196,18 @@
<version major="0" minor="1" stability="private" />
<property type="boolean" name="true" access="ro" />
</api>
+
+ <api name="RadRequest">
+ <version major="0" minor="1" stability="private"/>
+ <event name="tick" type="integer"/>
+
+ <method name="sleep">
+ <error/>
+ <argument name="milliseconds" type="integer"/>
+ </method>
+
+ <method name="eventsStart"/>
+
+ <method name="eventsStop"/>
+ </api>
</interface>
--- a/usr/src/cmd/rad/mod/test/Makefile Tue May 17 10:07:43 2011 -0400
+++ b/usr/src/cmd/rad/mod/test/Makefile Wed May 18 12:42:36 2011 -0400
@@ -26,7 +26,7 @@
include ../Makefile.env
MOD_APIS=test
-MOD_OBJS=mod_test.o properties.o addremove.o list.o derived.o
+MOD_OBJS=mod_test.o properties.o addremove.o list.o derived.o rad_request.o
MOD_LIBNAME=mod_test.so
MOD_INSTALLDIR=$(RADDIR_MODULE)
--- a/usr/src/cmd/rad/mod/test/mod_test.c Tue May 17 10:07:43 2011 -0400
+++ b/usr/src/cmd/rad/mod/test/mod_test.c Wed May 18 12:42:36 2011 -0400
@@ -27,6 +27,7 @@
#include <limits.h>
#include <string.h>
#include <rad/adr.h>
+
#include <rad/rad_modapi.h>
#include "api_test.h"
@@ -234,6 +235,8 @@
adr_name_t *addremove_template;
extern conerr_t list_populate(void);
+extern rad_container_t rad_container_auth;
+extern rad_container_t rad_container_unauth;
int
_rad_init(void *handle)
@@ -263,6 +266,21 @@
1, "type", "addremove"),
&api_AddRemove_svr);
+ adr_name_t *rr_name = adr_name_vcreate("org.opensolaris.os.rad.test",
+ 1, "type", "radRequest");
+ rad_instance_t *inst = instance_create(adr_name_hold(rr_name),
+ &api_RadRequest_svr, NULL, NULL);
+ rad_instance_t *uinst = instance_create(adr_name_hold(rr_name),
+ &api_RadRequest_svr, NULL, NULL);
+
+ conerr_t err = ce_nomem;
+ if (inst == NULL || uinst == NULL ||
+ (err = cont_insert(&rad_container_auth, inst, INST_ID_PICK)) !=
+ ce_ok ||
+ (err = cont_insert(&rad_container_unauth, uinst, INST_ID_PICK))
+ != ce_ok)
+ rad_log(RL_WARN, "Unable to install test objects : %d", err);
+
(void) list_populate();
return (0);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/rad/mod/test/rad_request.c Wed May 18 12:42:36 2011 -0400
@@ -0,0 +1,104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <unistd.h>
+#include "api_test.h"
+
+#define INTERVAL 300000
+
+pthread_mutex_t events_lock = PTHREAD_MUTEX_INITIALIZER;
+static boolean_t go = B_FALSE;
+static boolean_t started = B_FALSE;
+
+static boolean_t
+should_go()
+{
+ rad_mutex_enter(&events_lock);
+ boolean_t result = go;
+ rad_mutex_exit(&events_lock);
+
+ return (result);
+}
+
+static void
+generate_events(void *args)
+{
+ rad_instance_t *inst = (rad_instance_t *)args;
+ long seq = 0;
+ while (should_go()) {
+ data_t *result = data_new_integer(seq);
+
+ instance_notify(inst, "tick", seq++, result);
+ (void) usleep(INTERVAL);
+ }
+
+ rad_mutex_enter(&events_lock);
+ started = B_FALSE;
+ rad_mutex_exit(&events_lock);
+
+ instance_rele(inst);
+}
+
+conerr_t
+api_RadRequest_invoke_sleep(rad_instance_t *inst, adr_method_t *meth,
+ data_t **rtnval, data_t **args, int count, data_t **err)
+{
+ int milliseconds = data_to_integer(args[0]);
+ if (milliseconds <= 0)
+ return (ce_object);
+
+ (void) usleep(milliseconds * 1000);
+ return (ce_ok);
+}
+
+conerr_t
+api_RadRequest_invoke_eventsStart(rad_instance_t *inst, adr_method_t *meth,
+ data_t **rtnval, data_t **args, int count, data_t **error)
+{
+ rad_mutex_enter(&events_lock);
+ if (!started) {
+ instance_hold(inst);
+
+ if (rad_thread_create_async(generate_events, inst) != rm_ok)
+ rad_log(RL_FATAL, "unable to create events thread.");
+
+ go = B_TRUE;
+ started = B_TRUE;
+ }
+ rad_mutex_exit(&events_lock);
+ return (ce_ok);
+}
+
+conerr_t
+api_RadRequest_invoke_eventsStop(rad_instance_t *inst, adr_method_t *meth,
+ data_t **rtnval, data_t **args, int count, data_t **error)
+{
+ rad_mutex_enter(&events_lock);
+ if (started) {
+ go = B_FALSE;
+ }
+ rad_mutex_exit(&events_lock);
+ return (ce_ok);
+}
--- a/usr/src/test/assertions/asynchronous.txt Tue May 17 10:07:43 2011 -0400
+++ b/usr/src/test/assertions/asynchronous.txt Wed May 18 12:42:36 2011 -0400
@@ -40,4 +40,5 @@
Assertion: asyncStress
Description: Stresses multiple simultaneous requests of differing
- durations.
+ durations. All requests should be handled and in a reasonable
+ amount of time.
--- a/usr/src/test/assertions/synchronous.txt Tue May 17 10:07:43 2011 -0400
+++ b/usr/src/test/assertions/synchronous.txt Wed May 18 12:42:36 2011 -0400
@@ -35,5 +35,5 @@
Description: Requests made while a long-running request is being
processed are properly queued and handled.
- Assertion: syncEvents
+ Assertion: syncEventWhileSlow
Description: Events are delivered during long-running request.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/java/src/client/RadRequestAsyncTest.java Wed May 18 12:42:36 2011 -0400
@@ -0,0 +1,127 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package client;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opensolaris.os.rad.test.RadRequestMXBean;
+import testutil.Desc;
+
+import static org.junit.Assert.*;
+
+public class RadRequestAsyncTest extends RadRequestBase {
+
+ @Before
+ public void setUp() throws Exception {
+ // allow anonymous connections i.e. -t tcp:noauth,port=...
+ setUp("noauth,");
+ }
+
+ @Test(timeout = SLOW + PAUSE + TO_PADDING)
+ @Desc("Fast requests made while a long-running request is being " +
+ "return before the long-running request returns.")
+ public void asyncFastWhileSlow() throws Exception {
+ RadRequestThread tslow = new RadRequestThread(mbean_, SLOW);
+ RadRequestThread tfast = new RadRequestThread(mbean_, FAST);
+
+ tslow.start();
+ Thread.sleep(PAUSE);
+ tfast.start();
+
+ tslow.join();
+ tfast.join();
+
+ // test normal completion of both requests.
+ assertTrue(tslow.isNormalCompletion() && tfast.isNormalCompletion());
+
+ // are the requests taking as long as they should.
+ assertTrue((tslow.getExecutionTime() >= SLOW) &&
+ (tslow.getExecutionTime() <= SLOW + EPSILON));
+ assertTrue((tfast.getExecutionTime() >= FAST) &&
+ (tfast.getExecutionTime() <= FAST + EPSILON));
+
+ // tfast should return approximately SLOW - (FAST + PAUSE) give or take
+ // EPSILON earlier than tslow.
+ long diff =
+ tslow.getEndTime().getTime() - tfast.getEndTime().getTime();
+ assertTrue((diff > SLOW - (FAST + PAUSE) - EPSILON) &&
+ (diff < SLOW - (FAST + PAUSE) + EPSILON));
+ }
+
+ @Test(timeout = SLOW + TO_PADDING)
+ @Desc("Events are delivered during long-running request.")
+ public void asyncEventWhileSlow() throws Exception {
+ runEventsTest();
+ }
+
+ @Test(timeout = (SLOW + TO_PADDING) * 2)
+ @Desc("Stresses multiple requests of differing durations to see if they " +
+ "are all handled.")
+ public void asyncStressHandled() throws Exception {
+ RadRequestThread [] thread = asyncStress(MAX_THREADS * 2);
+
+ for (RadRequestThread t : thread) {
+ if (t.getExecutionTime() < t.getDuration())
+ fail("The request was not handled successfully.");
+ }
+ }
+
+ @Test(timeout = SLOW + TO_PADDING)
+ @Desc("Stresses multiple requests of differing durations to see if they " +
+ "are handled in a timely manner.")
+ public void asyncStressLatency() throws Exception {
+ RadRequestThread [] thread = asyncStress(MAX_THREADS);
+
+ for (RadRequestThread t : thread) {
+ assertTrue((t.getExecutionTime() >= t.getDuration()) &&
+ (t.getExecutionTime() <= (t.getDuration() + EPSILON)));
+ }
+ }
+
+ /**
+ * simultaneously submit the requested number of requests to the server.
+ * @param - number of requests to submit
+ */
+ private RadRequestThread [] asyncStress(int reqs) throws Exception {
+ RadRequestThread [] thread = new RadRequestThread[reqs];
+
+ for (int i = 0; i < reqs; i++) {
+ int duration = FAST + (int) (Math.random() * (SLOW - FAST));
+
+ thread[i] = new RadRequestThread(mbean_, duration);
+ }
+
+ for (Thread t : thread) {
+ t.start();
+ }
+
+ for (Thread t : thread) {
+ t.join();
+ }
+
+ return thread;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/java/src/client/RadRequestBase.java Wed May 18 12:42:36 2011 -0400
@@ -0,0 +1,171 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package client;
+
+import common.MBeanTestSingle;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import javax.management.MBeanServerConnection;
+import javax.management.Notification;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opensolaris.os.adr.Stability;
+import org.opensolaris.os.rad.api.pam.AuthenticatorMXBean;
+import org.opensolaris.os.rad.jmx.RadJMX;
+import org.opensolaris.os.rad.jmx.RadNotification;
+import org.opensolaris.os.rad.test.RadRequestMXBean;
+import testutil.Desc;
+
+import static org.junit.Assert.*;
+
+public abstract class RadRequestBase
+ extends MBeanTestSingle<RadRequestMXBean>
+ implements NotificationListener, NotificationFilter {
+
+ // durations in milliseconds
+ public static final int FAST = 300;
+ public static final int SLOW = 1500;
+ public static final int PAUSE = 200;
+ public static final int EPSILON = 50;
+ public static final int INTERVAL = 300;
+ public static final int TO_PADDING = 1000;
+
+ public static final int MAX_THREADS = 10;
+
+ private List<Date> eventList_ = new ArrayList<Date>();
+ private int TCP_PORT = 13579;
+ private final String URL = "service:jmx:rad://localhost:" + TCP_PORT;
+ private final String name = "org.opensolaris.os.rad.test:type=radRequest";
+ private ObjectName objName_;
+
+ protected JMXConnector connector_;
+ protected MBeanServerConnection mbsc_;
+ protected RadRequestMXBean mbean_;
+
+ private boolean isRadReady() throws Exception {
+ AuthenticatorMXBean authenticator = RadJMX.newMXBeanProxy(getMBSC(),
+ strToON("org.opensolaris.os.rad:type=authentication"),
+ AuthenticatorMXBean.class, Stability.PRIVATE);
+
+ if ((authenticator != null) &&
+ (authenticator.getconnectionTimeout() > 0)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ protected void setUp(String noauth) throws Exception {
+ super.setUpSingle(name, RadRequestMXBean.class,
+ new String [] {"-t", "tcp:" + noauth + "port=" + TCP_PORT +
+ ",localonly=false"},
+ "/usr/lib/rad/module/mod_test.so",
+ "/usr/lib/rad/transport/mod_xport_socket.so");
+
+ assertTrue(isRadReady());
+
+ objName_ = strToON(name);
+
+ // now make the tcp connection we'll need for the tests.
+ connector_ = JMXConnectorFactory.connect(new JMXServiceURL(URL));
+ mbsc_ = connector_.getMBeanServerConnection();
+ mbean_ = RadJMX.newMXBeanProxy(mbsc_, objName_, RadRequestMXBean.class,
+ Stability.PRIVATE);
+ }
+
+ @After
+ @Override
+ public void tearDown() throws IOException {
+ connector_.close();
+ super.tearDown();
+ }
+
+ /**
+ * implement <code>javax.management.NotificationListener.</code>
+ */
+ @Override
+ public void handleNotification(Notification notification, Object obj) {
+ if ((notification instanceof RadNotification) &&
+ notification.getType().equals("tick")) {
+
+ eventList_.add(new Date());
+ }
+ }
+
+ /**
+ * implement <code>javax.management.NotificationFilter.<code>
+ */
+ @Override
+ public boolean isNotificationEnabled(Notification notification) {
+ return notification.getType().equals("tick");
+ }
+
+ protected void runEventsTest() throws Exception {
+ RadRequestThread thread = new RadRequestThread(mbean_, SLOW);
+
+ mbean_.eventsStart();
+ mbsc_.addNotificationListener(objName_, this, this, null);
+ thread.start();
+ thread.join();
+ mbsc_.removeNotificationListener(objName_, this, this, null);
+ mbean_.eventsStop();
+
+ Date startTime = thread.getStartTime();
+ Date endTime = thread.getEndTime();
+
+ // the request completed normally
+ assertTrue(thread.isNormalCompletion());
+
+ // we received roughly ((endTime - startTime) / INTERVAL)
+ // events. At the very least we should get 1 less event than expected.
+ int expected = (int)
+ (endTime.getTime() - startTime.getTime()) / INTERVAL;
+ int received = eventList_.size();
+ assertTrue((received == expected) || (received == (expected - 1)));
+
+ // check that the interval between the events is roughly
+ // INTERVAL +/- a small margin.
+ for (int i = 0, j = 1; j < eventList_.size(); i++, j++) {
+ long delta =
+ eventList_.get(j).getTime() - eventList_.get(i).getTime();
+
+ // INTERVAL - EPSILON >= delta <= INTERVAL + EPSILON
+ boolean withinRange = (delta >= INTERVAL - EPSILON) &&
+ (delta <= INTERVAL + EPSILON);
+
+ assertTrue(withinRange);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/java/src/client/RadRequestSyncTest.java Wed May 18 12:42:36 2011 -0400
@@ -0,0 +1,73 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package client;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opensolaris.os.rad.test.RadRequestMXBean;
+import testutil.Desc;
+
+import static org.junit.Assert.*;
+
+public class RadRequestSyncTest extends RadRequestBase {
+
+ @Before
+ public void setUp() throws Exception {
+ // do not allow anonymous connections -t tcp:port=...
+ setUp("");
+ }
+
+ @Test(timeout = SLOW + PAUSE + FAST + TO_PADDING)
+ @Desc("Requests made while a long-running request is being processed " +
+ "are properly queued and handled.")
+ public void syncSlowBlocksFast() throws Exception {
+ RadRequestThread tslow = new RadRequestThread(mbean_, SLOW);
+ RadRequestThread tfast = new RadRequestThread(mbean_, FAST);
+
+ tslow.start();
+ Thread.sleep(PAUSE);
+ tfast.start();
+
+ tslow.join();
+ tfast.join();
+
+ // both requests completed.
+ assertTrue(tslow.isNormalCompletion() && tfast.isNormalCompletion());
+
+ // tslow finishes before tfast.
+ assertTrue(tslow.getEndTime().before(tfast.getEndTime()));
+
+ // tfast finishes atleast FAST after tslow finishes.
+ assertTrue((tslow.getEndTime().getTime() + FAST) <=
+ tfast.getEndTime().getTime());
+ }
+
+ @Test(timeout = SLOW + TO_PADDING)
+ @Desc("Events are delivered during a long-running request.")
+ public void syncEventWhileSlow() throws Exception {
+ runEventsTest();
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/java/src/client/RadRequestThread.java Wed May 18 12:42:36 2011 -0400
@@ -0,0 +1,78 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package client;
+
+import java.util.Date;
+import org.opensolaris.os.rad.ObjectException;
+import org.opensolaris.os.rad.test.RadRequestMXBean;
+
+public class RadRequestThread extends Thread {
+ private RadRequestMXBean mbean_;
+
+ private int duration_;
+ private Date startTime_;
+ private Date endTime_;
+
+ public RadRequestThread(RadRequestMXBean bean, int duration) {
+ mbean_ = bean;
+ duration_ = duration;
+ }
+
+ @Override
+ public void run() {
+ startTime_ = new Date();
+ try {
+ mbean_.sleep(duration_);
+ endTime_ = new Date();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public int getDuration() {
+ return duration_;
+ }
+
+ public Date getStartTime() {
+ return startTime_;
+ }
+
+ public Date getEndTime() {
+ return endTime_;
+ }
+
+ /* These two methods should be called after a successful join */
+ public boolean isNormalCompletion() {
+ return (endTime_ != null);
+ }
+
+ public long getExecutionTime() throws Exception {
+ if (!isNormalCompletion())
+ throw new Exception("The request was not handled successfuly.");
+
+ return (endTime_.getTime() - startTime_.getTime());
+ }
+}
\ No newline at end of file
--- a/usr/src/test/java/src/common/MBeanTestSingle.java Tue May 17 10:07:43 2011 -0400
+++ b/usr/src/test/java/src/common/MBeanTestSingle.java Wed May 18 12:42:36 2011 -0400
@@ -33,8 +33,12 @@
protected void setUpSingle(String name, Class<T> type, String... modules)
throws Exception {
+ setUpSingle(name, type, null, modules);
+ }
- super.setUpCommon(modules);
+ protected void setUpSingle(String name, Class<T> type, String auxargs[],
+ String... modules) throws Exception {
+ super.setUpCommon(auxargs, modules);
bean_ = RadJMX.newMXBeanProxy(getMBSC(), strToON(name), type,
Stability.PRIVATE);
}