Java tutorial
/** * @(#)MockNirvanaBot.java 03.04.2016 * Copyright 2016 Dmitry Trofimovich (KIN, Nirvanchik, DimaTrofimovich@gmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * WARNING: This file may contain Russian characters. * Recommended code page for this file is CP1251 (also called Windows-1251). * */ package org.wikipedia.nirvana.nirvanabot; import static org.mockito.Mockito.when; import org.wikipedia.Wiki.Revision; import org.wikipedia.nirvana.FileTools; import org.wikipedia.nirvana.MockNirvanaWiki; import org.wikipedia.nirvana.MockNirvanaWiki.EditInfoMinimal; import org.wikipedia.nirvana.MockWikiTools; import org.wikipedia.nirvana.NirvanaWiki; import org.wikipedia.nirvana.WikiTools; import org.wikipedia.nirvana.nirvanabot.serviceping.OnlineService.Status; import org.wikipedia.nirvana.nirvanabot.serviceping.WikiService; import org.apache.commons.lang3.StringUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.mockito.Mockito; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Calendar; import java.util.Iterator; import java.util.List; import java.util.TimeZone; import junit.framework.Assert; /** * Patches {@link NirvanaBot} to make it work with mocked wiki and mocked services in tests. * All mocked data are read from specified json config. * * @see {@link org.wikipedia.nirvana.nirvanabot.NirvanaBotTest} */ public class MockNirvanaBot extends NirvanaBot { WikiService mockWikiService; MockNirvanaWiki mainWiki; JSONObject mainWikiJson = null; JSONObject commonsWikiJson = null; List<EditInfoMinimal> expectedEdits = null; List<ExpectedQuery> expectedQueries = null; public static class TestError extends Exception { private static final long serialVersionUID = 1L; public final Exception original; public TestError(Exception error) { original = error; } } public static class ExpectedQuery { public final List<String> contains; public ExpectedQuery(List<String> contains) { this.contains = contains; } @Override public boolean equals(Object obj) { if (obj instanceof String) { return containsAll((String) obj); } return super.equals(obj); } @Override public String toString() { StringBuilder temp = new StringBuilder("ExpectedQuery [contains: "); temp.append(StringUtils.join(contains, ", ")); temp.append("]"); return temp.toString(); } private boolean containsAll(String str) { for (String thing : contains) { if (!str.contains(thing)) return false; } return true; } } /** * Constructs mocked NirvanaBot. * * @param flags global bot run flags, see {@link org.wikipedia.nirvana.NirvanaBasicBot} * @param testConfigPath path to js config from which to read mock data * @throws TestError in the case when test json data are corrupt or unavailable */ public MockNirvanaBot(int flags, String testConfigPath) throws TestError { super(flags); mockWikiService = Mockito.mock(WikiService.class); try { when(mockWikiService.isOk()).thenReturn(Status.OK); } catch (InterruptedException e) { // Ignored } if (testConfigPath != null) { try { initializeFromTestConfig(testConfigPath); } catch (IOException | ParseException e) { e.printStackTrace(); throw new TestError(e); } } setRetryCount(0); } void initializeFromTestConfig(String testConfigPath) throws IOException, ParseException { InputStreamReader reader = null; try { reader = new InputStreamReader(new FileInputStream(testConfigPath), FileTools.UTF8); JSONParser jsonParser = new JSONParser(); JSONObject jsonObject = (JSONObject) jsonParser.parse(reader); mainWikiJson = (JSONObject) jsonObject.get("wiki"); commonsWikiJson = (JSONObject) jsonObject.get("commons_wiki"); JSONArray jsonResponces = (JSONArray) jsonObject.get("wiki_tools"); if (jsonResponces != null) { List<String> wikiToolsResponces = parseMultilineStringArray(jsonResponces); MockWikiTools.mockResponses(wikiToolsResponces); } JSONArray jsonEdits = (JSONArray) jsonObject.get("expected_edits"); if (jsonEdits != null) { expectedEdits = parseEdits(jsonEdits); } JSONArray jsonQueries = (JSONArray) jsonObject.get("expected_tools_queries"); if (jsonQueries != null) { expectedQueries = parseQueries(jsonQueries); } } finally { if (reader != null) { reader.close(); } } } private List<ExpectedQuery> parseQueries(JSONArray jsonQueries) { Iterator<?> it = jsonQueries.iterator(); List<ExpectedQuery> queries = new ArrayList<>(); while (it.hasNext()) { JSONObject queryJson = (JSONObject) it.next(); ExpectedQuery query = new ExpectedQuery(readStringList(queryJson, "contains")); queries.add(query); } return queries; } private List<EditInfoMinimal> parseEdits(JSONArray jsonEdits) { Iterator<?> it = jsonEdits.iterator(); List<EditInfoMinimal> edits = new ArrayList<>(); while (it.hasNext()) { JSONObject editInfoJson = (JSONObject) it.next(); EditInfoMinimal edit = new EditInfoMinimal((String) editInfoJson.get("title"), readMultiLineString(editInfoJson, "text"), (int) (long) (Long) editInfoJson.get("section")); edits.add(edit); } return edits; } private List<String> parseMultilineStringArray(JSONArray jsonResponces) { List<String> list = new ArrayList<String>(); Iterator<?> it = jsonResponces.iterator(); while (it.hasNext()) { String val = parseMultiLineString(it.next()); list.add(val); } return list; } private void readWiki(MockNirvanaWiki wiki, JSONObject wikiJson) { JSONArray transcludesJsonList = (JSONArray) wikiJson.get("whatTranscludesHere"); if (transcludesJsonList != null) { Iterator<?> it = transcludesJsonList.iterator(); while (it.hasNext()) { JSONObject transcludesJson = (JSONObject) it.next(); wiki.mockWhatTranscludesHere((String) transcludesJson.get("title"), ((String) transcludesJson.get("list")).split(",")); } } JSONArray nsIdJsonList = (JSONArray) wikiJson.get("namespaceIdentifier"); if (nsIdJsonList != null) { Iterator<?> it = nsIdJsonList.iterator(); while (it.hasNext()) { JSONObject nsIdJson = (JSONObject) it.next(); //System.out.println("ns num type: "+ nsIdJson.get("number").getClass()); wiki.mockNamespaceIdentifier((Long) nsIdJson.get("number"), (String) nsIdJson.get("id")); } } JSONArray pageTextJsonList = (JSONArray) wikiJson.get("pageText"); if (pageTextJsonList != null) { Iterator<?> it = pageTextJsonList.iterator(); while (it.hasNext()) { JSONObject pageTextJson = (JSONObject) it.next(); wiki.mockPageText((String) pageTextJson.get("title"), readMultiLineString(pageTextJson, "text")); } } JSONArray firstRevJsonList = (JSONArray) wikiJson.get("firstRevision"); if (firstRevJsonList != null) { Iterator<?> it = firstRevJsonList.iterator(); while (it.hasNext()) { JSONObject revisionJson = (JSONObject) it.next(); wiki.mockFirstRevision((String) revisionJson.get("title"), parseRevision(wiki, revisionJson)); } } JSONArray pageTemplatesJsonList = (JSONArray) wikiJson.get("templates"); if (pageTemplatesJsonList != null) { Iterator<?> it = pageTemplatesJsonList.iterator(); while (it.hasNext()) { JSONObject pageTemplatesJson = (JSONObject) it.next(); wiki.mockPageTemplates((String) pageTemplatesJson.get("title"), readStringList(pageTemplatesJson, "templates")); } } } private Revision parseRevision(MockNirvanaWiki wiki, JSONObject revisionJson) { long revid = (Long) revisionJson.get("revid"); long timestamp = (Long) revisionJson.get("timestamp"); Calendar c = Calendar.getInstance(); c.setTimeInMillis(timestamp); c.setTimeZone(TimeZone.getTimeZone("UTC")); String title = (String) revisionJson.get("title"); String summary = (String) revisionJson.get("summary"); String user = (String) revisionJson.get("user"); boolean minor = (Boolean) revisionJson.get("minor"); boolean bot = (Boolean) revisionJson.get("bot"); boolean rvnew = (Boolean) revisionJson.get("rvnew"); int size = (int) (long) (Long) revisionJson.get("size"); Revision r = wiki.new Revision(revid, c, title, summary, user, minor, bot, rvnew, size); return r; } private String readMultiLineString(JSONObject object, String name) { return parseMultiLineString(object.get(name)); } private String parseMultiLineString(Object object) { if (object instanceof String) { return (String) object; } else { JSONArray list = (JSONArray) object; StringBuilder b = new StringBuilder(); Iterator<?> it = list.iterator(); while (it.hasNext()) { String str = (String) it.next(); b.append(str).append("\n"); } // Remove last "\n" if (b.length() > 0) { b.setLength(b.length() - 1); } return b.toString(); } } private List<String> readStringList(JSONObject jsonObject, String name) { JSONArray list = (JSONArray) jsonObject.get(name); Iterator<?> it = list.iterator(); List<String> result = new ArrayList<String>(); while (it.hasNext()) { result.add((String) it.next()); } return result; } @Override public String getConfig(String[] args) { String name = args[0]; return name; } @Override protected NirvanaWiki createWiki(String domain, String path, String protocol) { MockNirvanaWiki wiki = new MockNirvanaWiki(domain, path, protocol); wiki.allowEdits(this.DEBUG_MODE); mainWiki = wiki; // We need it to check edits when test finishes if (mainWikiJson != null) { readWiki(wiki, mainWikiJson); } return wiki; } @Override protected NirvanaWiki createCommonsWiki() { MockNirvanaWiki wiki = new MockNirvanaWiki("commons"); if (commonsWikiJson != null) { readWiki(wiki, commonsWikiJson); } return wiki; } @Override protected ServiceManager createServiceManager() throws BotFatalError { ServiceManager manager = Mockito.mock(ServiceManager.class); try { when(manager.checkServices()).thenReturn(true); when(manager.getMainWikiService()).thenReturn(mockWikiService); when(manager.getActiveService()).thenReturn(WikiTools.Service.PETSCAN); } catch (InterruptedException e) { // Ignored, is not going to come here } return manager; } @Override protected void go() throws InterruptedException { super.go(); } /** * Checks if edits to wikis are equal to those provided in test js config. */ public void validateEdits() { if (expectedEdits != null) { Assert.assertEquals(expectedEdits, mainWiki.getEdits()); } } /** * Checks if required http queries were made in test run or just validate * that they have required strings for example. */ public void validateQueries() { if (expectedQueries != null) { Assert.assertEquals(expectedQueries, MockWikiTools.getQueries()); } } }