summaryrefslogtreecommitdiff
path: root/java/systests/src/main/java/org/apache/qpid/interop/coordinator/InteropTestDecorator.java
blob: 7dcc39165034b92c91b3226ee0ed70663484d378 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*
 *
 * 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.
 *
 */
package org.apache.qpid.interop.coordinator;

import junit.framework.Test;
import junit.framework.TestResult;

import org.apache.log4j.Logger;

import org.apache.qpid.interop.coordinator.sequencers.DistributedTestSequencer;
import org.apache.qpid.interop.coordinator.sequencers.InteropTestSequencer;
import org.apache.qpid.util.ConversationFactory;

import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;

import javax.jms.Connection;

import java.util.*;

/**
 * DistributedTestDecorator is a test decorator, written to implement the interop test specification. Given a list
 * of enlisted test clients, that are available to run interop tests, this decorator invites them to participate
 * in each test in the wrapped test suite. Amongst all the clients that respond to the invite, all pairs are formed,
 * and each pairing (in both directions, but excluding the reflexive pairings) is split into a sender and receivers
 * role and a test case run between them. Any enlisted combinations that do not accept a test invite are automatically
 * failed.
 *
 * <p><table id="crc"><caption>CRC Card</caption>
 * <tr><th> Responsibilities <th> Collaborations
 * <tr><td> Broadcast test invitations and collect enlists. <td> {@link org.apache.qpid.util.ConversationFactory}.
 * <tr><td> Output test failures for clients unwilling to run the test case. <td> {@link Coordinator}
 * <tr><td> Execute distributed test cases. <td> {@link DistributedTestCase}
 * <tr><td> Fail non participating pairings. <td> {@link OptOutTestCase}
 * </table>
 */
public class InteropTestDecorator extends DistributedTestDecorator
{
    /** Used for debugging. */
    private static final Logger log = Logger.getLogger(InteropTestDecorator.class);

    /**
     * Creates a wrapped suite test decorator from another one.
     *
     * @param suite               The test suite.
     * @param availableClients    The list of all clients that responded to the compulsory invite.
     * @param controlConversation The conversation helper for the control level, test coordination conversation.
     * @param controlConnection   The connection that the coordination messages are sent over.
     */
    public InteropTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> availableClients,
        ConversationFactory controlConversation, Connection controlConnection)
    {
        super(suite, availableClients, controlConversation, controlConnection);
    }

    /**
     * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is
     * then repeated for every combination of test clients (provided the wrapped test case extends
     * {@link DistributedTestCase}.
     *
     * <p/>Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime exceptions,
     * resulting in the non-completion of the test run.
     *
     * @todo Better error recovery for failure of the invite/enlist conversation could be added.
     *
     * @param testResult The the results object to monitor the test results with.
     */
    public void run(TestResult testResult)
    {
        log.debug("public void run(TestResult testResult): called");

        Collection<Test> tests = testSuite.getAllUnderlyingTests();

        for (Test test : tests)
        {
            DistributedTestCase coordTest = (DistributedTestCase) test;

            // Broadcast the invitation to find out what clients are available to test.
            Set<TestClientDetails> enlists = signupClients(coordTest);

            // Compare the list of willing clients to the list of all available.
            Set<TestClientDetails> optOuts = new HashSet<TestClientDetails>(allClients);
            optOuts.removeAll(enlists);

            // Output test failures for clients that will not particpate in the test.
            Set<List<TestClientDetails>> failPairs = allPairs(optOuts, allClients);

            for (List<TestClientDetails> failPair : failPairs)
            {
                // Create a distributed test sequencer for the test.
                DistributedTestSequencer sequencer = getDistributedTestSequencer();

                // Create an automatic failure test for the opted out test pair.
                DistributedTestCase failTest = new OptOutTestCase("testOptOut");
                sequencer.setSender(failPair.get(0));
                sequencer.setReceiver(failPair.get(1));
                failTest.setTestSequencer(sequencer);

                failTest.run(testResult);
            }

            // Loop over all combinations of clients, willing to run the test.
            Set<List<TestClientDetails>> enlistedPairs = allPairs(enlists, enlists);

            for (List<TestClientDetails> enlistedPair : enlistedPairs)
            {
                // Create a distributed test sequencer for the test.
                DistributedTestSequencer sequencer = getDistributedTestSequencer();

                // Set the sending and receiving client details on the test sequencer.
                sequencer.setSender(enlistedPair.get(0));
                sequencer.setReceiver(enlistedPair.get(1));

                // Pass down the connection to hold the coordination conversation over.
                sequencer.setConversationFactory(conversationFactory);

                // Execute the test case.
                coordTest.setTestSequencer(sequencer);
                coordTest.run(testResult);
            }
        }
    }

    /**
     * Should provide the distributed test sequencer to pass to {@link org.apache.qpid.test.framework.FrameworkBaseCase}
     * tests.
     *
     * @return A distributed test sequencer.
     */
    public DistributedTestSequencer getDistributedTestSequencer()
    {
        return new InteropTestSequencer();
    }

    /**
     * Produces all pairs of combinations of elements from two sets. The ordering of the elements in the pair is
     * important, that is the pair <l, r> is distinct from <r, l>; both pairs are generated. For any element, i, in
     * both the left and right sets, the reflexive pair <i, i> is not generated.
     *
     * @param left  The left set.
     * @param right The right set.
     * @param <E>   The type of the content of the pairs.
     *
     * @return All pairs formed from the permutations of all elements of the left and right sets.
     */
    private <E> Set<List<E>> allPairs(Set<E> left, Set<E> right)
    {
        log.debug("private <E> Set<List<E>> allPairs(Set<E> left = " + left + ", Set<E> right = " + right + "): called");

        Set<List<E>> results = new HashSet<List<E>>();

        // Form all pairs from left to right.
        // Form all pairs from right to left.
        for (E le : left)
        {
            for (E re : right)
            {
                if (!le.equals(re))
                {
                    results.add(new Pair<E>(le, re));
                    results.add(new Pair<E>(re, le));
                }
            }
        }

        log.debug("results = " + results);

        return results;
    }

    /**
     * A simple implementation of a pair, using a list.
     */
    private class Pair<T> extends ArrayList<T>
    {
        /**
         * Creates a new pair of elements.
         *
         * @param first  The first element.
         * @param second The second element.
         */
        public Pair(T first, T second)
        {
            super();
            super.add(first);
            super.add(second);
        }
    }
}