From 1ee339cbbda9294078ba71285c0eb23cc5156751 Mon Sep 17 00:00:00 2001 From: jackyq2015 Date: Wed, 28 Jun 2017 08:12:36 -0400 Subject: [PATCH] cross function test suite. #1204 --- .../google/refine/expr/functions/Cross.java | 2 +- .../expr/functions/CrossFunctionTests.java | 208 ++++++++++++++++++ 2 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 main/tests/server/src/com/google/refine/tests/expr/functions/CrossFunctionTests.java diff --git a/main/src/com/google/refine/expr/functions/Cross.java b/main/src/com/google/refine/expr/functions/Cross.java index e04ed5e0a1f4..b8e203f00dc2 100644 --- a/main/src/com/google/refine/expr/functions/Cross.java +++ b/main/src/com/google/refine/expr/functions/Cross.java @@ -79,7 +79,7 @@ public void write(JSONWriter writer, Properties options) throws JSONException { writer.object(); - writer.key("description"); writer.value("join with anothe project by column"); + writer.key("description"); writer.value("join with another project by column"); writer.key("params"); writer.value("cell c, string projectName, string columnName"); writer.key("returns"); writer.value("array"); writer.endObject(); diff --git a/main/tests/server/src/com/google/refine/tests/expr/functions/CrossFunctionTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/CrossFunctionTests.java new file mode 100644 index 000000000000..663ea34b2aa9 --- /dev/null +++ b/main/tests/server/src/com/google/refine/tests/expr/functions/CrossFunctionTests.java @@ -0,0 +1,208 @@ + +package com.google.refine.tests.expr.functions; + +import static org.mockito.Mockito.mock; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Properties; + +import org.json.JSONObject; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.ProjectManager; +import com.google.refine.ProjectMetadata; +import com.google.refine.RefineServlet; +import com.google.refine.expr.EvalError; +import com.google.refine.expr.HasFieldsListImpl; +import com.google.refine.expr.WrappedRow; +import com.google.refine.grel.ControlFunctionRegistry; +import com.google.refine.grel.Function; +import com.google.refine.importers.SeparatorBasedImporter; +import com.google.refine.importing.ImportingJob; +import com.google.refine.importing.ImportingManager; +import com.google.refine.model.Project; +import com.google.refine.model.Row; +import com.google.refine.tests.ProjectManagerStub; +import com.google.refine.tests.RefineServletStub; +import com.google.refine.tests.RefineTest; + +/** + * Test cases for cross function. + */ +public class CrossFunctionTests extends RefineTest { + static Properties bindings; + + @Override + @BeforeTest + public void init() { + logger = LoggerFactory.getLogger(this.getClass()); + } + + // dependencies + RefineServlet servlet; + Project projectGift; + Project projectAddress; + ProjectMetadata metadata; + ImportingJob job; + JSONObject options; + SeparatorBasedImporter importer; + + @BeforeMethod + public void SetUp() { + bindings = new Properties(); + + servlet = new RefineServletStub(); + ProjectManager.singleton = new ProjectManagerStub(); + ImportingManager.initialize(servlet); + projectAddress = new Project(); + + job = ImportingManager.createJob(); + options = mock(JSONObject.class); + importer = new SeparatorBasedImporter(); + + createMyAddressBook(); + projectGift = createChristmasGifts(); + bindings.put("project", projectGift); + + // add a column address based on column recipient + bindings.put("columnName", "recipient"); + } + + // data from: https://github.com/OpenRefine/OpenRefine/wiki/GREL-Other-Functions + private Project createMyAddressBook() { + String projectName = "My Address Book"; + String input = "friend;address\n" + + "john;120 Main St.\n" + + "mary;50 Broadway Ave.\n" + + "john;999 XXXXXX St.\n" // john's 2nd address + + "anne;17 Morning Crescent\n"; + return createProject(projectName, input); + } + + private Project createChristmasGifts() { + String projectName = "Christmas Gifts"; + String input = "gift;recipient\n" + + "lamp;mary\n" + + "clock;john\n"; + return createProject(projectName, input); + } + + private Project createProject(String projectName, String input) { + Project project = new Project(); + ProjectMetadata metadata = new ProjectMetadata(); + + metadata.setName(projectName); + prepareOptions(";", -1, 0, 0, 1, false, false); + List exceptions = new ArrayList(); + importer.parseOneFile(project, metadata, job, "filesource", new StringReader(input), -1, options, exceptions); + project.update(); + ProjectManager.singleton.registerProject(project, metadata); + + return project; + } + + @AfterMethod + public void TearDown() { + ImportingManager.disposeJob(job.id); + ProjectManager.singleton.deleteProject(projectGift.id); + ProjectManager.singleton.deleteProject(projectAddress.id); + job = null; + metadata = null; + projectGift = null; + projectAddress = null; + options = null; + importer = null; + } + + + @Test + public void crossFunctionOneToOneTest() throws Exception { + Row row = ((Row)((WrappedRow) ((HasFieldsListImpl) invoke("cross", "mary", "My Address Book", "friend")).get(0)).row); + String address = row.getCell(1).value.toString(); + Assert.assertEquals(address, "50 Broadway Ave."); + } + + /** + * To demonstrate that the cross function can join multiple rows. + */ + @Test + public void crossFunctionOneToManyTest() throws Exception { + Row row = ((Row)((WrappedRow) ((HasFieldsListImpl) invoke("cross", "john", "My Address Book", "friend")).get(1)).row); + String address = row.getCell(1).value.toString(); + Assert.assertEquals(address, "999 XXXXXX St."); + } + + + @Test + public void crossFunctionCaseSensitiveTest() throws Exception { + Assert.assertNull(invoke("cross", "Anne", "My Address Book", "friend")); + } + + /** + * If no match, return null. + * + * But if user still apply grel:value.cross("My Address Book", "friend")[0].cells["address"].value, + * from the "Preview", the target cell shows "Error: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0". + * It will still end up with blank if the onError set so. + */ + @Test + public void crossFunctionMatchNotFoundTest() throws Exception { + Assert.assertNull(invoke("cross", "NON-EXIST", "My Address Book", "friend")); + } + + /** + * + * rest of cells shows "Error: cross expects a string, a project name to join with, and a column name in that project" + */ + @Test + public void crossFunctionNonLiteralValue() throws Exception { + Assert.assertEquals(((EvalError) invoke("cross", 1, "My Address Book", "friend")).message, + "cross expects a string, a project name to join with, and a column name in that project"); + + Assert.assertEquals(((EvalError) invoke("cross", null, "My Address Book", "friend")).message, + "cross expects a string, a project name to join with, and a column name in that project"); + + Assert.assertEquals(((EvalError) invoke("cross", Calendar.getInstance(), "My Address Book", "friend")).message, + "cross expects a string, a project name to join with, and a column name in that project"); + } + + /** + * Lookup a control function by name and invoke it with a variable number of args + */ + private static Object invoke(String name,Object... args) { + // registry uses static initializer, so no need to set it up + Function function = ControlFunctionRegistry.getFunction(name); + if (function == null) { + throw new IllegalArgumentException("Unknown function "+name); + } + if (args == null) { + return function.call(bindings,new Object[0]); + } else { + return function.call(bindings,args); + } + } + + + private void prepareOptions( + String sep, int limit, int skip, int ignoreLines, + int headerLines, boolean guessValueType, boolean ignoreQuotes) { + + whenGetStringOption("separator", options, sep); + whenGetIntegerOption("limit", options, limit); + whenGetIntegerOption("skipDataLines", options, skip); + whenGetIntegerOption("ignoreLines", options, ignoreLines); + whenGetIntegerOption("headerLines", options, headerLines); + whenGetBooleanOption("guessCellValueTypes", options, guessValueType); + whenGetBooleanOption("processQuotes", options, !ignoreQuotes); + whenGetBooleanOption("storeBlankCellsAsNulls", options, true); + } + +}