diff --git a/functional-test/pom.xml b/functional-test/pom.xml index 98c78c18e7..9f19ef3efc 100644 --- a/functional-test/pom.xml +++ b/functional-test/pom.xml @@ -194,6 +194,11 @@ resteasy-jaxb-provider + + org.zanata + zanata-adapter-po + + org.zanata @@ -204,10 +209,6 @@ org.jboss.resteasy jaxrs-api - - org.jboss.resteasy - resteasy-jaxrs - org.jboss.seam jboss-seam diff --git a/functional-test/sample-projects/plural/pl/test.po b/functional-test/sample-projects/plural/pl/test.po index f1428bd515..5978055144 100644 --- a/functional-test/sample-projects/plural/pl/test.po +++ b/functional-test/sample-projects/plural/pl/test.po @@ -15,39 +15,64 @@ msgid "One file removed" msgid_plural "%d files removed" msgstr[0] "1 aoeuaouaou" msgstr[1] "%d aoeuaouao" +msgstr[2] "" #, c-format msgid "One other file removed" msgid_plural "%d files removed" msgstr[0] "" msgstr[1] "%d aoeuaouao" +msgstr[2] "" #, c-format msgid "One file removed_" msgid_plural "%d more files removed" msgstr[0] "1 tnhdaoeuaoue" msgstr[1] "" +msgstr[2] "" #, c-format msgid "File removed" msgid_plural "%d files removed" msgstr[0] "1 tnhdnthdntioei" +msgstr[1] "" +msgstr[2] "" -#, c-format, fuzzy +#, fuzzy, c-format msgid "Yet Another File removed" msgid_plural "%d files removed" msgstr[0] "%d aoenuthaosneuh" +msgstr[1] "" +msgstr[2] "" -#, c-format, fuzzy +#, c-format msgid "2 Another File removed" msgid_plural "%d files removed" msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#, c-format +msgid "msgid" +msgstr "" + +#, c-format +msgid "%d files removed" +msgid_plural "One file removed" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" -# not legal because it has multiple msgstr but no msgid_plural: #, c-format -#msgid "File removed." -#msgstr[0] "" -#msgstr[1] "" +msgid "aaaaaaaaaaaa" +msgid_plural "bbbbbbbbbbb" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" -msgid "pl msgid" -msgstr "pl msgstr" +#, c-format +msgid "bbbbbbbbbbb" +msgid_plural "aaaaaaaaaaaa" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" diff --git a/functional-test/sample-projects/plural/pom.xml b/functional-test/sample-projects/plural/pom.xml index c1284a463f..da53efa558 100644 --- a/functional-test/sample-projects/plural/pom.xml +++ b/functional-test/sample-projects/plural/pom.xml @@ -4,6 +4,10 @@ null 0 + + pot + . + @@ -11,8 +15,8 @@ zanata-maven-plugin ${zanata.client.version} - pot - . + ${zanata.srcDir} + ${zanata.transDir} diff --git a/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java b/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java index dbee1e5d53..c8621cb641 100644 --- a/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java +++ b/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java @@ -11,6 +11,7 @@ import org.zanata.util.WebElementUtil; import com.google.common.base.Function; import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import lombok.extern.slf4j.Slf4j; @@ -24,6 +25,7 @@ public class EditorPage extends BasePage { private static final String SOURCE_ID_FMT = "gwt-debug-%d-source-panel-%d-container"; private static final int SINGULAR = 0; + private static final int PLURAL = 1; // first %d is row index, second %d is plural form index (i.e. 0-6) private static final String TARGET_ID_FMT = "gwt-debug-%d-target-%d"; @@ -83,6 +85,7 @@ public List> apply(WebDriver input) { * Get content of a text flow source at given row. * This assumes the text flow has singular form (i.e. no plural). * If a test requires to access plural content, this can be changed. + * TODO pahuang rename method name * * @param rowIndex * row index @@ -92,6 +95,13 @@ public String getTranslationSourceAtRowIndex(final int rowIndex) { return getCodeMirrorContent(rowIndex, SOURCE_ID_FMT, SINGULAR); } + public String getMessageSourceAtRowIndex(int rowIndex, int pluralIndex) { + Preconditions.checkArgument( + pluralIndex == SINGULAR || pluralIndex == PLURAL, + "plural index must be 0 or 1"); + return getCodeMirrorContent(rowIndex, SOURCE_ID_FMT, pluralIndex); + } + /** * Get content of a text flow target at given row. * This assumes the text flow has singular form (i.e. no plural). @@ -120,4 +130,10 @@ public String apply(WebDriver input) { } }); } + + public String getMessageTargetAtRowIndex(int rowIndex, int pluralIndex) { + Preconditions.checkArgument(pluralIndex >= 0 && pluralIndex <= 6, + "plural index must be in range [0,6]"); + return getCodeMirrorContent(rowIndex, TARGET_ID_FMT, pluralIndex); + } } diff --git a/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java b/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java index 3f611eacbe..a5d7dca8bb 100644 --- a/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java +++ b/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java @@ -24,6 +24,18 @@ import java.io.*; import java.nio.charset.Charset; +import java.util.List; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlElements; +import javax.xml.bind.annotation.XmlRootElement; + +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import lombok.Setter; /** * Create and manipulate basic text files for testing. @@ -154,4 +166,53 @@ public String getFirstFileNameInDirectory(String directory) { + " but none found."); } } + + /** + * Generates a zanata.xml with url default to test instance. + * + * @param output where to write it + * @param projectSlug project slug + * @param versionSlug version slug + * @param projectType project type + * @param locales locales + */ + public void generateZanataXml(File output, String projectSlug, String versionSlug, String projectType, List locales) { + ZanataXml zanataXml = new ZanataXml(); + zanataXml.setProject(projectSlug); + zanataXml.setProjectVersion(versionSlug); + zanataXml.setProjectType(projectType); + zanataXml.setLocales(locales); + marshall(output, zanataXml, ZanataXml.class); + } + + private static void marshall(File output, T object, Class xmlClass) { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(xmlClass); + Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); + jaxbMarshaller.marshal(object, output); + } + catch (JAXBException e) { + throw Throwables.propagate(e); + } + } + + @XmlRootElement(namespace = "http://zanata.org/namespace/config/", + name = "config") + @Setter + private static class ZanataXml { + @XmlElement + private String url = PropertiesHolder + .getProperty(Constants.zanataInstance.value()); + @XmlElement + private String project; + @XmlElement(name = "project-version") + private String projectVersion; + @XmlElement(name = "project-type") + private String projectType; + @XmlElementWrapper(name="locales") + @XmlElements( + @XmlElement(name = "locale") + ) + private List locales; + } } diff --git a/functional-test/src/test/java/org/zanata/feature/clientserver/GettextPluralSupportTest.java b/functional-test/src/test/java/org/zanata/feature/clientserver/GettextPluralSupportTest.java new file mode 100644 index 0000000000..afa960b09b --- /dev/null +++ b/functional-test/src/test/java/org/zanata/feature/clientserver/GettextPluralSupportTest.java @@ -0,0 +1,149 @@ +package org.zanata.feature.clientserver; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; + +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.xml.sax.InputSource; +import org.zanata.adapter.po.PoReader2; +import org.zanata.common.LocaleId; +import org.zanata.feature.BasicAcceptanceTest; +import org.zanata.page.webtrans.EditorPage; +import org.zanata.rest.dto.resource.TextFlow; +import org.zanata.rest.dto.resource.TextFlowTarget; +import org.zanata.util.SampleProjectRule; +import org.zanata.util.ZanataRestCaller; +import org.zanata.workflow.BasicWorkFlow; +import org.zanata.workflow.ClientPushWorkFlow; +import org.zanata.workflow.LoginWorkFlow; +import com.google.common.io.Files; + +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * This covers TCMS case 217601 and case + * 217905 + * + * @author Patrick Huang pahuang@redhat.com + */ +@Category(BasicAcceptanceTest.class) +public class GettextPluralSupportTest { + @Rule + public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + + private ClientPushWorkFlow client = new ClientPushWorkFlow(); + private ZanataRestCaller restCaller; + + private File tempDir = Files.createTempDir(); + + private String userConfigPath = ClientPushWorkFlow + .getUserConfigPath("admin"); + private File projectRootPath; + + @Before + public void setUp() throws IOException { + projectRootPath = client.getProjectRootPath("plural"); + Files.copy(new File(projectRootPath, "pom.xml"), new File(tempDir, + "pom.xml")); + Files.copy(new File(projectRootPath, "zanata.xml"), new File(tempDir, + "zanata.xml")); + File potDir = new File(tempDir, "pot"); + potDir.mkdirs(); + File plDir = new File(tempDir, "pl"); + plDir.mkdirs(); + Files.copy(new File(projectRootPath + "/pot", "test.pot"), new File( + potDir, "test.pot")); + Files.copy(new File(projectRootPath + "/pl", "test.po"), new File( + plDir, "test.po")); + restCaller = new ZanataRestCaller("admin"); + } + + @Test + public void canPushAndPullPlural() throws IOException { + restCaller.createProjectAndVersion("plurals", "master", "podir"); + List output = + client.callWithTimeout(tempDir, + "mvn -B zanata:push -Dzanata.pushType=both -Dzanata.userConfig=" + + userConfigPath); + + assertThat(client.isPushSuccessful(output), Matchers.is(true)); + + EditorPage editorPage = verifyPluralPushedToEditor(); + + File pullDir = Files.createTempDir(); + String pullDirPath = pullDir.getAbsolutePath(); + String command = + "mvn -B zanata:pull -Dzanata.pullType=both -Dzanata.srcDir=" + + pullDirPath + " -Dzanata.transDir=" + pullDirPath + + " -Dzanata.userConfig=" + userConfigPath; + output = client.callWithTimeout(tempDir, command); + assertThat(client.isPushSuccessful(output), Matchers.is(true)); + + // source round trip + List originalTextFlows = + getTextFlows(new File(projectRootPath + "/pot/test.pot")); + List pulledTextFlows = + getTextFlows(new File(pullDir, "test.pot")); + assertThat(pulledTextFlows, Matchers.equalTo(originalTextFlows)); + + // translation round trip + List originalTargets = + getTextFlowTargets(new File(projectRootPath + "/pl/test.po")); + List pulledTargets = + getTextFlowTargets(new File(pullDir + "/pl/test.po")); + assertThat(pulledTargets, Matchers.equalTo(originalTargets)); + + // TODO translate some text in UI and then pull and compare + } + + private static EditorPage verifyPluralPushedToEditor() { + // first message + // msgid "One file removed" + // msgid_plural "%d files removed" + // msgstr[0] "1 aoeuaouaou" + // msgstr[1] "%d aoeuaouao" + + new LoginWorkFlow().signIn("admin", "admin"); + EditorPage editorPage = + new BasicWorkFlow().goToPage(String.format( + BasicWorkFlow.EDITOR_TEMPLATE, "plurals", "master", + "pl", "test"), EditorPage.class); + + assertThat(editorPage.getMessageSourceAtRowIndex(0, 0), + Matchers.equalTo("One file removed")); + assertThat(editorPage.getMessageSourceAtRowIndex(0, 1), + Matchers.equalTo("%d files removed")); + // nplural for Polish is 3 + assertThat(editorPage.getMessageTargetAtRowIndex(0, 0), + Matchers.equalTo("1 aoeuaouaou")); + assertThat(editorPage.getMessageTargetAtRowIndex(0, 1), + Matchers.equalTo("%d aoeuaouao")); + assertThat(editorPage.getMessageTargetAtRowIndex(0, 2), + Matchers.equalTo(" ")); + + return editorPage; + } + + private static List getTextFlows(File file) + throws FileNotFoundException { + return new PoReader2().extractTemplate( + new InputSource(new FileInputStream(file)), LocaleId.EN_US, + file.getName()).getTextFlows(); + } + + private static List getTextFlowTargets(File file) + throws FileNotFoundException { + return new PoReader2().extractTarget( + new InputSource(new FileInputStream(file))) + .getTextFlowTargets(); + } +}