001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020package org.tynamo.test;
021
022import static com.gargoylesoftware.htmlunit.WebAssert.assertTextNotPresent;
023import static com.gargoylesoftware.htmlunit.WebAssert.assertTextPresent;
024import static org.testng.Assert.assertNotNull;
025import static org.testng.Assert.assertTrue;
026import static org.testng.Assert.fail;
027
028import java.io.File;
029import java.io.IOException;
030import java.net.Socket;
031import java.util.List;
032
033import org.eclipse.jetty.server.Connector;
034import org.eclipse.jetty.server.Handler;
035import org.eclipse.jetty.server.Server;
036import org.eclipse.jetty.server.handler.DefaultHandler;
037import org.eclipse.jetty.server.handler.HandlerCollection;
038import org.eclipse.jetty.server.nio.SelectChannelConnector;
039import org.eclipse.jetty.util.resource.ResourceCollection;
040import org.eclipse.jetty.webapp.WebAppContext;
041import org.testng.annotations.BeforeClass;
042
043import com.gargoylesoftware.htmlunit.ElementNotFoundException;
044import com.gargoylesoftware.htmlunit.WebClient;
045import com.gargoylesoftware.htmlunit.html.HtmlForm;
046import com.gargoylesoftware.htmlunit.html.HtmlInput;
047import com.gargoylesoftware.htmlunit.html.HtmlPage;
048// import org.eclipse.jetty.server.nio.SelectChannelConnector;
049
050public abstract class AbstractContainerTest
051{
052        protected static PauseableServer server;
053
054        public static int port = 8180;
055
056        protected static String BASEURI = "http://localhost:" + port + "/";
057
058        protected final WebClient webClient = new WebClient();
059
060        static String errorText = "You must correct the following errors before continuing";
061
062        @BeforeClass
063        public void startContainer() throws Exception
064        {
065                if (server == null)
066                {
067                        String reserveNetworkPort = System.getProperty("reserved.network.port");
068
069                        if (reserveNetworkPort != null)
070                        {
071                                port = Integer.valueOf(reserveNetworkPort);
072                                BASEURI = "http://localhost:" + port + "/";
073                        } else {
074                                // adapted from http://stackoverflow.com/questions/434718/sockets-discover-port-availability-using-java
075                                // arbitrarily try next ten ports
076                                int maxPort = port + 10;
077                                for (port = port; port < maxPort; port++) {
078                                        Socket sock = null;
079                                        try {
080                                                // Check if port is open by trying to connect as a client
081                                                sock = new Socket("localhost", port);
082                                                sock.close();
083                                                continue;
084                                        } catch (Exception e) {
085                                                if (sock != null) sock = null;
086                                                if (e.getMessage().contains("refused")) {
087                                                        break;
088                                                }
089                                                throw new RuntimeException("Couldn't find an available port to run the functional test server", e);
090                                        }
091                                }
092
093                        }
094
095
096                        server = new PauseableServer();
097                        Connector connector = new SelectChannelConnector();
098                        connector.setPort(port);
099                        server.setConnectors(new Connector[]{connector});
100
101                        HandlerCollection handlers = new HandlerCollection();
102                        handlers.setHandlers(new Handler[]{buildContext(), new DefaultHandler()});
103                        server.setHandler(handlers);
104                        server.start();
105                        assertTrue(server.isStarted());
106                }
107        }
108
109        /**
110         * Non-abstract hook method (with a default implementation) to allow subclasses to provide their own WebAppContext instance.
111         * @return a WebAppContext
112         */
113        public WebAppContext buildContext()
114        {
115                WebAppContext context = new WebAppContext("src/main/webapp", "/");
116                if (new File("src/test/webapp").exists()) {
117                        ResourceCollection resourceCollection = new ResourceCollection(new String[]{"src/main/webapp", "src/test/webapp"});
118                        context.setBaseResource(resourceCollection);
119                }
120
121                /**
122                 * like -Dorg.eclipse.jetty.webapp.parentLoaderPriority=true
123                 * Sets the classloading model for the context to avoid an strange "ClassNotFoundException: org.slf4j.Logger"
124                 */
125                context.setParentLoaderPriority(true);
126                return context;
127        }
128
129        public void pauseServer(boolean paused)
130        {
131                if (server != null) server.pause(paused);
132        }
133
134        public static class PauseableServer extends Server
135        {
136                public synchronized void pause(boolean paused)
137                {
138                        try
139                        {
140                                if (paused) for (Connector connector : getConnectors())
141                                        connector.stop();
142                                else for (Connector connector : getConnectors())
143                                        connector.start();
144                        } catch (Exception e)
145                        {
146                        }
147                }
148        }
149
150        /**
151         * Verifies that the specified xpath is somewhere on the page.
152         *
153         * @param page
154         * @param xpath
155         */
156        protected void assertXPathPresent(HtmlPage page, String xpath)
157        {
158                String message = "XPath not present: " + xpath;
159                List list = page.getByXPath(xpath);
160                if (list.isEmpty()) fail(message);
161                assertNotNull(list.get(0), message);
162        }
163
164        /**
165         * Verifies that the specified xpath does NOT appear anywhere on the page.
166         *
167         * @param page
168         * @param xpath
169         */
170        protected void assertXPathNotPresent(HtmlPage page, String xpath)
171        {
172                if (!page.getByXPath(xpath).isEmpty()) fail("XPath IS present: " + xpath);
173        }
174
175
176        protected HtmlPage clickLink(HtmlPage page, String linkText)
177        {
178                try
179                {
180                        return (HtmlPage) page.getAnchorByText(linkText).click();
181                } catch (ElementNotFoundException e)
182                {
183                        fail("Couldn't find a link with text '" + linkText + "' on page " + page);
184                } catch (IOException e)
185                {
186                        fail("Clicking on link '" + linkText + "' on page " + page + " failed because of: ", e);
187                }
188                return null;
189        }
190
191        protected HtmlPage clickButton(HtmlPage page, String buttonId) throws IOException
192        {
193                return page.getHtmlElementById(buttonId).click();
194        }
195
196        protected HtmlPage clickButton(HtmlForm form, String buttonValue) throws IOException
197        {
198                try
199                {
200                        return form.<HtmlInput>getInputByValue(buttonValue).click();
201                } catch (ElementNotFoundException e)
202                {
203                        try
204                        {
205                                return form.getButtonByName(buttonValue).click();
206                        } catch (ElementNotFoundException e1)
207                        {
208                                fail("Couldn't find a button with text/name '" + buttonValue + "' on form '" + form.getNameAttribute() +
209                                                "'");
210                        }
211                }
212                return null;
213        }
214
215        protected void assertErrorTextPresent(HtmlPage page)
216        {
217                assertTextPresent(page, errorText);
218        }
219
220        protected void assertErrorTextNotPresent(HtmlPage page)
221        {
222                assertTextNotPresent(page, errorText);
223        }
224
225}