PageRenderTime 86ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/test/java/com/aragost/javahg/test/AbstractTestCase.java

https://bitbucket.org/jongunnip/javahg
Java | 439 lines | 256 code | 57 blank | 126 comment | 16 complexity | 953f90d76c1b2c50e1b75d323e1c81b1 MD5 | raw file
  1. /*
  2. * #%L
  3. * JavaHg
  4. * %%
  5. * Copyright (C) 2011 aragost Trifork ag
  6. * %%
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. * THE SOFTWARE.
  24. * #L%
  25. */
  26. package com.aragost.javahg.test;
  27. import java.io.BufferedReader;
  28. import java.io.File;
  29. import java.io.FileInputStream;
  30. import java.io.IOException;
  31. import java.io.InputStreamReader;
  32. import java.nio.charset.Charset;
  33. import java.util.Collection;
  34. import java.util.logging.Level;
  35. import java.util.logging.Logger;
  36. import org.junit.After;
  37. import org.junit.Assert;
  38. import org.junit.Assume;
  39. import org.junit.runner.RunWith;
  40. import com.aragost.javahg.BaseRepository;
  41. import com.aragost.javahg.Changeset;
  42. import com.aragost.javahg.HgVersion;
  43. import com.aragost.javahg.Repository;
  44. import com.aragost.javahg.RepositoryConfiguration;
  45. import com.aragost.javahg.RepositoryConfiguration.CachePolicy;
  46. import com.aragost.javahg.commands.AddCommand;
  47. import com.aragost.javahg.commands.CommitCommand;
  48. import com.aragost.javahg.commands.UpdateCommand;
  49. import com.aragost.javahg.commands.VersionCommand;
  50. import com.aragost.javahg.internals.AbstractCommand;
  51. import com.aragost.javahg.internals.GenericCommand;
  52. import com.aragost.javahg.internals.HgInputStream;
  53. import com.aragost.javahg.internals.RuntimeIOException;
  54. import com.aragost.javahg.internals.Server;
  55. import com.aragost.javahg.internals.Utils;
  56. import com.google.common.collect.ObjectArrays;
  57. import com.google.common.io.Files;
  58. /**
  59. * Base class for test cases.
  60. */
  61. @RunWith(org.junit.runners.BlockJUnit4ClassRunner.class)
  62. public abstract class AbstractTestCase {
  63. // The jul root logger is changed in the initialization of this
  64. // class. A strong reference must be maintained to the logger. See
  65. // LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE on
  66. // http://findbugs.sourceforge.net/bugDescriptions.html
  67. private static final Logger JUL_ROOT_LOGGER = Logger.getLogger("");
  68. private static int count = 0;
  69. private BaseRepository testRepository;
  70. private BaseRepository testRepository2;
  71. protected static final RepositoryConfiguration REPO_CONF;
  72. static {
  73. REPO_CONF = makeRepoConf();
  74. JUL_ROOT_LOGGER.setLevel(Level.WARNING);
  75. File dir = Files.createTempDir();
  76. BaseRepository repo = Repository.create(REPO_CONF, dir);
  77. HgVersion version = VersionCommand.on(repo).execute();
  78. repo.close();
  79. try {
  80. deleteTempDir(dir);
  81. } catch (IOException e) {
  82. System.err.println("JavaHg: Failed to remove temp dir: " + dir.getAbsolutePath());
  83. }
  84. System.err.println("JavaHg test using Mercurial version: " + version + ", binary: " + REPO_CONF.getHgBin());
  85. }
  86. public File createMercurialRepository() {
  87. File dir = Files.createTempDir();
  88. Server server = new Server(RepositoryConfiguration.DEFAULT.getHgBin());
  89. server.initMecurialRepository(dir);
  90. return dir;
  91. }
  92. protected static RepositoryConfiguration makeRepoConf() {
  93. RepositoryConfiguration conf = new RepositoryConfiguration();
  94. conf.setCachePolicy(CachePolicy.WEAK);
  95. conf.addExtension(JavaHgTestExtension.class);
  96. return conf;
  97. }
  98. public Charset utf8() {
  99. return Charset.forName("UTF-8");
  100. }
  101. public BaseRepository getTestRepository() {
  102. if (this.testRepository == null) {
  103. File dir = Files.createTempDir();
  104. this.testRepository = Repository.create(REPO_CONF, dir);
  105. }
  106. return this.testRepository;
  107. }
  108. public BaseRepository getTestRepository2() {
  109. if (this.testRepository2 == null) {
  110. File dir = Files.createTempDir();
  111. this.testRepository2 = Repository.create(REPO_CONF, dir);
  112. }
  113. return this.testRepository2;
  114. }
  115. /**
  116. * Write to a file in the test repository
  117. *
  118. * @param name
  119. * @param content
  120. * @throws IOException
  121. */
  122. public void writeFile(String name, String content) throws IOException {
  123. writeFile(getTestRepository(), name, content);
  124. }
  125. public void writeFile(BaseRepository repo, String name, String content) throws IOException {
  126. File file = new File(repo.getDirectory(), name);
  127. Files.write(content, file, utf8());
  128. }
  129. /**
  130. * Write something to the file in the test repository.
  131. * <p>
  132. * Each call to this method will write different content
  133. *
  134. * @param name
  135. * @throws IOException
  136. */
  137. public void writeFile(String name) throws IOException {
  138. writeFile(name, String.valueOf(count++) + "\n");
  139. }
  140. public void appendFile(String name) throws IOException {
  141. File file = new File(getTestRepository().getDirectory(), name);
  142. Files.append(String.valueOf(count++) + "\n", file, utf8());
  143. }
  144. /**
  145. * Read first line of the file
  146. *
  147. * @param name
  148. * @return
  149. * @throws IOException
  150. */
  151. public String readFile(String name) throws IOException {
  152. File file = new File(getTestRepository().getDirectory(), name);
  153. return Files.readFirstLine(file, utf8());
  154. }
  155. /**
  156. * Delete the specified file from the working copy of the test
  157. * repository.
  158. *
  159. * @param name
  160. */
  161. public void deleteFile(String name) {
  162. File file = new File(getTestRepository().getDirectory(), name);
  163. boolean deleted = file.delete();
  164. if (!deleted) {
  165. throw new RuntimeException("Could not delete: " + file);
  166. }
  167. }
  168. /**
  169. * Commit the changes in the test repository
  170. *
  171. * @throws IOException
  172. */
  173. public Changeset commit() throws IOException {
  174. Repository repo = getTestRepository();
  175. AddCommand.on(repo).execute();
  176. CommitCommand cmd = CommitCommand.on(repo).user("testcase").message("testcase: " + getClass().getName());
  177. return cmd.execute();
  178. }
  179. /**
  180. * Create a new changeset in the test repository.
  181. *
  182. * @return the changeset Created
  183. * @throws IOException
  184. */
  185. public Changeset createChangeset() throws IOException {
  186. writeFile("dummyFileForCreatingChangesets", String.valueOf(count++));
  187. return commit();
  188. }
  189. /**
  190. * Update the test repository to the specified changeset
  191. *
  192. * @param cs
  193. * @throws IOException
  194. */
  195. public void update(Changeset cs) throws IOException {
  196. UpdateCommand.on(getTestRepository()).clean().rev(cs.getNode()).execute();
  197. }
  198. @After
  199. public void closeTestRepository() throws IOException {
  200. if (this.testRepository != null) {
  201. this.testRepository.close();
  202. deleteTempDir(this.testRepository.getDirectory());
  203. this.testRepository = null;
  204. }
  205. if (this.testRepository2 != null) {
  206. this.testRepository2.close();
  207. deleteTempDir(this.testRepository2.getDirectory());
  208. this.testRepository2 = null;
  209. }
  210. }
  211. /**
  212. * Return an absolute File object referencing a file in the
  213. * specified repository.
  214. *
  215. * @param repo
  216. * @param parts
  217. * @return
  218. */
  219. public static File repoFile(Repository repo, String... parts) {
  220. File result = repo.getDirectory();
  221. for (String part : parts) {
  222. result = new File(result, part);
  223. }
  224. return result;
  225. }
  226. /**
  227. * The error text for missing files is different on Windows
  228. * compared to Linux/Mac
  229. *
  230. * @return
  231. */
  232. public static String getMissingFileErrorText() {
  233. String error = "No such file or directory";
  234. String osName = System.getProperty("os.name");
  235. if (osName.startsWith("Windows")) {
  236. error = "The system cannot find the file specified";
  237. }
  238. return error;
  239. }
  240. /**
  241. * Create a temp directory, and return the canonical file object
  242. * (i.e. no symlinks).
  243. *
  244. * @return
  245. * @throws IOException
  246. */
  247. protected static File createTempDir() throws IOException {
  248. return Files.createTempDir().getCanonicalFile();
  249. }
  250. /**
  251. * Delete a directory in the system temporary directory
  252. * (java.io.tmpdir).
  253. *
  254. * @throws IOException
  255. */
  256. public static void deleteTempDir(File file) throws IOException {
  257. Utils.deleteTempDir(file);
  258. }
  259. protected void assertSingleton(Object obj, Collection<?> coll) {
  260. Assert.assertEquals(obj, Utils.single(coll));
  261. }
  262. protected void assertFailedExecution(AbstractCommand cmd) {
  263. assertFailedExecution(cmd, "");
  264. }
  265. protected void assertFailedExecution(AbstractCommand cmd, String msg) {
  266. if (msg.length() > 0) {
  267. msg = " Message: " + msg;
  268. }
  269. Assert.fail("Exception expected! Return code: " + cmd.getReturnCode() + "." + msg);
  270. }
  271. /**
  272. * Execute a Mercurial command direcotry for a repository, with
  273. * out using the server
  274. *
  275. * @param repo
  276. * @param hgrcPath
  277. * @param args
  278. */
  279. protected void execHgCommand(BaseRepository repo, String... args) {
  280. try {
  281. String[] arr = Utils.arrayConcat(new String[] { REPO_CONF.getHgBin(), "--repo",
  282. repo.getDirectory().getAbsolutePath() }, args);
  283. Process process = Runtime.getRuntime().exec(arr);
  284. String stderr = Utils.readStream(process.getErrorStream(), repo.newDecoder());
  285. Utils.consumeAll(process.getInputStream());
  286. if (process.waitFor() != 0) {
  287. throw new RuntimeException(stderr);
  288. }
  289. } catch (IOException e) {
  290. throw new RuntimeIOException(e);
  291. } catch (InterruptedException e) {
  292. throw Utils.asRuntime(e);
  293. }
  294. }
  295. /**
  296. * Warning: invoking commands on this server may put it in an invalid state.
  297. * Use AbstractCommand.launchStream(String...) instead.
  298. *
  299. * @param repo The repo
  300. * @return A Server from the repo
  301. */
  302. protected Server getFirstServer(Repository repo) {
  303. return repo.getServerPool().getServers().get(0);
  304. }
  305. protected static ServeState startServing(Repository repo,
  306. String... additionalConfig) {
  307. // On windows hg serve --port 0 doesn't print the port it's listening on
  308. Assume.assumeTrue(!Utils.isWindows());
  309. final File pidFile;
  310. try {
  311. pidFile = File.createTempFile("javahg", ".pid");
  312. final Process process = Runtime.getRuntime().exec(
  313. ObjectArrays.concat(
  314. new String[] {
  315. RepositoryConfiguration.DEFAULT.getHgBin(),
  316. "serve", "--port", "0", "-d", "--pid-file",
  317. pidFile.toString(), "-R",
  318. repo.getDirectory().toString() },
  319. additionalConfig, String.class));
  320. HgInputStream in = new HgInputStream(process.getInputStream(),
  321. repo.newDecoder());
  322. Assert.assertTrue(in.find("(bound to *:".getBytes()));
  323. final int port = in.readDecimal().intValue();
  324. in.close();
  325. return new ServeState() {
  326. public int getPort() {
  327. return port;
  328. }
  329. public void stop() {
  330. BufferedReader in = null;;
  331. try {
  332. // probably already dead:
  333. process.destroy();
  334. in = new BufferedReader(new InputStreamReader(new FileInputStream(pidFile)));
  335. killProcess(Integer.parseInt(in.readLine()));
  336. } catch (Exception e) {
  337. throw Utils.asRuntime(e);
  338. } finally {
  339. try {
  340. in.close();
  341. } catch (IOException e) {
  342. throw Utils.asRuntime(e);
  343. }
  344. }
  345. }};
  346. } catch (IOException e) {
  347. throw Utils.asRuntime(e);
  348. }
  349. }
  350. /**
  351. * Kill a process
  352. *
  353. * @param pid The process id
  354. */
  355. protected static void killProcess(int pid) {
  356. try {
  357. Runtime rt = Runtime.getRuntime();
  358. if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {
  359. rt.exec("taskkill " + pid).waitFor();
  360. } else {
  361. rt.exec(new String[] { "kill", "-9", "" + pid }).waitFor();
  362. }
  363. } catch (IOException e) {
  364. throw Utils.asRuntime(e);
  365. } catch (InterruptedException e) {
  366. throw Utils.asRuntime(e);
  367. }
  368. }
  369. protected static class TestableCommand extends GenericCommand {
  370. public TestableCommand(Repository repository, String commandName) {
  371. super(repository, commandName);
  372. }
  373. public HgInputStream executeToStream(String... args) {
  374. return launchStream(args);
  375. }
  376. }
  377. protected interface ServeState
  378. {
  379. public int getPort();
  380. public void stop();
  381. }
  382. }