/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat, Inc., and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * Licensed 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.jboss.cdi.tck.util; import static org.testng.Assert.assertEquals; import static org.testng.AssertJUnit.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Pattern; /** * Simple data holder for sequence of actions identified with {@link String}. * * Always call {@link #reset()} before your test code to remove previous sequences stored in static map! * * @author Martin Kouba */ public final class ActionSequence { private static final Pattern VALID_NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_.$\\-]+"); private static final TransformationUtils.Function<Class<?>, String> GET_SIMPLE_NAME = new TransformationUtils.Function<Class<?>, String>() { @Override public String apply(Class<?> input) { return input.getSimpleName(); } }; private String name; /** * Data - list of actions */ private List<String> data = Collections.synchronizedList(new ArrayList<String>()); public ActionSequence() { super(); this.name = DEFAULT_SEQUENCE; } /** * @param name */ public ActionSequence(String name) { super(); checkStringValue(name); this.name = name; } /** * @param actionId * @return data holder */ public ActionSequence add(String actionId) { checkStringValue(actionId); this.data.add(actionId); return this; } /** * @return read-only copy of sequence data */ public List<String> getData() { return Collections.unmodifiableList(new ArrayList<String>(this.data)); } /** * @return name of sequence */ public String getName() { return name; } /** * * @param actions * @return <code>true</code> if sequence data contain all of the specified actions, <code>false</code> otherwise */ public boolean containsAll(String... actions) { return getData().containsAll(Arrays.asList(actions)); } /** * * @param actions * @return <code>true</code> if sequence data begins with the specified actions, <code>false</code> otherwise */ public boolean beginsWith(String... actions) { List<String> sequenceData = getData(); List<String> matchData = Arrays.asList(actions); if (sequenceData.size() < matchData.size()) return false; return sequenceData.subList(0, matchData.size()).equals(matchData); } /** * * @param actions * @return <code>true</code> if sequence data ends with the specified actions, <code>false</code> otherwise */ public boolean endsWith(String... actions) { List<String> sequenceData = getData(); List<String> matchData = Arrays.asList(actions); if (sequenceData.size() < matchData.size()) return false; return sequenceData.subList(sequenceData.size() - matchData.size(), sequenceData.size()).equals(matchData); } @Override public String toString() { return String.format("ActionSequence [name=%s, data=%s]", name, getData()); } /** * * @return data in simple CSV format */ public String dataToCsv() { if (data.isEmpty()) { return ""; } StringBuilder csv = new StringBuilder(); for (Iterator<String> iterator = data.iterator(); iterator.hasNext();) { String actionId = iterator.next(); csv.append(actionId); if (iterator.hasNext()) { csv.append(","); } } return csv.toString(); } /** * Assert that strings stored in this sequence equal (in order!) to the {@code expected} strings. * * @param expected */ public void assertDataEquals(List<String> expected) { assertEquals(data.size(), expected.size(), String.format("%s and expected sequence differ in size.", toString())); for (int i = 0; i < expected.size(); i++) { assertEquals(data.get(i), expected.get(i), String.format("%s and expected sequence differ on the index %d.", toString(), i)); } } /** * Assert that strings stored in this sequence equal (in order!) to the {@code expected} strings. * * @param expected */ public void assertDataEquals(String... expected) { assertDataEquals(Arrays.asList(expected)); } /** * Assert that strings stored in this sequence equal (in order!) to the simple class names of the {@code expected} classes. * * @param expected */ public void assertDataEquals(Class<?>... expected) { assertDataEquals(TransformationUtils.transform(GET_SIMPLE_NAME, expected)); } /** * Assert that this sequence contains all of the {@code expected} strings. Note that this only verifies that the * {@code expected} strings are a SUBSET of the actual strings stored in this sequence. * * @param expected */ public void assertDataContainsAll(Collection<String> expected) { for (String s : expected) { assertTrue(String.format("%s does not contain %s", toString(), s), data.contains(s)); } } /** * Assert that this sequence contains all of the {@code expected} strings. Note that this only verifies that the * {@code expected} strings are a SUBSET of the actual strings stored in this sequence. * * @param expected */ public void assertDataContainsAll(String... expected) { assertDataContainsAll(Arrays.asList(expected)); } /** * Assert that this sequence contains simple class names of all of the {@code expected} classes. Note that this only * verifies that the {@code expected} classes are a SUBSET of the actual classes stored in this sequence. * * @param expected */ public void assertDataContainsAll(Class<?>... expected) { assertDataContainsAll(TransformationUtils.transform(GET_SIMPLE_NAME, expected)); } // Static members private static final String DEFAULT_SEQUENCE = "default"; /** * Static sequence map */ private static Map<String, ActionSequence> sequences = new HashMap<String, ActionSequence>(); /** * Remove all sequences. */ public static void reset() { synchronized (sequences) { sequences.clear(); } } /** * Add actionId to specified sequence. Add new sequence if needed. * * @param sequence * @param actionId * @return <code>true</code> if a new sequence was added, <code>false</code> otherwise */ public static boolean addAction(String sequenceName, String actionId) { boolean newSequenceAdded = false; synchronized (sequences) { if (!sequences.containsKey(sequenceName)) { sequences.put(sequenceName, new ActionSequence(sequenceName)); newSequenceAdded = true; } sequences.get(sequenceName).add(actionId); } return newSequenceAdded; } /** * Add actionId to default sequence. * * @param actionId * @return <code>true</code> if a new sequence was added, <code>false</code> otherwise */ public static boolean addAction(String actionId) { return addAction(DEFAULT_SEQUENCE, actionId); } /** * @return default sequence or <code>null</code> if no such sequence exists */ public static ActionSequence getSequence() { return getSequence(DEFAULT_SEQUENCE); } /** * @param name * @return specified sequence or <code>null</code> if no such sequence exists */ public static ActionSequence getSequence(String sequenceName) { synchronized (sequences) { return sequences.get(sequenceName); } } /** * @return data of default sequence or <code>null</code> if no such sequence exists */ public static List<String> getSequenceData() { return getSequenceData(DEFAULT_SEQUENCE); } /** * @param sequenceName * @return data of specified sequence or <code>null</code> if no such sequence exists */ public static List<String> getSequenceData(String sequenceName) { synchronized (sequences) { return sequences.containsKey(sequenceName) ? sequences.get(sequenceName).getData() : null; } } /** * @return size of default sequence */ public static int getSequenceSize() { return getSequenceSize(DEFAULT_SEQUENCE); } /** * @param sequence * @return size of specified sequence */ public static int getSequenceSize(String sequenceName) { synchronized (sequences) { return sequences.containsKey(sequenceName) ? sequences.get(sequenceName).getData().size() : 0; } } /** * * @param csv * @return */ public static ActionSequence buildFromCsvData(String csv) { if (csv == null) { throw new NullPointerException(); } ActionSequence sequence = new ActionSequence(); if (csv.length() != 0) { String[] data = csv.split(","); for (String actionId : data) { sequence.add(actionId); } } return sequence; } /** * Assert that strings stored in this sequence equal (in order!) to the {@code expected} strings. * * @param expected * @throws IllegalStateException if there is no default sequence */ public static void assertSequenceDataEquals(List<String> expected) { checkDefaultSequenceExists(); getSequence().assertDataEquals(expected); } /** * Assert that strings stored in this sequence equal (in order!) to the {@code expected} strings. * * @param expected * @throws IllegalStateException if there is no default sequence */ public static void assertSequenceDataEquals(String... expected) { checkDefaultSequenceExists(); getSequence().assertDataEquals(Arrays.asList(expected)); } /** * Assert that strings stored in the default sequence equal (in order!) to the simple class names of {@code expected}. * * @param expected * @throws IllegalStateException if there is no default sequence */ public static void assertSequenceDataEquals(Class<?>... expected) { checkDefaultSequenceExists(); getSequence().assertDataEquals(expected); } /** * Assert that this sequence contains all of the {@code expected} strings. Note that this only verifies that the * {@code expected} strings are a SUBSET of the actual strings stored in this sequence. * * @param expected * @throws IllegalStateException if there is no default sequence */ public static void assertSequenceDataContainsAll(Collection<String> expected) { checkDefaultSequenceExists(); getSequence().assertDataContainsAll(expected); } /** * Assert that this sequence contains all of the {@code expected} strings. Note that this only verifies that the * {@code expected} strings are a SUBSET of the actual strings stored in this sequence. * * @param expected * @throws IllegalStateException if there is no default sequence */ public static void assertSequenceDataContainsAll(String... expected) { checkDefaultSequenceExists(); getSequence().assertDataContainsAll(Arrays.asList(expected)); } /** * Assert that the default sequence contains simple class names of all of the {@code expected} classes. Note that this only * verifies that the {@code expected} classes are a SUBSET of the actual classes stored in this sequence. * * @param expected * @throws IllegalStateException if there is no default sequence */ public static void assertSequenceDataContainsAll(Class<?>... expected) { checkDefaultSequenceExists(); getSequence().assertDataContainsAll(expected); } private static void checkStringValue(String value) { if (!VALID_NAME_PATTERN.matcher(value).matches()) { throw new IllegalArgumentException("Invalid name/id specified:" + value); } } private static void checkDefaultSequenceExists() { if (getSequence() == null) { throw new IllegalStateException("There is no default sequence. You cannot assert anything about it."); } } }