Skip to content

Commit b0b1f04

Browse files
authored
Merge pull request #2429 from NattyNarwhal/pfgrep
2 parents 3510485 + 8c96c3a commit b0b1f04

File tree

6 files changed

+125
-8
lines changed

6 files changed

+125
-8
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,4 @@ Thanks so much to everyone [who has contributed](https://github.com/codefori/vsc
6666
* [@nathaniel-king-navarrete](https://github.com/Nathaniel-King-Navarrete)
6767
* [@buzzia2001](https://github.com/buzzia2001)
6868
* [@e1mais](https://github.com/e1mais)
69+
* [@NattyNarwhal](https://github.com/NattyNarwhal)

src/api/IBMi.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const remoteApps = [ // All names MUST also be defined as key in 'remoteFeatures
3939
},
4040
{
4141
path: `/QOpenSys/pkgs/bin/`,
42-
names: [`git`, `grep`, `tn5250`, `md5sum`, `bash`, `chsh`, `stat`, `sort`, `tar`, `ls`, `find`]
42+
names: [`git`, `grep`, `tn5250`, `pfgrep`, `md5sum`, `bash`, `chsh`, `stat`, `sort`, `tar`, `ls`, `find`]
4343
},
4444
{
4545
path: `/QSYS.LIB/`,
@@ -221,6 +221,7 @@ export default class IBMi {
221221
this.remoteFeatures = {
222222
git: undefined,
223223
grep: undefined,
224+
pfgrep: undefined,
224225
tn5250: undefined,
225226
setccsid: undefined,
226227
md5sum: undefined,

src/api/Search.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { GetMemberInfo } from './components/getMemberInfo';
22
import IBMi from './IBMi';
33
import { Tools } from './Tools';
4-
import { IBMiMember, SearchHit, SearchResults } from './types';
4+
import { IBMiMember, SearchHit, SearchResults, CommandResult } from './types';
55

66
export namespace Search {
77

@@ -26,6 +26,8 @@ export namespace Search {
2626
let detailedMembers: IBMiMember[] | undefined;
2727
let memberFilter: string | undefined;
2828

29+
const pfgrep = connection.remoteFeatures.pfgrep;
30+
2931
if (typeof members === `string`) {
3032
memberFilter = connection.sysNameInAmerican(`${members}.MBR`);
3133
} else
@@ -42,10 +44,23 @@ export namespace Search {
4244
const asp = await connection.lookupLibraryIAsp(library);
4345

4446
// Then search the members
45-
const result = await connection.sendQsh({
46-
command: `/usr/bin/grep -inHR -F "${sanitizeSearchTerm(searchTerm)}" ${memberFilter}`,
47-
directory: connection.sysNameInAmerican(`${asp ? `/${asp}` : ``}/QSYS.LIB/${library}.LIB/${sourceFile}.FILE`)
48-
});
47+
var result: CommandResult | undefined = undefined;
48+
if (pfgrep) {
49+
// pfgrep vs. qshell grep difference: uses -r for recursion instead of -R
50+
// (GNU/BSD grep treat them the same); we don't use recursion yet though...
51+
// older versions before 0.4 need -t to trim whitespace, 0.4 inverts the flag
52+
const command = `${pfgrep} -inHr -F "${sanitizeSearchTerm(searchTerm)}" ${memberFilter}`;
53+
result = await connection.sendCommand({
54+
command: command,
55+
directory: connection.sysNameInAmerican(`${asp ? `/${asp}` : ``}/QSYS.LIB/${library}.LIB/${sourceFile}.FILE`)
56+
});
57+
} else {
58+
const command = `/usr/bin/grep -inHR -F "${sanitizeSearchTerm(searchTerm)}" ${memberFilter}`;
59+
result = await connection.sendQsh({
60+
command: command,
61+
directory: connection.sysNameInAmerican(`${asp ? `/${asp}` : ``}/QSYS.LIB/${library}.LIB/${sourceFile}.FILE`)
62+
});
63+
}
4964

5065
if (!result.stderr) {
5166
let hits = parseGrepOutput(
@@ -204,4 +219,4 @@ export namespace Search {
204219
}
205220
return index;
206221
}
207-
}
222+
}

src/api/tests/suites/search.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,19 @@ describe('Search Tests', { concurrent: true }, () => {
9797
await connection.runCommand({ command: `DLTF FILE(${library}/${file})`, noLibList: true });
9898
}
9999
});
100+
101+
it('Filtered members list search', async () => {
102+
const pfgrep = connection.remoteFeatures.pfgrep;
103+
// This test only needs to run if pfgrep is installed
104+
if (pfgrep) {
105+
const resultPfgrep = await Search.searchMembers(connection, "QSYSINC", "QRPGLESRC", "IBM", "CMRPG");
106+
connection.remoteFeatures.pfgrep = undefined;
107+
const resultQsh = await Search.searchMembers(connection, "QSYSINC", "QRPGLESRC", "IBM", "CMRPG");
108+
connection.remoteFeatures.pfgrep = pfgrep;
109+
// XXX: Do a deep equals here (without having to reimplement one)
110+
expect(resultPfgrep.hits[0].lines[0] == resultQsh.hits[0].lines[0]);
111+
} else {
112+
expect(true)
113+
}
114+
});
100115
});

src/testing/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ import { StorageSuite } from "./storage";
99
import { TestSuitesTreeProvider } from "./testCasesTree";
1010
import { ToolsSuite } from "./tools";
1111
import { Server } from "../typings";
12+
import { SearchSuite } from "./search";
1213

1314
const suites: TestSuite[] = [
1415
ActionSuite,
1516
ContentSuite,
1617
DeployToolsSuite,
1718
ToolsSuite,
1819
StorageSuite,
19-
EncodingSuite
20+
EncodingSuite,
21+
SearchSuite
2022
]
2123

2224
export type TestSuite = {

src/testing/search.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import assert from "assert";
2+
import { TestSuite } from ".";
3+
import { parseFilter } from "../api/Filter";
4+
import { Search } from "../api/Search";
5+
import { instance } from "../instantiate";
6+
7+
export const SearchSuite: TestSuite = {
8+
name: `Search API tests`,
9+
tests: [
10+
{
11+
name: "Single member search", test: async () => {
12+
const connection = instance.getConnection()!;
13+
const result = await Search.searchMembers(connection, "QSYSINC", "QRPGLESRC", "IBM", "CMRPG");
14+
assert.strictEqual(result.term, "IBM");
15+
assert.strictEqual(result.hits.length, 1);
16+
const [hit] = result.hits;
17+
assert.strictEqual(hit.lines.length, 3);
18+
19+
const checkLine = (index: number, expectedNumber: number) => {
20+
assert.strictEqual(hit.lines[index].number, expectedNumber);
21+
assert.ok(hit.lines[index].content.includes(result.term));
22+
}
23+
24+
checkLine(0, 7);
25+
checkLine(1, 11);
26+
checkLine(2, 13);
27+
}
28+
},
29+
{
30+
name: "Generic name search", test: async () => {
31+
const connection = instance.getConnection()!;
32+
const memberFilter = "E*";
33+
const filter = parseFilter(memberFilter);
34+
const result = await Search.searchMembers(connection, "QSYSINC", "QRPGLESRC", "IBM", memberFilter);
35+
assert.ok(result.hits.every(hit => filter.test(hit.path.split("/").at(-1)!)));
36+
assert.ok(result.hits.every(hit => !hit.path.endsWith(`MBR`)));
37+
}
38+
},
39+
{
40+
name: "Filtered members list search", test: async () => {
41+
const connection = instance.getConnection()!;
42+
const library = "QSYSINC";
43+
const sourceFile = "QRPGLESRC";
44+
// Be stricter in the filter to try to make sure we have six results
45+
const memberFilter = "SQL*,T*";
46+
const filter = parseFilter(memberFilter);
47+
const checkNames = (names: string[]) => names.every(filter.test);
48+
49+
const members = await getConnection().getContent().getMemberList({ library, sourceFile, members: memberFilter });
50+
assert.ok(checkNames(members.map(member => member.name)));
51+
52+
const result = await Search.searchMembers(connection, "QSYSINC", "QRPGLESRC", "SQL", members);
53+
assert.strictEqual(result.hits.length, 6);
54+
assert.ok(checkNames(result.hits.map(hit => hit.path.split("/").at(-1)!)));
55+
assert.ok(result.hits.every(hit => !hit.path.endsWith(`MBR`)));
56+
}
57+
},
58+
{
59+
name: "pfgrep vs. qsh grep equivalency", test: async () => {
60+
const connection = instance.getConnection()!;
61+
const pfgrep = connection.remoteFeatures.pfgrep;
62+
// This test only needs to run if pfgrep is installed
63+
if (pfgrep) {
64+
const resultPfgrep = await Search.searchMembers(connection, "QSYSINC", "QRPGLESRC", "IBM", "CMRPG");
65+
getConnection().remoteFeatures.pfgrep = undefined;
66+
const resultQsh = await Search.searchMembers(connection, "QSYSINC", "QRPGLESRC", "IBM", "CMRPG");
67+
getConnection().remoteFeatures.pfgrep = pfgrep;
68+
assert.deepEqual(resultPfgrep, resultQsh);
69+
} else {
70+
assert.ok(true)
71+
}
72+
}
73+
}
74+
]
75+
}
76+
77+
function getConnection() {
78+
const connection = instance.getConnection();
79+
if (!connection) {
80+
throw Error("Cannot run test: no connection")
81+
}
82+
return connection;
83+
}

0 commit comments

Comments
 (0)