001    /*
002     * $Id: TestOn.java,v 1.15 2011/11/21 20:46:43 oboehm Exp $
003     *
004     * Copyright (c) 2010 by Oliver Boehm
005     *
006     * Licensed under the Apache License, Version 2.0 (the "License");
007     * you may not use this file except in compliance with the License.
008     * 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, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     *
018     * (c)reated 23.04.2010 by oliver (ob@oasd.de)
019     */
020    
021    package patterntesting.runtime.junit.internal;
022    
023    import java.net.*;
024    
025    import org.apache.commons.lang.*;
026    import org.slf4j.*;
027    
028    import patterntesting.runtime.net.Localhost;
029    import patterntesting.runtime.util.*;
030    
031    /**
032     * This is a local helper class for the RunTestOn and SkipTestOn annotation.
033     * So most of the methods are package default because there is no need to
034     * access from somewhere else.
035     *
036     * @author oliver
037     * @since 1.0 (23.04.2010)
038     */
039    public final class TestOn {
040    
041        private static final Logger log = LoggerFactory.getLogger(TestOn.class);
042        private String osName = Environment.OS_NAME;
043        private String osArch = Environment.OS_ARCH;
044        private String userName = Environment.USER_NAME;
045        /** Contains the reason of the last match. */
046        private transient String reason;
047    
048        /**
049         * For testing you can set the OS name.
050         *
051         * @param name the new os name
052         */
053        public void setOsName(final String name) {
054            this.osName = name;
055        }
056    
057        /**
058         * For testing you can set the OS architector.
059         *
060         * @param arch the new os architector
061         */
062        public void setOsArch(final String arch) {
063            this.osArch = arch;
064        }
065    
066        /**
067         * For testing you can set the user name.
068         *
069         * @param name the new user name
070         */
071        public void setUserName(final String name) {
072            this.userName = name;
073        }
074    
075        /**
076         * Gets the reason.
077         *
078         * @return the reason
079         */
080        public String getReason() {
081            return this.reason;
082        }
083    
084        /**
085         * Checks for reason.
086         *
087         * @return true, if reason is set
088         */
089        public boolean hasReason() {
090            return this.reason != null;
091        }
092    
093        /**
094         * If the operating system(s) match and one of the other parameters (if
095         * they contain values) this method will return true.
096         *
097         * @param value same as os parameter
098         * @param os the operating system(s) which should match
099         * @param arch the architectures
100         * @param version the version(s) of the operating systems
101         * @param host the host(s)
102         * @param javaVersions the java version(s)
103         * @param javaVendors the java vendor(s)
104         * @param users the user(s)
105         * @param systemProps the system properties
106         * @return true if the parameter matches
107         */
108        public boolean matches(final String[] value, final String[] os,
109                final String[] arch, final String[] version, final String[] host,
110                final String[] javaVersions, final String[] javaVendors,
111                final String[] users, final String[] systemProps) {
112            String[] osValue = StringUtils.isEmpty(os[0]) ? value : os;
113            return matches(osValue, arch, version, host, javaVersions, javaVendors, users, systemProps);
114        }
115    
116        /**
117         * If the operating system(s) match and one of the other parameters (if
118         * they contain values) this method will return true.
119         *
120         * @param os the operating system(s) which should match
121         * @param arch the architectures
122         * @param version the version(s) of the operating systems
123         * @param host the host(s)
124         * @param javaVersions the java version(s)
125         * @param javaVendors the java vendor(s)
126         * @param users the user(s)
127         * @param systemProps the system properties
128         * @return true if the parameter matches
129         */
130        public boolean matches(final String[] os,
131                final String[] arch, final String[] version, final String[] host,
132                final String[] javaVersions, final String[] javaVendors,
133                final String[] users, final String[] systemProps) {
134            boolean valueGiven = false;
135            if (!StringUtils.isEmpty(os[0])) {
136                if (!matches(os, osName)) {
137                    return false;
138                }
139                valueGiven = true;
140            }
141            if (!StringUtils.isEmpty(arch[0])) {
142                if (!matches(arch, osArch)) {
143                    return false;
144                }
145                valueGiven = true;
146            }
147            if (!StringUtils.isEmpty(version[0])) {
148                if (!matches(version, Environment.OS_VERSION)) {
149                    return false;
150                }
151                valueGiven = true;
152            }
153            if (!StringUtils.isEmpty(javaVersions[0])) {
154                if (!matches(javaVersions, Environment.JAVA_VERSION)) {
155                    return false;
156                }
157                valueGiven = true;
158            }
159            if (!StringUtils.isEmpty(javaVendors[0])) {
160                if (!matches(javaVendors, Environment.JAVA_VENDOR)) {
161                    return false;
162                }
163                valueGiven = true;
164            }
165            if (!StringUtils.isEmpty(users[0])) {
166                if (!matches(users, userName)) {
167                    return false;
168                }
169                valueGiven = true;
170            }
171            if (!StringUtils.isEmpty(systemProps[0])) {
172                if (!Environment.matchesOneOf(systemProps)) {
173                    return false;
174                }
175                valueGiven = true;
176            }
177            // this call is expensive so we shift it to the end
178            if (!StringUtils.isEmpty(host[0])) {
179                if (!runsOn(host)) {
180                    return false;
181                }
182                valueGiven = true;
183            }
184            if (!valueGiven) {
185                throw new IllegalArgumentException("no value(s) given");
186                //return true;
187            }
188            reason = (StringUtils.isEmpty(os[0]) ? "" : osName)
189                    + (StringUtils.isEmpty(arch[0]) ? "" : osArch)
190                    + (StringUtils.isEmpty(version[0]) ? "" : Environment.OS_VERSION)
191                    + (StringUtils.isEmpty(javaVersions[0]) ? "" : "JDK " + Environment.JAVA_VERSION)
192                    + (StringUtils.isEmpty(javaVendors[0]) ? "" : Environment.JAVA_VENDOR + " as vendor")
193                    + (StringUtils.isEmpty(users[0]) ? "" : Environment.JAVA_VENDOR + " as user")
194                    + (StringUtils.isEmpty(systemProps[0]) ? "" : Converter.toString(systemProps))
195                    + (StringUtils.isEmpty(host[0]) ? "" : "host " + Converter.toString(host))
196                    + " detected";
197            return true;
198        }
199    
200        private static boolean matches(final String[] names, final String name) {
201            for (int i = 0; i < names.length; i++) {
202                if (matches(names[i], name)) {
203                    return true;
204                }
205            }
206            return false;
207        }
208        
209        /**
210         * If the given pattern parameter contains an asterisk ("*") it is
211         * considered as pattern. Otherwise only the beginning with name
212         * must match.
213         *
214         * @param pattern the pattern
215         * @param name the name
216         * @return true, if successful
217         * @since 1.1
218         */
219        private static boolean matches(final String pattern, final String name) {
220            if (name.startsWith(pattern)) {
221                return true;
222            }
223            if (pattern.contains("*") || (pattern.contains("?"))) {
224                String regex = wildcardToRegex(pattern);
225                return name.matches(regex);
226            }
227            return false;
228        }
229    
230        private static boolean runsOn(final String[] hosts) {
231            try {
232                InetAddress localhost = InetAddress.getLocalHost();
233                if (ArrayUtils.contains(hosts, localhost.getHostAddress())
234                        || matches(hosts, localhost.getHostName())) {
235                    return true;
236                }
237                return Localhost.matches(hosts);
238            } catch (UnknownHostException e) {
239                log.debug("can't get local InetAddress - using localhost", e);
240                return runsOnLocalhost(hosts);
241            }
242        }
243    
244        private static boolean runsOnLocalhost(final String[] hosts) {
245            return ArrayUtils.contains(hosts, "127.0.0.1")
246                    || StringHelper.containsIgnoreCase(hosts, "localhost"); //NOPMD
247        }
248    
249        /**
250         * Wildcard to regex.
251         *
252         * @param wildcard the wildcard
253         * @return the string
254         * @see "http://www.rgagnon.com/javadetails/java-0515.html"
255         */
256        private static String wildcardToRegex(final String wildcard) {
257            StringBuffer s = new StringBuffer(wildcard.length());
258            s.append('^');
259            for (int i = 0, is = wildcard.length(); i < is; i++) {
260                char c = wildcard.charAt(i);
261                switch(c) {
262                    case '*':
263                        s.append(".*");
264                        break;
265                    case '?':
266                        s.append(".");
267                        break;
268                        // escape special regexp-characters
269                    case '(': case ')': case '[': case ']': case '$':
270                    case '^': case '.': case '{': case '}': case '|':
271                    case '\\':
272                        s.append("\\");
273                        s.append(c);
274                        break;
275                    default:
276                        s.append(c);
277                        break;
278                }
279            }
280            s.append('$');
281            return(s.toString());
282        }
283    
284    }
285