Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Fix matching workspace folders (primarily for 'restrictWorkspace') #264

Merged
merged 3 commits into from
Jun 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 33 additions & 9 deletions src/tfvc/commands/findworkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
"use strict";

import * as path from "path";
import { Strings } from "../../helpers/strings";
import { IButtonMessageItem } from "../../helpers/vscodeutils.interfaces";
import { Constants, TfvcTelemetryEvents } from "../../helpers/constants";
Expand Down Expand Up @@ -113,15 +112,12 @@ export class FindWorkspace implements ITfvcCommand<IWorkspace> {
}
//If we're restricting the workspace, find the proper teamProject name
if (this._restrictWorkspace) {
const folder: string = path.basename(this._localPath).toLowerCase();
//With tf.exe, folder and teamProject should match (TEE won't)
if (folder !== teamProject.toLowerCase()) {
for (let i: number = 0; i < mappings.length; i++) {
for (let i: number = 0; i < mappings.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about cloaked paths? Are those excluded somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄 Yeah, those are actually excluded by never being returned from the earlier call to getMapping. TF.exe returns a cloaked mapping that ends with ':' and the CLC returns a cloaked mapping that doesn't end with ':'. Both of these fail the if in getMapping. We should talk about how to properly handle cloaked folders (since they aren't today).

const isWithin: boolean = this.pathIsWithin(this._localPath, mappings[i].localPath);
if (isWithin) {
const project: string = this.getTeamProject(mappings[i].serverPath); //maintain case in serverPath
if (project.toLowerCase() === folder) {
teamProject = project;
break;
}
teamProject = project;
break;
}
}
}
Expand Down Expand Up @@ -236,4 +232,32 @@ export class FindWorkspace implements ITfvcCommand<IWorkspace> {

return "";
}

//Checks to see if the openedPath (in VS Code) is within the workspacePath
//specified in the workspace. The funcation needs to ensure we get the
//"best" (most specific) match.
private pathIsWithin(openedPath: string, workspacePath: string): boolean {
//Replace all backslashes with forward slashes on both paths
openedPath = openedPath.replace(/\\/g, "/");
workspacePath = workspacePath.replace(/\\/g, "/");

//Add trailing separators to ensure they're included in the lastIndexOf
//(e.g., to ensure we match "/path2" with "/path2" and not "/path2" with "/path" first)
openedPath = this.addTrailingSeparator(openedPath, "/");
workspacePath = this.addTrailingSeparator(workspacePath, "/");

//Lowercase both paths (TFVC should be case-insensitive)
openedPath = openedPath.toLowerCase();
workspacePath = workspacePath.toLowerCase();

return openedPath.startsWith(workspacePath);
};

//If the path doesn't end with a separator, add one
private addTrailingSeparator(path: string, separator: string): string {
if (path[path.length - 1] !== separator) {
return path += separator;
}
return path;
}
}
102 changes: 100 additions & 2 deletions test/tfvc/commands/findworkspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ describe("Tfvc-FindWorkspaceCommand", function() {
});

it("should verify parse output - no errors - restrictWorkspace", async function() {
const localPath: string = "/path/to/workspace/project2";
const localPath: string = "/path2/to/workspace/project2";
const cmd: FindWorkspace = new FindWorkspace(localPath, true);
const executionResult: IExecutionResult = {
exitCode: 0,
Expand All @@ -181,6 +181,56 @@ describe("Tfvc-FindWorkspaceCommand", function() {
assert.equal(workspace.mappings.length, 2);
});

//The CLC will always return *all* server mappings in the workspace even if you pass a particular local folder
//TF.exe will only return the server mappings in the workspace that apply to the particular local folder
it("should verify parse output - no errors - restrictWorkspace - sub-folder", async function() {
const localPath: string = "/path2/to/workspace/project2/sub-folder";
const cmd: FindWorkspace = new FindWorkspace(localPath, true);
const executionResult: IExecutionResult = {
exitCode: 0,
stdout: "=====================================================================================================================================================\n" +
"Workspace: MyWorkspace\n" +
"Collection: http://server:8080/tfs/\n" +
"$/project1: /path\n" +
"$/project2: /path2",
stderr: undefined
};

const workspace: IWorkspace = await cmd.ParseOutput(executionResult);
assert.equal(workspace.name, "MyWorkspace");
assert.equal(workspace.server, "http://server:8080/tfs/");
//This test should find project2 as the team project since the localPath contains project2 and we have restrictWorkspace
assert.equal(workspace.defaultTeamProject, "project2");
assert.equal(workspace.comment, undefined);
assert.equal(workspace.computer, undefined);
assert.equal(workspace.owner, undefined);
assert.equal(workspace.mappings.length, 2);
});

it("should verify parse output - no errors - restrictWorkspace - sub-folder - Windows path", async function() {
const localPath: string = "c:\\path2\\to\\workspace\\project2\\sub-folder\\";
const cmd: FindWorkspace = new FindWorkspace(localPath, true);
const executionResult: IExecutionResult = {
exitCode: 0,
stdout: "=====================================================================================================================================================\n" +
"Workspace: MyWorkspace\n" +
"Collection: http://server:8080/tfs/\n" +
"$/project1: c:\\path\n" +
"$/project2: c:\\path2",
stderr: undefined
};

const workspace: IWorkspace = await cmd.ParseOutput(executionResult);
assert.equal(workspace.name, "MyWorkspace");
assert.equal(workspace.server, "http://server:8080/tfs/");
//This test should find project2 as the team project since the localPath contains project2 and we have restrictWorkspace
assert.equal(workspace.defaultTeamProject, "project2");
assert.equal(workspace.comment, undefined);
assert.equal(workspace.computer, undefined);
assert.equal(workspace.owner, undefined);
assert.equal(workspace.mappings.length, 2);
});

it("should verify parse output - no errors - encoded output", async function() {
const localPath: string = "/path/to/workspace/project1";
const cmd: FindWorkspace = new FindWorkspace(localPath, true);
Expand Down Expand Up @@ -301,7 +351,7 @@ describe("Tfvc-FindWorkspaceCommand", function() {
});

it("should verify parse EXE output - no errors - restrictWorkspace", async function() {
const localPath: string = "/path/to/workspace/project2";
const localPath: string = "/path2/to/workspace/project2";
const cmd: FindWorkspace = new FindWorkspace(localPath, true);
const executionResult: IExecutionResult = {
exitCode: 0,
Expand All @@ -324,6 +374,54 @@ describe("Tfvc-FindWorkspaceCommand", function() {
assert.equal(workspace.mappings.length, 2);
});

it("should verify parse EXE output - no errors - restrictWorkspace - sub-folder", async function() {
const localPath: string = "/path2/to/workspace/project2/sub-folder";
const cmd: FindWorkspace = new FindWorkspace(localPath, true);
//TF.exe won't return "$/project1: /path1" if it's in the overall workspace (see the CLC test of the same scenario, above)
const executionResult: IExecutionResult = {
exitCode: 0,
stdout: "=====================================================================================================================================================\n" +
"Workspace: MyWorkspace\n" +
"Collection: http://server:8080/tfs/\n" +
"$/project2: /path2",
stderr: undefined
};

const workspace: IWorkspace = await cmd.ParseExeOutput(executionResult);
assert.equal(workspace.name, "MyWorkspace");
assert.equal(workspace.server, "http://server:8080/tfs/");
//This test should find project2 as the team project since the localPath contains project2 and we have restrictWorkspace
assert.equal(workspace.defaultTeamProject, "project2");
assert.equal(workspace.comment, undefined);
assert.equal(workspace.computer, undefined);
assert.equal(workspace.owner, undefined);
assert.equal(workspace.mappings.length, 1);
});

it("should verify parse EXE output - no errors - restrictWorkspace - sub-folder - Windows path", async function() {
const localPath: string = "c:\\path2\\to\\workspace\\project2\\sub-folder\\";
const cmd: FindWorkspace = new FindWorkspace(localPath, true);
//TF.exe won't return "$/project1: c:\\path1" if it's in the overall workspace (see the CLC test of the same scenario, earlier)
const executionResult: IExecutionResult = {
exitCode: 0,
stdout: "=====================================================================================================================================================\n" +
"Workspace: MyWorkspace\n" +
"Collection: http://server:8080/tfs/\n" +
"$/project2: c:\\path2",
stderr: undefined
};

const workspace: IWorkspace = await cmd.ParseExeOutput(executionResult);
assert.equal(workspace.name, "MyWorkspace");
assert.equal(workspace.server, "http://server:8080/tfs/");
//This test should find project2 as the team project since the localPath contains project2 and we have restrictWorkspace
assert.equal(workspace.defaultTeamProject, "project2");
assert.equal(workspace.comment, undefined);
assert.equal(workspace.computer, undefined);
assert.equal(workspace.owner, undefined);
assert.equal(workspace.mappings.length, 1);
});

it("should verify parse EXE output - no errors - encoded output", async function() {
const localPath: string = "/path/to/workspace/project1";
const cmd: FindWorkspace = new FindWorkspace(localPath, true);
Expand Down