From 92e53d8d437dc58c93f11af8a8f2d67b52b00d7d Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 13 Dec 2023 14:54:29 -0600 Subject: [PATCH 1/4] CLDR-17287 basic performance test for the back end - execute a bunch of votes all in a row, which calls STFactory.PerLocaleData.voteForvalueWithType() --- .../cldr/unittest/web/TestSTFactory.java | 5 +++ .../cldr/unittest/web/data/TestVotePerf.xml | 45 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml diff --git a/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java b/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java index 357ac5e4224..8d3ac0d5d30 100644 --- a/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java +++ b/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java @@ -451,6 +451,11 @@ public void TestVettingDataDriven() throws SQLException, IOException { runDataDrivenTest(TestSTFactory.class.getSimpleName()); // TestSTFactory.xml } + public void TestVotePerf() throws SQLException, IOException { + if (TestAll.skipIfNoDb()) return; + runDataDrivenTest("TestVotePerf"); // TestSTFactory.xml + } + public void TestUserRegistry() throws SQLException, IOException { if (TestAll.skipIfNoDb()) return; runDataDrivenTest("TestUserRegistry"); diff --git a/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml b/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml new file mode 100644 index 00000000000..c9ac24ea9c7 --- /dev/null +++ b/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml @@ -0,0 +1,45 @@ + + + + + + test performance + + + + Territory-001 + Territory-002 + Territory-003 + Territory-005 + Territory-009 + Territory-011 + Territory-013 + Territory-014 + Territory-015 + Territory-017 + Territory-018 + Territory-019 + Territory-021 + Territory-029 + Territory-030 + Territory-034 + Territory-035 + Territory-039 + Territory-053 + Territory-054 + Territory-057 + Territory-061 + Territory-142 + Territory-143 + Territory-145 + Territory-150 + Territory-151 + Territory-154 + Territory-155 + Territory-202 + Territory-419 + + verify something + Territory-001 + + From ff320de84765115f880f2cc659a8efa39455cfe6 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 14 Dec 2023 12:24:35 -0600 Subject: [PATCH 2/4] CLDR-17287 API performance test for the back end - refactor VoteAPIHelper to be callable from unit tests outside of a web server - improve error results from ST --- .../org/unicode/cldr/web/SurveyException.java | 3 +- .../org/unicode/cldr/web/api/STError.java | 24 ++++- .../unicode/cldr/web/api/VoteAPIHelper.java | 27 ++++-- .../cldr/unittest/web/TestSTFactory.java | 41 ++++++++ .../cldr/unittest/web/data/TestSTFactory.dtd | 23 ++++- .../cldr/unittest/web/data/TestVotePerf.xml | 96 ++++++++++++------- .../java/org/unicode/cldr/test/CheckCLDR.java | 11 ++- 7 files changed, 179 insertions(+), 46 deletions(-) diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/SurveyException.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/SurveyException.java index 02b1f13b689..98160a37488 100644 --- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/SurveyException.java +++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/SurveyException.java @@ -31,7 +31,8 @@ public enum ErrorCode { E_BAD_VALUE, E_BAD_XPATH, E_NO_OLD_VOTES, - E_PERMANENT_VOTE_NO_FORUM; + E_PERMANENT_VOTE_NO_FORUM, + E_VOTE_NOT_ACCEPTED; } private final ErrorCode err_code; diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/STError.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/STError.java index 225061fad25..4ba276dc3f8 100644 --- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/STError.java +++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/STError.java @@ -1,6 +1,8 @@ package org.unicode.cldr.web.api; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.unicode.cldr.web.SurveyException; import org.unicode.cldr.web.SurveyException.ErrorCode; @@ -64,7 +66,27 @@ public void setMessage(String message) { * @return */ public Response build() { - return Response.serverError().entity(this).build(); + return Response.status(getStatus()).entity(this).build(); + } + + /** the error as a Status */ + public Status getStatus() { + switch(code) { + case E_BAD_LOCALE: + case E_BAD_SECTION: + case E_BAD_XPATH: + return Response.Status.NOT_FOUND; + case E_NO_PERMISSION: + return Response.Status.FORBIDDEN; + case E_BAD_VALUE: + case E_VOTE_NOT_ACCEPTED: + return Response.Status.NOT_ACCEPTABLE; + case E_SESSION_DISCONNECTED: + case E_NOT_LOGGED_IN: + return Response.Status.UNAUTHORIZED; + default: + return Response.Status.INTERNAL_SERVER_ERROR; + } } public static Response surveyNotQuiteReady() { diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java index 4a189ca3477..d154b0dfeca 100644 --- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java +++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java @@ -372,18 +372,33 @@ private static Dashboard.ReviewNotification[] getOnePathDash( // separated from // the HTTP response concerns, so that VoteAPI and XPathAlt can share code without the // awkwardness of forbiddenIsOk. - static Response handleVote( + public static Response handleVote( String loc, String xp, String value, int voteLevelChanged, final CookieSession mySession, boolean forbiddenIsOk) { + // translate this call into jax-rs Response + try { + final VoteResponse r = getHandleVoteResponse(loc, xp, value, voteLevelChanged, mySession, forbiddenIsOk); + return Response.ok(r).build(); + } catch (Throwable se) { + return new STError(se).build(); + } + } + /** this function is the implementation of handleVote() but does not use any jax-rs, for unit tests */ + public static VoteResponse getHandleVoteResponse(String loc, + String xp, + String value, + int voteLevelChanged, + final CookieSession mySession, + boolean forbiddenIsOk) throws SurveyException { VoteResponse r = new VoteResponse(); mySession.userDidAction(); CLDRLocale locale = CLDRLocale.getInstance(loc); if (!UserRegistry.userCanModifyLocale(mySession.user, locale)) { - return Response.status(Status.FORBIDDEN).build(); + throw new SurveyException(ErrorCode.E_NO_PERMISSION, "Not allowed to modify " + locale); } loc = locale.getBaseName(); // sanitized final SurveyMain sm = CookieSession.sm; @@ -445,15 +460,13 @@ static Response handleVote( } } catch (Throwable t) { SurveyLog.logException(logger, t, "Processing submission " + locale + ":" + xp); - return (new STError(t).build()); + throw new SurveyException(ErrorCode.E_INTERNAL, "Processing submission " + locale + ":" + xp); } } if (!forbiddenIsOk && r.statusAction.isForbidden()) { - return Response.status(Response.Status.NOT_ACCEPTABLE) - .entity(new STError("Status action is forbidden: " + r.statusAction)) - .build(); + throw new SurveyException(ErrorCode.E_VOTE_NOT_ACCEPTED, "Status action is forbidden: " + r.statusAction); } - return Response.ok(r).build(); + return r; } private static void normalizedToZeroLengthError(VoteResponse r, List result) { diff --git a/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java b/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java index 8d3ac0d5d30..5936ed1c2a9 100644 --- a/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java +++ b/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java @@ -10,8 +10,13 @@ import java.util.Date; import java.util.Map; import java.util.TreeMap; + +import javax.ws.rs.core.Response; + import org.unicode.cldr.draft.FileUtilities; +import org.unicode.cldr.test.CheckCLDR; import org.unicode.cldr.unittest.web.TestAll.WebTestInfo; +import org.unicode.cldr.util.CLDRConfig; import org.unicode.cldr.util.CLDRFile; import org.unicode.cldr.util.CLDRFile.DraftStatus; import org.unicode.cldr.util.CLDRLocale; @@ -28,6 +33,7 @@ import org.unicode.cldr.web.BallotBox; import org.unicode.cldr.web.BallotBox.InvalidXPathException; import org.unicode.cldr.web.BallotBox.VoteNotAcceptedException; +import org.unicode.cldr.web.SurveyMain.Phase; import org.unicode.cldr.web.CookieSession; import org.unicode.cldr.web.DBUtils; import org.unicode.cldr.web.STFactory; @@ -36,6 +42,8 @@ import org.unicode.cldr.web.UserRegistry; import org.unicode.cldr.web.UserRegistry.LogoutException; import org.unicode.cldr.web.UserRegistry.User; +import org.unicode.cldr.web.api.VoteAPI; +import org.unicode.cldr.web.api.VoteAPIHelper; import org.unicode.cldr.web.XPathTable; public class TestSTFactory extends TestFmwk { @@ -453,6 +461,8 @@ public void TestVettingDataDriven() throws SQLException, IOException { public void TestVotePerf() throws SQLException, IOException { if (TestAll.skipIfNoDb()) return; + final CheckCLDR.Phase p = CLDRConfig.getInstance().getPhase(); + assertTrue("phase " + p + ".isUnitTest()", p.isUnitTest()); runDataDrivenTest("TestVotePerf"); // TestSTFactory.xml } @@ -550,6 +560,36 @@ public void handlePathValue(String path, String value) { + xpath); } break; + case "apivote": + case "apiunvote": + { + UserRegistry.User u = getUserFromAttrs(attrs, "name"); + CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); + boolean needException = + getBooleanAttr(attrs, "exception", false); + if (elem.equals("apiunvote")) { + value = null; + } + final CookieSession mySession = CookieSession.newSession(u, "999.999.999.999"); + try { + final VoteAPI.VoteResponse r = VoteAPIHelper.getHandleVoteResponse(locale.getBaseName(), xpath, value, 0, mySession, false); + final boolean isOk = r.didVote; + final boolean asExpected = (isOk == !needException); + if (!asExpected) { + errln("exception=" + needException + " but got status " + r.didNotSubmit + " - " + r.toString()); + } else { + logln(" status = " + r.didNotSubmit); + } + } catch(Throwable iae) { + if (needException == true) { + logln("Caught expected: " + iae); + } else { + iae.printStackTrace(); + errln("Unexpected exception: " + iae); + } + } + } + break; case "vote": case "unvote": { @@ -592,6 +632,7 @@ public void handlePathValue(String path, String value) { logln(u + " " + elem + "d for " + xpath + " = " + value); } break; + case "apiverify": // TODO case "verify": { value = value.trim(); diff --git a/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestSTFactory.dtd b/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestSTFactory.dtd index efb55401fab..037c60fcf24 100644 --- a/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestSTFactory.dtd +++ b/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestSTFactory.dtd @@ -8,7 +8,7 @@ THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. --> - + @@ -23,6 +23,20 @@ Except as contained in this notice, the name of a copyright holder shall not be + + + + + + + + + + + + + + @@ -35,6 +49,13 @@ Except as contained in this notice, the name of a copyright holder shall not be + + + + + + + diff --git a/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml b/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml index c9ac24ea9c7..3d4a93ed041 100644 --- a/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml +++ b/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml @@ -7,39 +7,69 @@ - Territory-001 - Territory-002 - Territory-003 - Territory-005 - Territory-009 - Territory-011 - Territory-013 - Territory-014 - Territory-015 - Territory-017 - Territory-018 - Territory-019 - Territory-021 - Territory-029 - Territory-030 - Territory-034 - Territory-035 - Territory-039 - Territory-053 - Territory-054 - Territory-057 - Territory-061 - Territory-142 - Territory-143 - Territory-145 - Territory-150 - Territory-151 - Territory-154 - Territory-155 - Territory-202 - Territory-419 + Territory-001 + Territory-002 + Territory-003 + Territory-005 + Territory-009 + Territory-011 + Territory-013 + Territory-014 + Territory-015 + Territory-017 + Territory-018 + Territory-019 + Territory-021 + Territory-029 + Territory-030 + Territory-034 + Territory-035 + Territory-039 + Territory-053 + Territory-054 + Territory-057 + Territory-061 + Territory-142 + Territory-143 + Territory-145 + Territory-150 + Territory-151 + Territory-154 + Territory-155 + Territory-202 + Territory-419 - verify something - Territory-001 + verify them + Territory-001 + diff --git a/tools/cldr-code/src/main/java/org/unicode/cldr/test/CheckCLDR.java b/tools/cldr-code/src/main/java/org/unicode/cldr/test/CheckCLDR.java index 2e75cf38633..d98b42a0a94 100644 --- a/tools/cldr-code/src/main/java/org/unicode/cldr/test/CheckCLDR.java +++ b/tools/cldr-code/src/main/java/org/unicode/cldr/test/CheckCLDR.java @@ -160,6 +160,11 @@ public static Phase forString(String value) { return result != null ? result : Phase.valueOf(value); } + /** true if it's a 'unit test' phase. */ + public boolean isUnitTest() { + return this == BUILD || this == FINAL_TESTING; + } + /** * Return whether or not to show a row, and if so, how. * @@ -199,7 +204,7 @@ public StatusAction getShowRowAction( } // always forbid bulk import except in data submission. - if (inputMethod == InputMethod.BULK && this != Phase.SUBMISSION) { + if (inputMethod == InputMethod.BULK && (this != Phase.SUBMISSION && isUnitTest())) { return StatusAction.FORBID_UNLESS_DATA_SUBMISSION; } @@ -222,7 +227,7 @@ public StatusAction getShowRowAction( } } - if (this == Phase.SUBMISSION) { + if (this == Phase.SUBMISSION || isUnitTest()) { return (ph.canReadAndWrite()) ? StatusAction.ALLOW : StatusAction.ALLOW_VOTING_AND_TICKET; @@ -288,7 +293,7 @@ public StatusAction getAcceptNewItemAction( } // Allow items if submission - if (this == Phase.SUBMISSION) { + if (this == Phase.SUBMISSION || isUnitTest()) { return StatusAction.ALLOW; } From 7bc7af9bfe600242eeee045f915b95e332009bd9 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 14 Dec 2023 15:00:45 -0600 Subject: [PATCH 3/4] CLDR-17287 API performance test for the back end - update apiverify to verify via API --- .../org/unicode/cldr/web/CookieSession.java | 9 + .../org/unicode/cldr/web/api/STError.java | 3 +- .../unicode/cldr/web/api/VoteAPIHelper.java | 168 ++++++++++-------- .../cldr/unittest/web/TestSTFactory.java | 67 +++++-- .../cldr/unittest/web/data/TestVotePerf.xml | 124 +++++++------ 5 files changed, 218 insertions(+), 153 deletions(-) diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/CookieSession.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/CookieSession.java index 3f2454ca21a..d5855713904 100644 --- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/CookieSession.java +++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/CookieSession.java @@ -261,6 +261,15 @@ public static CookieSession retrieveUser(String email) { } } + /** only for tests. */ + public static CookieSession getTestSession(User user) { + final CookieSession extant = retrieveUser(user); + if (extant != null) { + return extant; + } + return newSession(user, "TEST.TEST.TEST.TEST"); + } + /** * Retrieve the session for a user * diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/STError.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/STError.java index 4ba276dc3f8..ab2fcd666d2 100644 --- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/STError.java +++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/STError.java @@ -2,7 +2,6 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; - import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.unicode.cldr.web.SurveyException; import org.unicode.cldr.web.SurveyException.ErrorCode; @@ -71,7 +70,7 @@ public Response build() { /** the error as a Status */ public Status getStatus() { - switch(code) { + switch (code) { case E_BAD_LOCALE: case E_BAD_SECTION: case E_BAD_XPATH: diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java index d154b0dfeca..cb8d807a832 100644 --- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java +++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java @@ -5,7 +5,6 @@ import java.util.logging.Logger; import javax.json.bind.spi.JsonbProvider; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; import org.unicode.cldr.test.CheckCLDR; import org.unicode.cldr.test.CheckCLDR.CheckStatus; import org.unicode.cldr.test.CheckCLDR.CheckStatus.Subtype; @@ -62,11 +61,11 @@ public VoteEntry(User u, Integer override, boolean redacted) { static final Logger logger = SurveyLog.forClass(VoteAPIHelper.class); - private static class ArgsForGet { + public static class ArgsForGet { String localeId; String sessionId; String page = null; - String xpstrid = null; + public String xpstrid = null; Boolean getDashboard = false; public ArgsForGet(String loc, String session) { @@ -119,79 +118,85 @@ private static Response handleGetRows(ArgsForGet args) { /** if true, hide emails. TODO: CLDR-16829 remove this parameter */ final boolean redacted = ((mySession.user == null) || (!mySession.user.getLevel().isGuest())); - final RowResponse r = new RowResponse(); - XPathMatcher matcher = null; - PageId pageId = null; - String xp = null; - - if (args.xpstrid == null && args.page != null) { - try { - pageId = PageId.valueOf(args.page); - } catch (IllegalArgumentException iae) { - return Response.status(404) - .entity(new STError(ErrorCode.E_BAD_SECTION)) - .build(); - } - if (pageId.getSectionId() == org.unicode.cldr.util.PathHeader.SectionId.Special) { - return new STError( - ErrorCode.E_SPECIAL_SECTION, - "Items not visible - page " - + pageId - + " section " - + pageId.getSectionId()) - .build(); - } - r.pageId = pageId.name(); - } else if (args.xpstrid != null && args.page == null) { - xp = sm.xpt.getByStringID(args.xpstrid); - if (xp == null) { - return Response.status(404).entity(new STError(ErrorCode.E_BAD_XPATH)).build(); - } - matcher = XPathMatcher.exactMatcherForString(xp); - } else { - // Should not get here. - return new STError( - ErrorCode.E_INTERNAL, - "handleGetRows: need xpstrid or page, but not both") - .build(); - } - final DataPage pageData = DataPage.make(pageId, mySession, locale, xp, matcher); - pageData.setUserForVotelist(mySession.user); - r.page = new RowResponse.Page(); - if (args.xpstrid != null) { - r.setOneRowPath(args.xpstrid); + final RowResponse r = getRowsResponse(args, sm, locale, mySession, redacted); + return Response.ok(r).build(); + } catch (Throwable t) { + t.printStackTrace(); + if (!(t instanceof SurveyException + && ((SurveyException) t).getErrCode() != ErrorCode.E_INTERNAL)) { + // log unknown or internal errs + SurveyLog.logException( + logger, t, "Trying to load " + args.localeId + " / " + args.xpstrid); } + return new STError(t).build(); + } + } - // don't return default content - CLDRLocale dcParent = - SupplementalDataInfo.getInstance().getBaseFromDefaultContent(locale); - if (dcParent != null) { - r.dcParent = dcParent.getBaseName(); - r.page.nocontent = true; - } else { - r.canModify = UserRegistry.userCanModifyLocale(mySession.user, locale); - r.localeDisplayName = locale.getDisplayName(); - r.page.nocontent = false; - Collection dataRows = pageData.getAll(); - r.page.rows = makePageRows(dataRows, redacted); - if (args.page != null) { - r.displaySets = makeDisplaySets(dataRows); - } + public static RowResponse getRowsResponse( + ArgsForGet args, + final SurveyMain sm, + final CLDRLocale locale, + final CookieSession mySession, + final boolean redacted) + throws SurveyException { + final RowResponse r = new RowResponse(); + XPathMatcher matcher = null; + PageId pageId = null; + String xp = null; + + if (args.xpstrid == null && args.page != null) { + try { + pageId = PageId.valueOf(args.page); + } catch (IllegalArgumentException iae) { + throw new SurveyException(ErrorCode.E_BAD_SECTION); } - if (args.getDashboard) { - r.notifications = - getOnePathDash(XPathTable.xpathToBaseXpath(xp), locale, mySession); + if (pageId.getSectionId() == org.unicode.cldr.util.PathHeader.SectionId.Special) { + throw new SurveyException( + ErrorCode.E_SPECIAL_SECTION, + "Items not visible - page " + pageId + " section " + pageId.getSectionId()); } - if (DEBUG_SERIALIZATION) { - debugSerialization(r, args); + r.pageId = pageId.name(); + } else if (args.xpstrid != null && args.page == null) { + xp = sm.xpt.getByStringID(args.xpstrid); + if (xp == null) { + throw new SurveyException(ErrorCode.E_BAD_XPATH); } - return Response.ok(r).build(); - } catch (Throwable t) { - t.printStackTrace(); - SurveyLog.logException( - logger, t, "Trying to load " + args.localeId + " / " + args.xpstrid); - return new STError(t).build(); // 500 + matcher = XPathMatcher.exactMatcherForString(xp); + } else { + // Should not get here. but could be a 'not acceptable' + throw new SurveyException( + ErrorCode.E_INTERNAL, // or E_BAD_XPATH? + "handleGetRows: need xpstrid or page, but not both"); } + final DataPage pageData = DataPage.make(pageId, mySession, locale, xp, matcher); + pageData.setUserForVotelist(mySession.user); + r.page = new RowResponse.Page(); + if (args.xpstrid != null) { + r.setOneRowPath(args.xpstrid); + } + + // don't return default content + CLDRLocale dcParent = SupplementalDataInfo.getInstance().getBaseFromDefaultContent(locale); + if (dcParent != null) { + r.dcParent = dcParent.getBaseName(); + r.page.nocontent = true; + } else { + r.canModify = UserRegistry.userCanModifyLocale(mySession.user, locale); + r.localeDisplayName = locale.getDisplayName(); + r.page.nocontent = false; + Collection dataRows = pageData.getAll(); + r.page.rows = makePageRows(dataRows, redacted); + if (args.page != null) { + r.displaySets = makeDisplaySets(dataRows); + } + } + if (args.getDashboard) { + r.notifications = getOnePathDash(XPathTable.xpathToBaseXpath(xp), locale, mySession); + } + if (DEBUG_SERIALIZATION) { + debugSerialization(r, args); + } + return r; } private static void debugSerialization(RowResponse r, ArgsForGet args) { @@ -381,24 +386,31 @@ public static Response handleVote( boolean forbiddenIsOk) { // translate this call into jax-rs Response try { - final VoteResponse r = getHandleVoteResponse(loc, xp, value, voteLevelChanged, mySession, forbiddenIsOk); + final VoteResponse r = + getHandleVoteResponse( + loc, xp, value, voteLevelChanged, mySession, forbiddenIsOk); return Response.ok(r).build(); } catch (Throwable se) { return new STError(se).build(); } } - /** this function is the implementation of handleVote() but does not use any jax-rs, for unit tests */ - public static VoteResponse getHandleVoteResponse(String loc, + /** + * this function is the implementation of handleVote() but does not use any jax-rs, for unit + * tests + */ + public static VoteResponse getHandleVoteResponse( + String loc, String xp, String value, int voteLevelChanged, final CookieSession mySession, - boolean forbiddenIsOk) throws SurveyException { + boolean forbiddenIsOk) + throws SurveyException { VoteResponse r = new VoteResponse(); mySession.userDidAction(); CLDRLocale locale = CLDRLocale.getInstance(loc); if (!UserRegistry.userCanModifyLocale(mySession.user, locale)) { - throw new SurveyException(ErrorCode.E_NO_PERMISSION, "Not allowed to modify " + locale); + throw new SurveyException(ErrorCode.E_NO_PERMISSION, "Not allowed to modify " + locale); } loc = locale.getBaseName(); // sanitized final SurveyMain sm = CookieSession.sm; @@ -460,11 +472,13 @@ public static VoteResponse getHandleVoteResponse(String loc, } } catch (Throwable t) { SurveyLog.logException(logger, t, "Processing submission " + locale + ":" + xp); - throw new SurveyException(ErrorCode.E_INTERNAL, "Processing submission " + locale + ":" + xp); + throw new SurveyException( + ErrorCode.E_INTERNAL, "Processing submission " + locale + ":" + xp); } } if (!forbiddenIsOk && r.statusAction.isForbidden()) { - throw new SurveyException(ErrorCode.E_VOTE_NOT_ACCEPTED, "Status action is forbidden: " + r.statusAction); + throw new SurveyException( + ErrorCode.E_VOTE_NOT_ACCEPTED, "Status action is forbidden: " + r.statusAction); } return r; } diff --git a/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java b/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java index 5936ed1c2a9..2e83a5f641f 100644 --- a/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java +++ b/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java @@ -10,9 +10,6 @@ import java.util.Date; import java.util.Map; import java.util.TreeMap; - -import javax.ws.rs.core.Response; - import org.unicode.cldr.draft.FileUtilities; import org.unicode.cldr.test.CheckCLDR; import org.unicode.cldr.unittest.web.TestAll.WebTestInfo; @@ -33,7 +30,6 @@ import org.unicode.cldr.web.BallotBox; import org.unicode.cldr.web.BallotBox.InvalidXPathException; import org.unicode.cldr.web.BallotBox.VoteNotAcceptedException; -import org.unicode.cldr.web.SurveyMain.Phase; import org.unicode.cldr.web.CookieSession; import org.unicode.cldr.web.DBUtils; import org.unicode.cldr.web.STFactory; @@ -42,9 +38,12 @@ import org.unicode.cldr.web.UserRegistry; import org.unicode.cldr.web.UserRegistry.LogoutException; import org.unicode.cldr.web.UserRegistry.User; +import org.unicode.cldr.web.XPathTable; import org.unicode.cldr.web.api.VoteAPI; +import org.unicode.cldr.web.api.VoteAPI.RowResponse; +import org.unicode.cldr.web.api.VoteAPI.RowResponse.Row; import org.unicode.cldr.web.api.VoteAPIHelper; -import org.unicode.cldr.web.XPathTable; +import org.unicode.cldr.web.api.VoteAPIHelper.ArgsForGet; public class TestSTFactory extends TestFmwk { @@ -463,7 +462,7 @@ public void TestVotePerf() throws SQLException, IOException { if (TestAll.skipIfNoDb()) return; final CheckCLDR.Phase p = CLDRConfig.getInstance().getPhase(); assertTrue("phase " + p + ".isUnitTest()", p.isUnitTest()); - runDataDrivenTest("TestVotePerf"); // TestSTFactory.xml + runDataDrivenTest("TestVotePerf"); } public void TestUserRegistry() throws SQLException, IOException { @@ -570,17 +569,30 @@ public void handlePathValue(String path, String value) { if (elem.equals("apiunvote")) { value = null; } - final CookieSession mySession = CookieSession.newSession(u, "999.999.999.999"); + final CookieSession mySession = CookieSession.getTestSession(u); try { - final VoteAPI.VoteResponse r = VoteAPIHelper.getHandleVoteResponse(locale.getBaseName(), xpath, value, 0, mySession, false); + final VoteAPI.VoteResponse r = + VoteAPIHelper.getHandleVoteResponse( + locale.getBaseName(), + xpath, + value, + 0, + mySession, + false); final boolean isOk = r.didVote; final boolean asExpected = (isOk == !needException); if (!asExpected) { - errln("exception=" + needException + " but got status " + r.didNotSubmit + " - " + r.toString()); + errln( + "exception=" + + needException + + " but got status " + + r.didNotSubmit + + " - " + + r.toString()); } else { logln(" status = " + r.didNotSubmit); } - } catch(Throwable iae) { + } catch (Throwable iae) { if (needException == true) { logln("Caught expected: " + iae); } else { @@ -632,7 +644,40 @@ public void handlePathValue(String path, String value) { logln(u + " " + elem + "d for " + xpath + " = " + value); } break; - case "apiverify": // TODO + case "apiverify": + { + // like verify, but via API + value = value.trim(); + if (value.isEmpty()) value = null; + UserRegistry.User u = getUserFromAttrs(attrs, "name"); + CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); + final CookieSession mySession = CookieSession.getTestSession(u); + + ArgsForGet args = + new ArgsForGet(locale.getBaseName(), mySession.id); + args.xpstrid = XPathTable.getStringIDString(xpath); + // args.getDashboard = false; + try { + final RowResponse r = + VoteAPIHelper.getRowsResponse( + args, + CookieSession.sm, + locale, + mySession, + false); + assertEquals("xpstrid", args.xpstrid, r.xpstrid); + assertEquals("row count", 1, r.page.rows.size()); + final Row firstRow = r.page.rows.values().iterator().next(); + assertEquals("rxpath", firstRow.xpath, xpath); + assertEquals( + "value for " + args.xpstrid, + value, + firstRow.winningValue); + } catch (Throwable t) { + assertNull("did not expect an exception", t); + } + } + break; case "verify": { value = value.trim(); diff --git a/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml b/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml index 3d4a93ed041..871cd4b4855 100644 --- a/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml +++ b/tools/cldr-apps/src/test/resources/org/unicode/cldr/unittest/web/data/TestVotePerf.xml @@ -6,70 +6,68 @@ test performance - - Territory-001 - Territory-002 - Territory-003 - Territory-005 - Territory-009 - Territory-011 - Territory-013 - Territory-014 - Territory-015 - Territory-017 - Territory-018 - Territory-019 - Territory-021 - Territory-029 - Territory-030 - Territory-034 - Territory-035 - Territory-039 - Territory-053 - Territory-054 - Territory-057 - Territory-061 - Territory-142 - Territory-143 - Territory-145 - Territory-150 - Territory-151 - Territory-154 - Territory-155 - Territory-202 + Territory-419 - - verify them - Territory-001 - + Territory-155 + Territory-155 + Territory-154 + Territory-154 + Territory-151 + Territory-151 + Territory-150 + Territory-150 + Territory-145 + Territory-145 + Territory-143 + Territory-143 + Territory-142 + Territory-142 + Territory-061 + Territory-061 + Territory-057 + Territory-057 + Territory-054 + Territory-054 + Territory-053 + Territory-053 + Territory-039 + Territory-039 + Territory-035 + Territory-035 + Territory-034 + Territory-034 + Territory-030 + Territory-030 + Territory-029 + Territory-029 + Territory-021 + Territory-021 + Territory-019 + Territory-019 + Territory-018 + Territory-018 + Territory-017 + Territory-017 + Territory-015 + Territory-015 + Territory-014 + Territory-014 + Territory-013 + Territory-013 + Territory-011 + Territory-011 + Territory-009 + Territory-009 + Territory-005 + Territory-005 + Territory-003 + Territory-003 + Territory-002 + Territory-002 + Territory-001 + Territory-001 From 4e608f7a4fb0a054c9a3e8b1880cc74b1f47fd6d Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 14 Dec 2023 15:16:42 -0600 Subject: [PATCH 4/4] CLDR-17287 API performance test for the back end - refactor to subroutines --- .../cldr/unittest/web/TestSTFactory.java | 952 +++++++++--------- 1 file changed, 471 insertions(+), 481 deletions(-) diff --git a/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java b/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java index 2e83a5f641f..d4b789ce3d5 100644 --- a/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java +++ b/tools/cldr-apps/src/test/java/org/unicode/cldr/unittest/web/TestSTFactory.java @@ -522,504 +522,31 @@ public void handlePathValue(String path, String value) { } switch (elem) { case "user": - { - String name = attrs.get("name"); - String org = attrs.get("org"); - String locales = attrs.get("locales"); - VoteResolver.Level level = - VoteResolver.Level.valueOf( - attrs.get("level").toLowerCase()); - - String email = name + "@" + org + ".example.com"; - UserRegistry.User u = fac.sm.reg.get(email); - if (u == null) { - u = - fac.sm.reg.createTestUser( - name, org, locales, level, email); - } - if (u == null) { - throw new InternalError( - "Couldn't find/register user " + name); - } else { - logln(name + " = " + u); - users.put(name, u); - } - } + handleElementUser(fac, attrs); break; case "setvar": - { - final String id = attrs.get("id"); - final CLDRLocale locale = - CLDRLocale.getInstance(attrs.get("locale")); - final String xvalue = - fac.make(locale, true).getStringValue(xpath); - vars.put(id, xvalue); - logln( - "$" + id + " = '" + xvalue + "' from " + locale + ":" - + xpath); - } + handleElementSetvar(fac, attrs, vars, xpath); break; case "apivote": case "apiunvote": - { - UserRegistry.User u = getUserFromAttrs(attrs, "name"); - CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); - boolean needException = - getBooleanAttr(attrs, "exception", false); - if (elem.equals("apiunvote")) { - value = null; - } - final CookieSession mySession = CookieSession.getTestSession(u); - try { - final VoteAPI.VoteResponse r = - VoteAPIHelper.getHandleVoteResponse( - locale.getBaseName(), - xpath, - value, - 0, - mySession, - false); - final boolean isOk = r.didVote; - final boolean asExpected = (isOk == !needException); - if (!asExpected) { - errln( - "exception=" - + needException - + " but got status " - + r.didNotSubmit - + " - " - + r.toString()); - } else { - logln(" status = " + r.didNotSubmit); - } - } catch (Throwable iae) { - if (needException == true) { - logln("Caught expected: " + iae); - } else { - iae.printStackTrace(); - errln("Unexpected exception: " + iae); - } - } - } + handleElementApivote(attrs, value, elem, xpath); break; case "vote": case "unvote": - { - UserRegistry.User u = getUserFromAttrs(attrs, "name"); - - CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); - BallotBox box = fac.ballotBoxForLocale(locale); - value = value.trim(); - boolean needException = - getBooleanAttr(attrs, "exception", false); - if (elem.equals("unvote")) { - value = null; - } - try { - box.voteForValue(u, xpath, value); - if (needException) { - errln( - "ERR: path #" - + pathCount - + ", xpath=" - + xpath - + ", locale=" - + locale - + ": expected exception, didn't get one"); - } - } catch (InvalidXPathException e) { - errln( - "Error: invalid xpath exception " - + xpath - + " : " - + e); - } catch (VoteNotAcceptedException iae) { - if (needException == true) { - logln("Caught expected: " + iae); - } else { - iae.printStackTrace(); - errln("Unexpected exception: " + iae); - } - } - logln(u + " " + elem + "d for " + xpath + " = " + value); - } + handleElementVote(fac, attrs, value, elem, xpath); break; case "apiverify": - { - // like verify, but via API - value = value.trim(); - if (value.isEmpty()) value = null; - UserRegistry.User u = getUserFromAttrs(attrs, "name"); - CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); - final CookieSession mySession = CookieSession.getTestSession(u); - - ArgsForGet args = - new ArgsForGet(locale.getBaseName(), mySession.id); - args.xpstrid = XPathTable.getStringIDString(xpath); - // args.getDashboard = false; - try { - final RowResponse r = - VoteAPIHelper.getRowsResponse( - args, - CookieSession.sm, - locale, - mySession, - false); - assertEquals("xpstrid", args.xpstrid, r.xpstrid); - assertEquals("row count", 1, r.page.rows.size()); - final Row firstRow = r.page.rows.values().iterator().next(); - assertEquals("rxpath", firstRow.xpath, xpath); - assertEquals( - "value for " + args.xpstrid, - value, - firstRow.winningValue); - } catch (Throwable t) { - assertNull("did not expect an exception", t); - } - } + handleElementApiverify(attrs, value, xpath); break; case "verify": - { - value = value.trim(); - if (value.isEmpty()) value = null; - CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); - BallotBox box = fac.ballotBoxForLocale(locale); - CLDRFile cf = fac.make(locale, true); - String stringValue = cf.getStringValue(xpath); - String fullXpath = cf.getFullXPath(xpath); - // logln("V"+ xpath + " = " + stringValue + ", " + - // fullXpath); - // logln("Resolver=" + box.getResolver(xpath)); - if (value == null && stringValue != null) { - errln( - pathCount - + "a Expected null value at " - + locale - + ":" - + xpath - + " got " - + stringValue); - } else if (value != null && !value.equals(stringValue)) { - errln( - pathCount - + "b Expected " - + value - + " at " - + locale - + ":" - + xpath - + " got " - + stringValue); - } else { - logln("OK: " + locale + ":" + xpath + " = " + value); - } - Status expStatus = Status.fromString(attrs.get("status")); - - VoteResolver r = box.getResolver(xpath); - Status winStatus = r.getWinningStatus(); - if (winStatus == expStatus) { - logln( - "OK: Status=" - + winStatus - + " " - + locale - + ":" - + xpath - + " Resolver=" - + box.getResolver(xpath)); - } else if (pathCount == 49 - && !VoteResolver.DROP_HARD_INHERITANCE) { - logln( - "Ignoring status mismatch for " - + pathCount - + "c, test assumes DROP_HARD_INHERITANCE is true"); - } else { - errln( - pathCount - + "c Expected: Status=" - + expStatus - + " got " - + winStatus - + " " - + locale - + ":" - + xpath - + " Resolver=" - + box.getResolver(xpath)); - } - - Status xpathStatus; - CLDRFile.Status newPath = new CLDRFile.Status(); - CLDRLocale newLocale = - CLDRLocale.getInstance( - cf.getSourceLocaleIdExtended( - fullXpath, newPath, false)); - final boolean localeChanged = - newLocale != null && !newLocale.equals(locale); - final boolean pathChanged = - newPath.pathWhereFound != null - && !newPath.pathWhereFound.equals(xpath); - final boolean itMoved = localeChanged || pathChanged; - if (localeChanged && pathChanged) { - logln( - "Aliased(locale+path): " - + locale - + "->" - + newLocale - + " and " - + xpath - + "->" - + newPath.pathWhereFound); - } else if (localeChanged) { - logln("Aliased(locale): " + locale + "->" + newLocale); - } else if (pathChanged) { - logln( - "Aliased(path): " - + xpath - + "->" - + newPath.pathWhereFound); - } - if ((fullXpath == null) || itMoved) { - xpathStatus = Status.missing; - } else { - XPathParts xpp2 = XPathParts.getFrozenInstance(fullXpath); - String statusFromXpath = - xpp2.getAttributeValue(-1, "draft"); - - if (statusFromXpath == null) { - statusFromXpath = "approved"; // no draft = approved - } - xpathStatus = Status.fromString(statusFromXpath); - } - if (xpathStatus != winStatus) { - logln( - "Warning: Winning Status=" - + winStatus - + " but xpath status is " - + xpathStatus - + " " - + locale - + ":" - + fullXpath - + " Resolver=" - + box.getResolver(xpath)); - } else if (xpathStatus == expStatus) { - logln( - "OK from fullxpath: Status=" - + xpathStatus - + " " - + locale - + ":" - + fullXpath - + " Resolver=" - + box.getResolver(xpath)); - } else { - errln( - pathCount - + "d Expected from fullxpath: Status=" - + expStatus - + " got " - + xpathStatus - + " " - + locale - + ":" - + fullXpath - + " Resolver=" - + box.getResolver(xpath)); - } - - // Verify from XML - File outFile = new File(targDir, locale.getBaseName() + ".xml"); - if (outFile.exists()) outFile.delete(); - try { - PrintWriter pw; - pw = - FileUtilities.openUTF8Writer( - targDir.getAbsolutePath(), - locale.getBaseName() + ".xml"); - cf.write(pw, noDtdPlease); - pw.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - handleException(e); - return; - } - - // logln("Read back.."); - CLDRFile readBack = null; - try { - readBack = - CLDRFile.loadFromFile( - outFile, - locale.getBaseName(), - DraftStatus.unconfirmed); - } catch (IllegalArgumentException iae) { - iae.getCause().printStackTrace(); - System.err.println(iae.getCause().toString()); - handleException(iae); - } - String reRead = readBack.getStringValue(xpath); - String fullXpathBack = readBack.getFullXPath(xpath); - Status xpathStatusBack; - if (fullXpathBack == null || itMoved) { - xpathStatusBack = Status.missing; - } else { - XPathParts xpp2 = - XPathParts.getFrozenInstance(fullXpathBack); - String statusFromXpathBack = - xpp2.getAttributeValue(-1, "draft"); - - if (statusFromXpathBack == null) { - statusFromXpathBack = "approved"; // no draft = - // approved - } - xpathStatusBack = Status.fromString(statusFromXpathBack); - } - - if (value == null && reRead != null) { - errln( - pathCount - + "e Expected null value from XML at " - + locale - + ":" - + xpath - + " got " - + reRead); - } else if (value != null && !value.equals(reRead)) { - errln( - pathCount - + "f Expected from XML " - + value - + " at " - + locale - + ":" - + xpath - + " got " - + reRead); - } else { - logln( - "OK from XML: " - + locale - + ":" - + xpath - + " = " - + reRead); - } - - if (xpathStatusBack == expStatus) { - logln( - "OK from XML: Status=" - + xpathStatusBack - + " " - + locale - + ":" - + fullXpathBack - + " Resolver=" - + box.getResolver(xpath)); - } else if (xpathStatusBack != winStatus) { - logln( - "Warning: Problem from XML: Winning Status=" - + winStatus - + " got " - + xpathStatusBack - + " " - + locale - + ":" - + fullXpathBack - + " Resolver=" - + box.getResolver(xpath)); - } else { - errln( - pathCount - + "g Expected from XML: Status=" - + expStatus - + " got " - + xpathStatusBack - + " " - + locale - + ":" - + fullXpathBack - + " Resolver=" - + box.getResolver(xpath)); - } - verifyOrgStatus(r, attrs); - } + handleElementVerify(fac, attrs, value, elem, xpath); break; case "verifyUser": - { - final User u = getUserFromAttrs(attrs, "name"); - final User onUser = getUserFromAttrs(attrs, "onUser"); - - final String action = attrs.get("action"); - final boolean allowed = getBooleanAttr(attrs, "allowed", true); - - boolean actualResult = true; - - // - final Level uLevel = u.getLevel(); - final Level onLevel = onUser.getLevel(); - switch (action) { - case "create": - actualResult = - actualResult - && UserRegistry.userCanCreateUsers(u); - if (!u.isSameOrg(onUser)) { - actualResult = - actualResult - && UserRegistry.userCreateOtherOrgs( - u); // if of different org - } - actualResult = - actualResult - && uLevel.canCreateOrSetLevelTo( - onLevel); - break; - case "delete": // assume same perms for now (?) - case "modify": - { - final boolean oldTest = u.isAdminFor(onUser); - final boolean newTest = - uLevel.canManageSomeUsers() - && uLevel.isManagerFor( - u.getOrganization(), - onLevel, - onUser.getOrganization()); - assertEquals( - "New(ex) vs old(got) manage test: " - + uLevel - + "/" - + onLevel, - newTest, - oldTest); - actualResult = actualResult && newTest; - } - break; - default: - errln("Unhandled action: " + action); - } - assertEquals( - u.org - + ":" - + uLevel - + " " - + action - + " " - + onUser.org - + ":" - + onLevel, - allowed, - actualResult); - } + handleElementVerifyUser(attrs); break; case "echo": case "warn": - if (value == null) { - logln("*** " + elem + " \"" + "null" + "\""); - } else { - logln("*** " + elem + " \"" + value.trim() + "\""); - } + handleElementEcho(value, elem); break; default: throw new IllegalArgumentException( @@ -1027,6 +554,469 @@ public void handlePathValue(String path, String value) { } } + private void handleElementVerify( + STFactory fac, + Map attrs, + String value, + String elem, + String xpath) { + value = value.trim(); + if (value.isEmpty()) value = null; + CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); + BallotBox box = fac.ballotBoxForLocale(locale); + CLDRFile cf = fac.make(locale, true); + String stringValue = cf.getStringValue(xpath); + String fullXpath = cf.getFullXPath(xpath); + // logln("V"+ xpath + " = " + stringValue + ", " + + // fullXpath); + // logln("Resolver=" + box.getResolver(xpath)); + if (value == null && stringValue != null) { + errln( + pathCount + + "a Expected null value at " + + locale + + ":" + + xpath + + " got " + + stringValue); + } else if (value != null && !value.equals(stringValue)) { + errln( + pathCount + + "b Expected " + + value + + " at " + + locale + + ":" + + xpath + + " got " + + stringValue); + } else { + logln("OK: " + locale + ":" + xpath + " = " + value); + } + Status expStatus = Status.fromString(attrs.get("status")); + + VoteResolver r = box.getResolver(xpath); + Status winStatus = r.getWinningStatus(); + if (winStatus == expStatus) { + logln( + "OK: Status=" + + winStatus + + " " + + locale + + ":" + + xpath + + " Resolver=" + + box.getResolver(xpath)); + } else if (pathCount == 49 && !VoteResolver.DROP_HARD_INHERITANCE) { + logln( + "Ignoring status mismatch for " + + pathCount + + "c, test assumes DROP_HARD_INHERITANCE is true"); + } else { + errln( + pathCount + + "c Expected: Status=" + + expStatus + + " got " + + winStatus + + " " + + locale + + ":" + + xpath + + " Resolver=" + + box.getResolver(xpath)); + } + + Status xpathStatus; + CLDRFile.Status newPath = new CLDRFile.Status(); + CLDRLocale newLocale = + CLDRLocale.getInstance( + cf.getSourceLocaleIdExtended(fullXpath, newPath, false)); + final boolean localeChanged = + newLocale != null && !newLocale.equals(locale); + final boolean pathChanged = + newPath.pathWhereFound != null + && !newPath.pathWhereFound.equals(xpath); + final boolean itMoved = localeChanged || pathChanged; + if (localeChanged && pathChanged) { + logln( + "Aliased(locale+path): " + + locale + + "->" + + newLocale + + " and " + + xpath + + "->" + + newPath.pathWhereFound); + } else if (localeChanged) { + logln("Aliased(locale): " + locale + "->" + newLocale); + } else if (pathChanged) { + logln("Aliased(path): " + xpath + "->" + newPath.pathWhereFound); + } + if ((fullXpath == null) || itMoved) { + xpathStatus = Status.missing; + } else { + XPathParts xpp2 = XPathParts.getFrozenInstance(fullXpath); + String statusFromXpath = xpp2.getAttributeValue(-1, "draft"); + + if (statusFromXpath == null) { + statusFromXpath = "approved"; // no draft = approved + } + xpathStatus = Status.fromString(statusFromXpath); + } + if (xpathStatus != winStatus) { + logln( + "Warning: Winning Status=" + + winStatus + + " but xpath status is " + + xpathStatus + + " " + + locale + + ":" + + fullXpath + + " Resolver=" + + box.getResolver(xpath)); + } else if (xpathStatus == expStatus) { + logln( + "OK from fullxpath: Status=" + + xpathStatus + + " " + + locale + + ":" + + fullXpath + + " Resolver=" + + box.getResolver(xpath)); + } else { + errln( + pathCount + + "d Expected from fullxpath: Status=" + + expStatus + + " got " + + xpathStatus + + " " + + locale + + ":" + + fullXpath + + " Resolver=" + + box.getResolver(xpath)); + } + + // Verify from XML + File outFile = new File(targDir, locale.getBaseName() + ".xml"); + if (outFile.exists()) outFile.delete(); + try { + PrintWriter pw; + pw = + FileUtilities.openUTF8Writer( + targDir.getAbsolutePath(), + locale.getBaseName() + ".xml"); + cf.write(pw, noDtdPlease); + pw.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + handleException(e); + return; + } + + // logln("Read back.."); + CLDRFile readBack = null; + try { + readBack = + CLDRFile.loadFromFile( + outFile, locale.getBaseName(), DraftStatus.unconfirmed); + } catch (IllegalArgumentException iae) { + iae.getCause().printStackTrace(); + System.err.println(iae.getCause().toString()); + handleException(iae); + } + String reRead = readBack.getStringValue(xpath); + String fullXpathBack = readBack.getFullXPath(xpath); + Status xpathStatusBack; + if (fullXpathBack == null || itMoved) { + xpathStatusBack = Status.missing; + } else { + XPathParts xpp2 = XPathParts.getFrozenInstance(fullXpathBack); + String statusFromXpathBack = xpp2.getAttributeValue(-1, "draft"); + + if (statusFromXpathBack == null) { + statusFromXpathBack = "approved"; // no draft = + // approved + } + xpathStatusBack = Status.fromString(statusFromXpathBack); + } + + if (value == null && reRead != null) { + errln( + pathCount + + "e Expected null value from XML at " + + locale + + ":" + + xpath + + " got " + + reRead); + } else if (value != null && !value.equals(reRead)) { + errln( + pathCount + + "f Expected from XML " + + value + + " at " + + locale + + ":" + + xpath + + " got " + + reRead); + } else { + logln("OK from XML: " + locale + ":" + xpath + " = " + reRead); + } + + if (xpathStatusBack == expStatus) { + logln( + "OK from XML: Status=" + + xpathStatusBack + + " " + + locale + + ":" + + fullXpathBack + + " Resolver=" + + box.getResolver(xpath)); + } else if (xpathStatusBack != winStatus) { + logln( + "Warning: Problem from XML: Winning Status=" + + winStatus + + " got " + + xpathStatusBack + + " " + + locale + + ":" + + fullXpathBack + + " Resolver=" + + box.getResolver(xpath)); + } else { + errln( + pathCount + + "g Expected from XML: Status=" + + expStatus + + " got " + + xpathStatusBack + + " " + + locale + + ":" + + fullXpathBack + + " Resolver=" + + box.getResolver(xpath)); + } + verifyOrgStatus(r, attrs); + } + + private void handleElementEcho(String value, String elem) { + if (value == null) { + logln("*** " + elem + " \"" + "null" + "\""); + } else { + logln("*** " + elem + " \"" + value.trim() + "\""); + } + } + + private void handleElementVerifyUser(final Map attrs) { + final User u = getUserFromAttrs(attrs, "name"); + final User onUser = getUserFromAttrs(attrs, "onUser"); + final String action = attrs.get("action"); + final boolean allowed = getBooleanAttr(attrs, "allowed", true); + boolean actualResult = true; + // + final Level uLevel = u.getLevel(); + final Level onLevel = onUser.getLevel(); + switch (action) { + case "create": + actualResult = actualResult && UserRegistry.userCanCreateUsers(u); + if (!u.isSameOrg(onUser)) { + actualResult = + actualResult + && UserRegistry.userCreateOtherOrgs( + u); // if of different org + } + actualResult = + actualResult && uLevel.canCreateOrSetLevelTo(onLevel); + break; + case "delete": // assume same perms for now (?) + case "modify": + { + final boolean oldTest = u.isAdminFor(onUser); + final boolean newTest = + uLevel.canManageSomeUsers() + && uLevel.isManagerFor( + u.getOrganization(), + onLevel, + onUser.getOrganization()); + assertEquals( + "New(ex) vs old(got) manage test: " + + uLevel + + "/" + + onLevel, + newTest, + oldTest); + actualResult = actualResult && newTest; + } + break; + default: + errln("Unhandled action: " + action); + } + assertEquals( + u.org + + ":" + + uLevel + + " " + + action + + " " + + onUser.org + + ":" + + onLevel, + allowed, + actualResult); + } + + private void handleElementApiverify( + final Map attrs, String value, String xpath) { + // like verify, but via API + value = value.trim(); + if (value.isEmpty()) value = null; + UserRegistry.User u = getUserFromAttrs(attrs, "name"); + CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); + final CookieSession mySession = CookieSession.getTestSession(u); + ArgsForGet args = new ArgsForGet(locale.getBaseName(), mySession.id); + args.xpstrid = XPathTable.getStringIDString(xpath); + // args.getDashboard = false; + try { + final RowResponse r = + VoteAPIHelper.getRowsResponse( + args, CookieSession.sm, locale, mySession, false); + assertEquals("xpstrid", args.xpstrid, r.xpstrid); + assertEquals("row count", 1, r.page.rows.size()); + final Row firstRow = r.page.rows.values().iterator().next(); + assertEquals("rxpath", firstRow.xpath, xpath); + assertEquals("value for " + args.xpstrid, value, firstRow.winningValue); + } catch (Throwable t) { + assertNull("did not expect an exception", t); + } + } + + private void handleElementVote( + final STFactory fac, + final Map attrs, + String value, + String elem, + String xpath) { + UserRegistry.User u = getUserFromAttrs(attrs, "name"); + CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); + BallotBox box = fac.ballotBoxForLocale(locale); + value = value.trim(); + boolean needException = getBooleanAttr(attrs, "exception", false); + if (elem.equals("unvote")) { + value = null; + } + try { + box.voteForValue(u, xpath, value); + if (needException) { + errln( + "ERR: path #" + + pathCount + + ", xpath=" + + xpath + + ", locale=" + + locale + + ": expected exception, didn't get one"); + } + } catch (InvalidXPathException e) { + errln("Error: invalid xpath exception " + xpath + " : " + e); + } catch (VoteNotAcceptedException iae) { + if (needException == true) { + logln("Caught expected: " + iae); + } else { + iae.printStackTrace(); + errln("Unexpected exception: " + iae); + } + } + logln(u + " " + elem + "d for " + xpath + " = " + value); + } + + private void handleElementApivote( + final Map attrs, + String value, + String elem, + String xpath) { + UserRegistry.User u = getUserFromAttrs(attrs, "name"); + CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); + boolean needException = getBooleanAttr(attrs, "exception", false); + if (elem.equals("apiunvote")) { + value = null; + } + final CookieSession mySession = CookieSession.getTestSession(u); + try { + final VoteAPI.VoteResponse r = + VoteAPIHelper.getHandleVoteResponse( + locale.getBaseName(), + xpath, + value, + 0, + mySession, + false); + final boolean isOk = r.didVote; + final boolean asExpected = (isOk == !needException); + if (!asExpected) { + errln( + "exception=" + + needException + + " but got status " + + r.didNotSubmit + + " - " + + r.toString()); + } else { + logln(" status = " + r.didNotSubmit); + } + } catch (Throwable iae) { + if (needException == true) { + logln("Caught expected: " + iae); + } else { + iae.printStackTrace(); + errln("Unexpected exception: " + iae); + } + } + } + + private void handleElementSetvar( + final STFactory fac, + final Map attrs, + final Map vars, + String xpath) { + final String id = attrs.get("id"); + final CLDRLocale locale = CLDRLocale.getInstance(attrs.get("locale")); + final String xvalue = fac.make(locale, true).getStringValue(xpath); + vars.put(id, xvalue); + logln("$" + id + " = '" + xvalue + "' from " + locale + ":" + xpath); + } + + private void handleElementUser( + final STFactory fac, final Map attrs) + throws InternalError { + String name = attrs.get("name"); + String org = attrs.get("org"); + String locales = attrs.get("locales"); + VoteResolver.Level level = + VoteResolver.Level.valueOf(attrs.get("level").toLowerCase()); + String email = name + "@" + org + ".example.com"; + UserRegistry.User u = fac.sm.reg.get(email); + if (u == null) { + u = fac.sm.reg.createTestUser(name, org, locales, level, email); + } + if (u == null) { + throw new InternalError("Couldn't find/register user " + name); + } else { + logln(name + " = " + u); + users.put(name, u); + } + } + /** * If a "verify" element includes "orgStatus" and "statusOrg" attributes, then * report an error unless getStatusForOrganization returns the specified status