Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 5aa0f1df46787d1000456aee8602780bf2da44e2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
/*******************************************************************************
 * Copyright (c) 2000, 2011 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.team.tests.ccvs.core.compatible;
import java.io.IOException;
import java.io.InputStream;
import java.util.StringTokenizer;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.ICVSFile;
import org.eclipse.team.internal.ccvs.core.ICVSFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.ICVSResource;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
import org.eclipse.team.tests.ccvs.core.CommandLineCVSClient;
import org.eclipse.team.tests.ccvs.core.EclipseCVSClient;
import org.eclipse.team.tests.ccvs.core.ICVSClient;
import org.eclipse.team.tests.ccvs.core.JUnitTestCase;


/**
 * This is a TestCase that does provide the possibility
 * to run tests on both the reference reference-client and the
 * client provided by us, and to check on equal results
 * in files and messages to the consol.
 * 
 * No own tests should be placed here, instead you should
 * instantiate this testcase in order to make your test from
 * another suite.
 * The class is final, because you need to be able to open
 * two (or more) different environments to test certain things.
 */
public final class SameResultEnv extends JUnitTestCase {
	private IProject referenceProject;
	private ICVSFolder referenceRoot;
	private IProject eclipseProject;
	private ICVSFolder eclipseRoot;

	private boolean ignoreExceptions;

	public SameResultEnv(String arg) {
		super(arg);
	}
	
	/**
	 * Always to be called in the setUp of the testCase that wants to 
	 * use the same-result environment.
	 */
	public void setUp() throws Exception {
		super.setUp();
		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		// setup reference client test project
		referenceProject = root.getProject(getName() + "-reference");
		referenceProject.delete(true /*deleteContent*/, true /*force*/, null);
		mkdirs(referenceProject);
		referenceRoot = CVSWorkspaceRoot.getCVSFolderFor(referenceProject);
		
		// setup eclipse client test project
		eclipseProject = root.getProject(getName() + "-eclipse");
		eclipseProject.delete(true /*deleteContent*/, true /*force*/, null);
		mkdirs(eclipseProject);
		eclipseRoot = CVSWorkspaceRoot.getCVSFolderFor(eclipseProject);

		// By default, exceptions are not ignored.
		// Specific test cases can choose to ignore exceptions
		ignoreExceptions = false;
	}
	
	/**
	 * Always to be called in the tearDown of the testCase that wants to 
	 * use the same-result environment.
	 */
	public void tearDown() throws Exception {
		// we deliberately don't clean up test projects to simplify debugging
		super.tearDown();
	}
	
	/**
	 * Helper method.
	 * Calls execute(command, EMPTY_ARGS, localOptions, arguments, pathRelativeToRoot)
	 */				
	public void execute(String command, String[] localOptions, String[] arguments, String pathRelativeToRoot)
		throws CVSException {
		execute(command, EMPTY_ARGS, localOptions, arguments, pathRelativeToRoot);
	}

	/**
	 * Helper method.
	 * Calls execute(command, EMPTY_ARGS, localOptions, arguments, "")
	 */				
	public void execute(String command, String[] localOptions, String[] arguments)
		throws CVSException {
		execute(command, EMPTY_ARGS, localOptions, arguments, "");
	}

	/**
	 * Runs a command twice, once in the reference environments, once
	 * in the eclipse environment.  Compares the resulting resources
	 * on disk, but not console output.
	 */
	public void execute(String command,
		String[] globalOptions, String[] localOptions, String[] arguments,
		String pathRelativeToRoot) throws CVSException {
		
		// run with reference client
		boolean referenceClientException = execute(CommandLineCVSClient.INSTANCE,
			CompatibleTestSetup.referenceClientRepository, referenceProject,
			command, globalOptions, localOptions, arguments, pathRelativeToRoot);
		// run with Eclipse client
		boolean eclipseClientException = execute(EclipseCVSClient.INSTANCE,
			CompatibleTestSetup.eclipseClientRepository, eclipseProject,
			command, globalOptions, localOptions, arguments, pathRelativeToRoot);
			
		// assert same results
		assertEquals(referenceClientException, eclipseClientException);
		assertConsistent();
	}
	
	private boolean execute(ICVSClient client, ICVSRepositoryLocation repositoryLocation,
		IContainer localRoot, String command,
		String[] globalOptions, String[] localOptions, String[] arguments,
		String pathRelativeToRoot) throws CVSException {
		try {
			IPath path = new Path(pathRelativeToRoot);
			if (path.segmentCount() != 0) {
				localRoot = localRoot.getFolder(path);
			}
			client.executeCommand(repositoryLocation, localRoot, command, globalOptions,
				localOptions, arguments);
		} catch (CVSException e) {
			if (ignoreExceptions) return true;
			throw e;
		}
		return false;
	}

	/**
	 * Deletes files on the both of the cvs-servers.
	 */
	public void magicDeleteRemote(String remoteName) throws CVSException {
		super.magicDeleteRemote(CompatibleTestSetup.referenceClientRepository, remoteName);
		super.magicDeleteRemote(CompatibleTestSetup.eclipseClientRepository, remoteName);		
	}
	
	/**
	 * Set up both of the repos on the cvs-server(s) with the standard
	 * file-structure:
	 * project
	 *   a.txt
	 *   f1
	 *     b.txt
	 *     c.txt
	 */
	public void magicSetUpRepo(String projectName)
		throws IOException, CoreException, CVSException {
		magicSetUpRepo(projectName, new String[]{"a.txt","f1/b.txt","f1/c.txt"});
	}
	
	/**
	 * Set up both of the repos on the cvs-server(s) with a file structure
	 * resulting for your input in the parameter createResources.
	 */
	public void magicSetUpRepo(String projectName, String[] createResources)
		throws IOException, CoreException, CVSException {
		magicDeleteRemote(projectName);

		IProject projectRoot = workspaceRoot.getProject(projectName + "-setup-tmp");
		mkdirs(projectRoot);
		createRandomFile(projectRoot, createResources);
		
		String[] lOptions = new String[]{"-m","msg"};
		String[] args = new String[]{projectName,"a","b"};
	
		magicDeleteRemote(CompatibleTestSetup.referenceClientRepository, projectName);
		EclipseCVSClient.execute(CompatibleTestSetup.referenceClientRepository, CVSWorkspaceRoot.getCVSFolderFor(projectRoot),
			"import", EMPTY_ARGS, lOptions, args);
			
		magicDeleteRemote(CompatibleTestSetup.eclipseClientRepository, projectName);
		EclipseCVSClient.execute(CompatibleTestSetup.eclipseClientRepository, CVSWorkspaceRoot.getCVSFolderFor(projectRoot),
			"import", EMPTY_ARGS, lOptions, args);

		projectRoot.delete(false /*force*/, null);
	}

	/**
	 * Create a file with random-content in both, the reference client and 
	 * the eclipse-client.
	 * 
	 * @param relativeFileName is the relative path as always in the 
	           class used for access
	 */
	public void createRandomFile(String relativeFileName)
		throws IOException, CoreException {
		String[] contents = new String[] { createRandomContent() };
		writeToFile(relativeFileName, contents);
	}

	/**
	 * Call createRandomFile for every element of the array
	 * 
	 * @see SameResultEnv#createRandomFile(String)
	 */
	public void createRandomFile(String[] relativeFileNames,
		String pathRelativeToRoot) throws CoreException, IOException {
		if (pathRelativeToRoot == null) {
			pathRelativeToRoot = "";
		} else if (! pathRelativeToRoot.endsWith("/")) {
			pathRelativeToRoot += "/";
		}
		for (int i = 0; i < relativeFileNames.length; i++) {
			createRandomFile(pathRelativeToRoot + relativeFileNames[i]);
		}
	}
	
	/**
	 * Read from the file (check that we have actually got the same
	 * content in both versions
	 */
	public String[] readFromFile(String relativeFileName)
		throws IOException, CoreException {
		IFile referenceFile = referenceProject.getFile(relativeFileName);
		String[] content1 = super.readFromFile(referenceFile);
		IFile eclipseFile = eclipseProject.getFile(relativeFileName);
		String[] content2 = super.readFromFile(eclipseFile);
		assertEqualsArrays(content1,content2);
		return content1;
	}
	
	/**
	 * Delete a file / folder from both directories.
	 */
	public void deleteFile(String relativeFileName) throws CoreException {
		IResource referenceFile, eclipseFile;
		if (relativeFileName.length() != 0) {
			referenceFile = referenceProject.findMember(relativeFileName);
			eclipseFile = eclipseProject.findMember(relativeFileName);
		} else {
			referenceFile = referenceProject;
			eclipseFile = eclipseProject;
		}
		assertEquals(referenceFile != null, eclipseFile != null);
		if (referenceFile == null) return;
		assertEquals(referenceFile.exists(), eclipseFile.exists());
		referenceFile.delete(true, null);
		eclipseFile.delete(true, null);
	}
	
	/**
	 * Creates a folder (and its parents if needed) in both environments.
	 */
	public void mkdirs(String relativeFolderName) throws CoreException {
		IFolder referenceFolder = referenceProject.getFolder(relativeFolderName);
		IFolder eclipseFolder = eclipseProject.getFolder(relativeFolderName);
		assertEquals(referenceFolder.exists(), eclipseFolder.exists());
		mkdirs(referenceFolder);
		mkdirs(eclipseFolder);
	}
	
	/**
	 * Append a String to an file (actually to both of the files, that are going
	 * to have the same content)
	 */
	public void appendToFile(String relativeFileName, String[] contents)
		throws IOException, CoreException {
		// Wait a second so that the timestamp will change for sure
		waitMsec(1500);

		IFile referenceFile = referenceProject.getFile(relativeFileName);
		appendToFile(referenceFile, contents);
		IFile eclipseFile = eclipseProject.getFile(relativeFileName);
		appendToFile(eclipseFile, contents);		
	}
	
		/**
	 * Append a String to an file (actually to both of the files, that are going
	 * to have the same content)
	 */
	public void prefixToFile(String relativeFileName, String[] contents)
		throws IOException, CoreException {
		// Wait a second so that the timestamp will change for sure
		waitMsec(1500);

		IFile referenceFile = referenceProject.getFile(relativeFileName);
		prefixToFile(referenceFile, contents);
		IFile eclipseFile = eclipseProject.getFile(relativeFileName);
		prefixToFile(eclipseFile, contents);		
	}
	
	/**
	 * Write to the file (actually to both of the files, that are going
	 * to have the same content)
	 */
	public void writeToFile(String relativeFileName, String[] contents)
		throws IOException, CoreException {
		IFile referenceFile = referenceProject.getFile(relativeFileName);
		writeToFile(referenceFile, contents);
		IFile eclipseFile = eclipseProject.getFile(relativeFileName);
		writeToFile(eclipseFile, contents);
	}		

	/**
	 * Checks whether the two directories inside the environment
	 * are equal and therefore the state valid.
	 */
	public void assertConsistent() throws CVSException {
		assertEquals(referenceRoot, eclipseRoot);
	}	

	/**
	 * Deep compare of two ManagedResources (most likely folders).
	 * Passwords are ignored.
	 * 
	 * @param ignoreTimestamp if true timestamps of
	           files are ignored for the comparison
	 */
	public static void assertEquals(ICVSResource mResource1, 
										ICVSResource mResource2) 
										throws CVSException {
		
		assertEquals(mResource1.isFolder(), mResource2.isFolder());
		assertEquals(mResource1.isManaged() , mResource2.isManaged());
		assertEquals(mResource1.exists(), mResource2.exists());
		
		if (!mResource1.exists()) {
			return;
		}
		
		if (mResource1.isFolder()) {
			assertEquals((ICVSFolder)mResource1,(ICVSFolder)mResource2);
		} else {
			assertEquals((ICVSFile)mResource1,(ICVSFile)mResource2);
		}	
	}	
	
	/**
	 * Assert that two CVSFile's are equal by comparing the content
	 * and the meta information out of the ResourceSync.
	 */
	private static void assertEquals(ICVSFile mFile1, ICVSFile mFile2) throws CVSException {
		
		if (mFile1.getName().equals(".project")) return;
		
		// Check the permissions on disk
		assertEquals(mFile1.isReadOnly(), mFile2.isReadOnly());
					
		// Compare the content of the files
		try {
			InputStream in1 = mFile1.getContents();
			InputStream in2 = mFile2.getContents();
			byte[] buffer1 = new byte[(int)mFile1.getSize()];
			byte[] buffer2 = new byte[(int)mFile2.getSize()];
			// This is not the right way to do it, because the Stream
			// may read less than the whole file
			in1.read(buffer1);
			in2.read(buffer2);
			in1.close();
			in2.close();
			assertEquals("Length differs for file " + mFile1.getName(), buffer1.length, buffer2.length);
			assertEquals("Contents differs for file " + mFile1.getName(), new String(buffer1),new String(buffer2));
		} catch (IOException e) {
			throw new CVSException("Error in TestCase");
		}

		// We can not do the check, because the reference client does
		// check out dirty files ?!?
		// assertEquals(mFile1.isDirty(),mFile2.isDirty());
		
		assertEquals(mFile1.getSyncInfo() == null,mFile2.getSyncInfo() == null);
		if (mFile1.getSyncInfo() == null) {
			return;
		}
		
		ResourceSyncInfo info1 = mFile1.getSyncInfo();
		ResourceSyncInfo info2 = mFile2.getSyncInfo();
		
		assertEquals(info1.getKeywordMode(), info2.getKeywordMode());
		assertEquals(info1.getTag(), info2.getTag());
		assertEquals(info1.getName(), info2.getName());
		assertEquals(info1.getRevision(), info2.getRevision());
		
		assertEquals(info1.isDeleted(), info2.isDeleted());
		assertEquals(info1.isAdded(), info2.isAdded());
		assertEquals(info1.isMerged(), info2.isMerged());
		assertEquals(info1.isMergedWithConflicts(), info2.isMergedWithConflicts());
		
		// Ensure that timestamps are written using same time zone.
		// assertTimestampEquals(info1.getTimeStamp(), info2.getTimeStamp());
		
		// We are not able to check for the permissions, as the reference-client doesn't save them
	}
	
	/**
	 * Assert that two CVSFile's are equal. First the 
	 * meta information out of the FolderSync for this 
	 * folder is compared, then the amount of children is 
	 * checked and finally the recursion is started to
	 * compare the children of this folder.
	 */
	private static void assertEquals(ICVSFolder mFolder1, 
										ICVSFolder mFolder2) 
										throws CVSException {

		assertEquals(mFolder1.isCVSFolder(),mFolder2.isCVSFolder());
		
		if (mFolder1.isCVSFolder()) {
			String root1 = removePassword(mFolder1.getFolderSyncInfo().getRoot());
			String root2 = removePassword(mFolder2.getFolderSyncInfo().getRoot());
			root1 = root1.substring(0,root1.lastIndexOf("@"));
			root2 = root2.substring(0,root2.lastIndexOf("@"));
			assertEquals(root1,root2);
			
			assertEquals(mFolder1.getFolderSyncInfo().getRepository(),mFolder2.getFolderSyncInfo().getRepository());
			assertEquals(mFolder1.getFolderSyncInfo().getIsStatic(),mFolder2.getFolderSyncInfo().getIsStatic());
			assertEquals(mFolder1.getFolderSyncInfo().getTag(),mFolder2.getFolderSyncInfo().getTag());
		}
		
		ICVSResource[] resourceList1 = mFolder1.members(ICVSFolder.FILE_MEMBERS | ICVSFolder.FOLDER_MEMBERS);
		ICVSResource[] resourceList2 = mFolder2.members(ICVSFolder.FILE_MEMBERS | ICVSFolder.FOLDER_MEMBERS);
		assertEquals(resourceList1.length, resourceList2.length);
		for (int i=0; i<resourceList1.length; i++) {
			boolean resourceFound = false;
			for (int j=0; j<resourceList2.length; j++) {
				if (resourceList1[i].getName().equals(resourceList2[j].getName())) {
					assertEquals(resourceList1[i], resourceList2[j]);
					resourceFound = true;
					break;
				}
			}
			assertTrue("Resource " + resourceList1[i].getName() + " not found in the list",resourceFound);
		}
	}
	
	/**
	 * Sets whether Exceptions that are thrown in the execution of both of the clients are
	 * caught or thrown to the upper level. If the exceptions are caught the result of the 
	 * reference-client and this client are compared as if the execution succeeded.
	 */
	public void setIgnoreExceptions(boolean ignoreExceptions) {
		this.ignoreExceptions = ignoreExceptions;
	}
	
	/**
	 * returns ":pserver:nkrambro@fiji:/home/nkrambro/repo"
	 *         when you insert ":pserver:nkrambro:password@fiji:/home/nkrambro/repo"
	 */
	public static String removePassword(String root) {
		int indexOfHostSeparator = root.lastIndexOf("@", root.length());
		String hostAndPath = root.substring(indexOfHostSeparator);
		root = root.substring(0, indexOfHostSeparator);
		StringTokenizer tok = new StringTokenizer(root, ":", true);
		StringBuffer filteredRoot = new StringBuffer();
		int colonCounter = 3;
		while (tok.hasMoreTokens()) {
			String token = tok.nextToken();
			if (":".equals(token)) {
				if (--colonCounter == 0) continue; // skip colon
			}
			if (colonCounter == 0) continue; // skip password
			filteredRoot.append(token);
		}
		filteredRoot.append(hostAndPath);
		return filteredRoot.toString();
	}
}

Back to the top