aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client-compiler/src/com/vaadin/sass/linker/SassLinker.java82
-rw-r--r--server/src/com/vaadin/data/fieldgroup/FieldGroup.java98
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java84
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java20
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AutomaticSassTests.java14
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java14
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java14
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassTestRunner.java133
8 files changed, 374 insertions, 85 deletions
diff --git a/client-compiler/src/com/vaadin/sass/linker/SassLinker.java b/client-compiler/src/com/vaadin/sass/linker/SassLinker.java
index 82a228a166..a568ca9672 100644
--- a/client-compiler/src/com/vaadin/sass/linker/SassLinker.java
+++ b/client-compiler/src/com/vaadin/sass/linker/SassLinker.java
@@ -35,7 +35,7 @@ public class SassLinker extends AbstractLinker {
@Override
public String getDescription() {
- return "Compiling SASS files in public folders to standard CSS";
+ return "Compiling SCSS files in public folders to standard CSS";
}
@Override
@@ -47,8 +47,8 @@ public class SassLinker extends AbstractLinker {
// The artifact to return
ArtifactSet toReturn = new ArtifactSet(artifacts);
- // The temporary sass files provided from the artefacts
- List<FileInfo> sassFiles = new ArrayList<FileInfo>();
+ // The temporary scss files provided from the artefacts
+ List<FileInfo> scssFiles = new ArrayList<FileInfo>();
// The public files are provided as inputstream, but the compiler
// needs real files, as they can contain references to other
@@ -56,10 +56,13 @@ public class SassLinker extends AbstractLinker {
String tempFolderName = new Date().getTime() + File.separator;
File tempFolder = createTempDir(tempFolderName);
- // Create the temporary files
+ // Can't search here specifically for public resources, as the type
+ // is different during compilation. This means we have to loop
+ // through all the artifacts
for (EmittedArtifact resource : artifacts
.find(EmittedArtifact.class)) {
+ // Create the temporary files.
String partialPath = resource.getPartialPath();
if (partialPath.endsWith(".scss")) {
@@ -85,46 +88,54 @@ public class SassLinker extends AbstractLinker {
tempfile);
// Store the file info for the compilation
- sassFiles.add(new FileInfo(tempfile, partialPath));
+ scssFiles.add(new FileInfo(tempfile, partialPath));
- // In my oppinion, the SASS file does not need to be
- // output to the web content, as they can't be used
- // there
+ // In my opinion, the SCSS file does not need to be
+ // output to the web content folder, as they can't
+ // be used there
toReturn.remove(resource);
+ } else {
+ logger.log(TreeLogger.WARN, "Duplicate file "
+ + tempfile.getPath());
}
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ logger.log(TreeLogger.ERROR,
+ "Could not write temporary file " + fileName, e);
}
}
}
// Compile the files and store them in the artifact
- logger.log(TreeLogger.INFO, "Processing " + sassFiles.size()
+ logger.log(TreeLogger.INFO, "Processing " + scssFiles.size()
+ " Sass file(s)");
- for (FileInfo fileInfo : sassFiles) {
+ for (FileInfo fileInfo : scssFiles) {
logger.log(TreeLogger.INFO, " " + fileInfo.originalScssPath
+ " -> " + fileInfo.getOriginalCssPath());
- ScssStylesheet scss;
-
try {
- scss = ScssStylesheet.get(fileInfo.getAbsolutePath());
- scss.compile();
- InputStream is = new ByteArrayInputStream(scss.toString()
- .getBytes());
+ ScssStylesheet scss = ScssStylesheet.get(fileInfo
+ .getAbsolutePath());
+ if (!fileInfo.isMixin()) {
+ scss.compile();
+ InputStream is = new ByteArrayInputStream(scss
+ .toString().getBytes());
+
+ toReturn.add(this.emitInputStream(logger, is,
+ fileInfo.getOriginalCssPath()));
+ }
- toReturn.add(this.emitInputStream(logger, is,
- fileInfo.getOriginalCssPath()));
+ fileInfo.getFile().delete();
} catch (CSSException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ logger.log(TreeLogger.ERROR, "SCSS compilation failed for "
+ + fileInfo.getOriginalCssPath(), e);
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ logger.log(
+ TreeLogger.ERROR,
+ "Could not write CSS file for "
+ + fileInfo.getOriginalCssPath(), e);
} catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ logger.log(TreeLogger.ERROR, "SCSS compilation failed for "
+ + fileInfo.getOriginalCssPath(), e);
}
}
@@ -134,6 +145,13 @@ public class SassLinker extends AbstractLinker {
return artifacts;
}
+ /**
+ * Writes the contents of an InputStream out to a file.
+ *
+ * @param contents
+ * @param tempfile
+ * @throws IOException
+ */
private void writeFromInputStream(InputStream contents, File tempfile)
throws IOException {
// write the inputStream to a FileOutputStream
@@ -170,8 +188,7 @@ public class SassLinker extends AbstractLinker {
}
/**
- * Temporal storage for file info from Artifact
- *
+ * Temporal storage for file info from Artifact.
*/
private class FileInfo {
private String originalScssPath;
@@ -182,6 +199,10 @@ public class SassLinker extends AbstractLinker {
this.originalScssPath = originalScssPath;
}
+ public boolean isMixin() {
+ return file.getName().startsWith("_");
+ }
+
public String getAbsolutePath() {
return file.getAbsolutePath();
}
@@ -190,6 +211,9 @@ public class SassLinker extends AbstractLinker {
return originalScssPath.substring(0, originalScssPath.length() - 5)
+ ".css";
}
- }
+ public File getFile() {
+ return file;
+ }
+ }
}
diff --git a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java
index 8803054857..dad2e26497 100644
--- a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java
+++ b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java
@@ -693,10 +693,11 @@ public class FieldGroup implements Serializable {
* Binds member fields found in the given object.
* <p>
* This method processes all (Java) member fields whose type extends
- * {@link Field} and that can be mapped to a property id. Property id
- * mapping is done based on the field name or on a @{@link PropertyId}
- * annotation on the field. All non-null fields for which a property id can
- * be determined are bound to the property id.
+ * {@link Field} and that can be mapped to a property id. Property ids are
+ * searched in the following order: @{@link PropertyId} annotations, exact
+ * field name matches and the case-insensitive matching that ignores
+ * underscores. All non-null fields for which a property id can be
+ * determined are bound to the property id.
* </p>
* <p>
* For example:
@@ -733,11 +734,12 @@ public class FieldGroup implements Serializable {
* that have not been initialized.
* <p>
* This method processes all (Java) member fields whose type extends
- * {@link Field} and that can be mapped to a property id. Property id
- * mapping is done based on the field name or on a @{@link PropertyId}
- * annotation on the field. Fields that are not initialized (null) are built
- * using the field factory. All non-null fields for which a property id can
- * be determined are bound to the property id.
+ * {@link Field} and that can be mapped to a property id. Property ids are
+ * searched in the following order: @{@link PropertyId} annotations, exact
+ * field name matches and the case-insensitive matching that ignores
+ * underscores. Fields that are not initialized (null) are built using the
+ * field factory. All non-null fields for which a property id can be
+ * determined are bound to the property id.
* </p>
* <p>
* For example:
@@ -777,11 +779,12 @@ public class FieldGroup implements Serializable {
* member fields that have not been initialized.
* <p>
* This method processes all (Java) member fields whose type extends
- * {@link Field} and that can be mapped to a property id. Property id
- * mapping is done based on the field name or on a @{@link PropertyId}
- * annotation on the field. Fields that are not initialized (null) are built
- * using the field factory is buildFields is true. All non-null fields for
- * which a property id can be determined are bound to the property id.
+ * {@link Field} and that can be mapped to a property id. Property ids are
+ * searched in the following order: @{@link PropertyId} annotations, exact
+ * field name matches and the case-insensitive matching that ignores
+ * underscores. Fields that are not initialized (null) are built using the
+ * field factory is buildFields is true. All non-null fields for which a
+ * property id can be determined are bound to the property id.
* </p>
*
* @param objectWithMemberFields
@@ -792,6 +795,10 @@ public class FieldGroup implements Serializable {
*/
protected void buildAndBindMemberFields(Object objectWithMemberFields,
boolean buildFields) throws BindException {
+ if (getItemDataSource() == null) {
+ // no data source set, cannot find property ids
+ return;
+ }
Class<?> objectClass = objectWithMemberFields.getClass();
for (java.lang.reflect.Field memberField : getFieldsInDeclareOrder(objectClass)) {
@@ -812,7 +819,11 @@ public class FieldGroup implements Serializable {
// @PropertyId(propertyId) always overrides property id
propertyId = propertyIdAnnotation.value();
} else {
- propertyId = memberField.getName();
+ propertyId = findPropertyId(memberField);
+ if (propertyId == null) {
+ // Property id was not found, skip this field
+ continue;
+ }
}
// Ensure that the property id exists
@@ -873,6 +884,51 @@ public class FieldGroup implements Serializable {
}
}
+ /**
+ * Searches for a property id from the current itemDataSource that matches
+ * the given memberField.
+ * <p>
+ * If perfect match is not found, uses a case insensitive search that also
+ * ignores underscores. Returns null if no match is found. Throws a
+ * SearchException if no item data source has been set.
+ * </p>
+ * <p>
+ * The propertyId search logic used by
+ * {@link #buildAndBindMemberFields(Object, boolean)
+ * buildAndBindMemberFields} can easily be customized by overriding this
+ * method. No other changes are needed.
+ * </p>
+ *
+ * @param memberField
+ * The field an object id is searched for
+ * @return
+ */
+ protected Object findPropertyId(java.lang.reflect.Field memberField) {
+ String fieldName = memberField.getName();
+ if (getItemDataSource() == null) {
+ throw new SearchException(
+ "Property id type for field '"
+ + fieldName
+ + "' could not be determined. No item data source has been set.");
+ }
+ Item dataSource = getItemDataSource();
+ if (dataSource.getItemProperty(fieldName) != null) {
+ return fieldName;
+ } else {
+ String minifiedFieldName = fieldName.toLowerCase().replace("_", "");
+ for (Object itemPropertyId : dataSource.getItemPropertyIds()) {
+ if (itemPropertyId instanceof String) {
+ String itemPropertyName = (String) itemPropertyId;
+ if (minifiedFieldName.equals(itemPropertyName.toLowerCase()
+ .replace("_", ""))) {
+ return itemPropertyName;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
public static class CommitException extends Exception {
public CommitException() {
@@ -909,6 +965,18 @@ public class FieldGroup implements Serializable {
}
+ public static class SearchException extends RuntimeException {
+
+ public SearchException(String message) {
+ super(message);
+ }
+
+ public SearchException(String message, Throwable t) {
+ super(message, t);
+ }
+
+ }
+
/**
* Builds a field and binds it to the given property id using the field
* binder.
diff --git a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java
new file mode 100644
index 0000000000..9b768ef77f
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java
@@ -0,0 +1,84 @@
+package com.vaadin.tests.server.component.fieldgroup;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.vaadin.data.fieldgroup.FieldGroup;
+import com.vaadin.data.util.ObjectProperty;
+import com.vaadin.data.util.PropertysetItem;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.TextField;
+
+public class CaseInsensitiveBinding {
+
+ @Test
+ public void caseInsensitivityAndUnderscoreRemoval() {
+ PropertysetItem item = new PropertysetItem();
+ item.addItemProperty("LastName", new ObjectProperty<String>("Sparrow"));
+
+ class MyForm extends FormLayout {
+ TextField lastName = new TextField("Last name");
+
+ public MyForm() {
+
+ // Should bind to the LastName property
+ addComponent(lastName);
+ }
+ }
+
+ MyForm form = new MyForm();
+
+ FieldGroup binder = new FieldGroup(item);
+ binder.bindMemberFields(form);
+
+ assertTrue("Sparrow".equals(form.lastName.getValue()));
+ }
+
+ @Test
+ public void UnderscoreRemoval() {
+ PropertysetItem item = new PropertysetItem();
+ item.addItemProperty("first_name", new ObjectProperty<String>("Jack"));
+
+ class MyForm extends FormLayout {
+ TextField firstName = new TextField("First name");
+
+ public MyForm() {
+ // Should bind to the first_name property
+ addComponent(firstName);
+ }
+ }
+
+ MyForm form = new MyForm();
+
+ FieldGroup binder = new FieldGroup(item);
+ binder.bindMemberFields(form);
+
+ assertTrue("Jack".equals(form.firstName.getValue()));
+ }
+
+ @Test
+ public void perfectMatchPriority() {
+ PropertysetItem item = new PropertysetItem();
+ item.addItemProperty("first_name", new ObjectProperty<String>(
+ "Not this"));
+ item.addItemProperty("firstName", new ObjectProperty<String>("This"));
+
+ class MyForm extends FormLayout {
+ TextField firstName = new TextField("First name");
+
+ public MyForm() {
+ // should bind to the firstName property, not first_name property
+ addComponent(firstName);
+ }
+ }
+
+ MyForm form = new MyForm();
+
+ FieldGroup binder = new FieldGroup(item);
+ binder.bindMemberFields(form);
+
+ assertTrue("This".equals(form.firstName.getValue()));
+ }
+
+}
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java
index c86866b591..38915fe3e2 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java
@@ -31,25 +31,17 @@ import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
-import org.junit.Test;
import com.vaadin.sass.internal.ScssStylesheet;
+import com.vaadin.sass.testcases.scss.SassTestRunner.FactoryTest;
public abstract class AbstractDirectoryScanningSassTests extends TestCase {
- private String scssResourceName;
-
- protected AbstractDirectoryScanningSassTests(String scssResourceName) {
- this.scssResourceName = scssResourceName;
- }
-
- public static Collection<Object[]> getScssResourceNames(URL directoryUrl)
+ public static Collection<String> getScssResourceNames(URL directoryUrl)
throws URISyntaxException {
- List<Object[]> resources = new ArrayList<Object[]>();
- // temporary instance to enable subclasses to define where to scan for
- // files
+ List<String> resources = new ArrayList<String>();
for (File scssFile : getScssFiles(directoryUrl)) {
- resources.add(new Object[] { scssFile.getName() });
+ resources.add(scssFile.getName());
}
return resources;
}
@@ -72,8 +64,8 @@ public abstract class AbstractDirectoryScanningSassTests extends TestCase {
protected abstract URL getResourceURL(String path);
- @Test
- public void compareScssWithCss() throws Exception {
+ @FactoryTest
+ public void compareScssWithCss(String scssResourceName) throws Exception {
String referenceCss;
File scssFile = getSassLangResourceFile(scssResourceName);
File cssFile = getCssFile(scssFile);
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AutomaticSassTests.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AutomaticSassTests.java
index 1e5ec1ad37..fbccae349a 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AutomaticSassTests.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AutomaticSassTests.java
@@ -20,15 +20,11 @@ import java.net.URL;
import java.util.Collection;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-@RunWith(Parameterized.class)
-public class AutomaticSassTests extends AbstractDirectoryScanningSassTests {
+import com.vaadin.sass.testcases.scss.SassTestRunner.TestFactory;
- public AutomaticSassTests(String scssResourceName) {
- super(scssResourceName);
- }
+@RunWith(SassTestRunner.class)
+public class AutomaticSassTests extends AbstractDirectoryScanningSassTests {
@Override
protected URL getResourceURL(String path) {
@@ -39,8 +35,8 @@ public class AutomaticSassTests extends AbstractDirectoryScanningSassTests {
return AutomaticSassTests.class.getResource("/automatic" + path);
}
- @Parameters
- public static Collection<Object[]> getScssResourceNames()
+ @TestFactory
+ public static Collection<String> getScssResourceNames()
throws URISyntaxException {
return getScssResourceNames(getResourceURLInternal(""));
}
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java
index 7aabb9d23c..d0e53a8726 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java
@@ -20,15 +20,11 @@ import java.net.URL;
import java.util.Collection;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-@RunWith(Parameterized.class)
-public class SassLangTests extends AbstractDirectoryScanningSassTests {
+import com.vaadin.sass.testcases.scss.SassTestRunner.TestFactory;
- public SassLangTests(String scssResourceName) {
- super(scssResourceName);
- }
+@RunWith(SassTestRunner.class)
+public class SassLangTests extends AbstractDirectoryScanningSassTests {
@Override
protected URL getResourceURL(String path) {
@@ -39,8 +35,8 @@ public class SassLangTests extends AbstractDirectoryScanningSassTests {
return SassLangTests.class.getResource("/sasslang" + path);
}
- @Parameters
- public static Collection<Object[]> getScssResourceNames()
+ @TestFactory
+ public static Collection<String> getScssResourceNames()
throws URISyntaxException {
return getScssResourceNames(getResourceURLInternal(""));
}
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java
index 6e9ed007cd..a84a8ca814 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java
@@ -20,15 +20,11 @@ import java.net.URL;
import java.util.Collection;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-@RunWith(Parameterized.class)
-public class SassLangTestsBroken extends AbstractDirectoryScanningSassTests {
+import com.vaadin.sass.testcases.scss.SassTestRunner.TestFactory;
- public SassLangTestsBroken(String scssResourceName) {
- super(scssResourceName);
- }
+@RunWith(SassTestRunner.class)
+public class SassLangTestsBroken extends AbstractDirectoryScanningSassTests {
@Override
protected URL getResourceURL(String path) {
@@ -39,8 +35,8 @@ public class SassLangTestsBroken extends AbstractDirectoryScanningSassTests {
return SassLangTestsBroken.class.getResource("/sasslangbroken" + path);
}
- @Parameters
- public static Collection<Object[]> getScssResourceNames()
+ @TestFactory
+ public static Collection<String> getScssResourceNames()
throws URISyntaxException {
return getScssResourceNames(getResourceURLInternal(""));
}
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassTestRunner.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassTestRunner.java
new file mode 100644
index 0000000000..f871d43b6c
--- /dev/null
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassTestRunner.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2012 Vaadin Ltd.
+ *
+ * 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 com.vaadin.sass.testcases.scss;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.Parameterized;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+
+/**
+ * Test runner that executes methods annotated with @{@link FactoryTest} with
+ * all the values returned by a method annotated with @{@link TestFactory} as
+ * their parameters parameter.
+ *
+ * This runner is loosely based on FactoryTestRunner by Ted Young
+ * (http://tedyoung.me/2011/01/23/junit-runtime-tests-custom-runners/). The
+ * generated test names give information about the parameters used (unlike
+ * {@link Parameterized}).
+ *
+ * @since 7.0
+ */
+public class SassTestRunner extends BlockJUnit4ClassRunner {
+
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface TestFactory {
+ }
+
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface FactoryTest {
+ }
+
+ public SassTestRunner(Class<?> klass) throws InitializationError {
+ super(klass);
+ }
+
+ @Override
+ protected List<FrameworkMethod> computeTestMethods() {
+ List<FrameworkMethod> tests = new LinkedList<FrameworkMethod>();
+
+ // Final all methods in our test class marked with @TestFactory.
+ for (FrameworkMethod method : getTestClass().getAnnotatedMethods(
+ TestFactory.class)) {
+ // Make sure the TestFactory method is static
+ if (!Modifier.isStatic(method.getMethod().getModifiers())) {
+ throw new IllegalArgumentException("TestFactory " + method
+ + " must be static.");
+ }
+
+ // Execute the method (statically)
+ Object params;
+ try {
+ params = method.getMethod().invoke(
+ getTestClass().getJavaClass());
+ } catch (Throwable t) {
+ throw new RuntimeException("Could not run test factory method "
+ + method.getName());
+ }
+
+ // Did the factory return an array? If so, make it a list.
+ if (params.getClass().isArray()) {
+ params = Arrays.asList((Object[]) params);
+ }
+
+ // Did the factory return a scalar object? If so, put it in a list.
+ if (!(params instanceof Iterable<?>)) {
+ params = Collections.singletonList(params);
+ }
+
+ // For each object returned by the factory.
+ for (Object param : (Iterable<?>) params) {
+ // Find any methods marked with @SassTest.
+ for (FrameworkMethod m : getTestClass().getAnnotatedMethods(
+ FactoryTest.class)) {
+ tests.add(new ParameterizedFrameworkMethod(m.getMethod(),
+ new Object[] { param }));
+ }
+ }
+ }
+
+ return tests;
+ }
+
+ private static class ParameterizedFrameworkMethod extends FrameworkMethod {
+ private Object[] params;
+
+ public ParameterizedFrameworkMethod(Method method, Object[] params) {
+ super(method);
+ this.params = params;
+ }
+
+ @Override
+ public Object invokeExplosively(Object target, Object... params)
+ throws Throwable {
+ // Executes the test method with the supplied parameters (returned
+ // by the
+ // TestFactory) and not the instance generated by FrameworkMethod.
+ return super.invokeExplosively(target, this.params);
+ }
+
+ @Override
+ public String getName() {
+ return String.format("%s[%s]", getMethod().getName(),
+ Arrays.toString(params));
+ }
+ }
+}