Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.

Commit fce02a0

Browse files
authored
Merge pull request #376 from tmc-cli/external-util-tests-aleksi
External util tests
2 parents 7f80b0b + 0ef2021 commit fce02a0

File tree

16 files changed

+437
-124
lines changed

16 files changed

+437
-124
lines changed

docs/HACKING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ Conceptually, there are three main parts in the program; the launch code, backen
2020

2121
Tmc-cli itself is mostly just an command line interface for the backend libraries. All the heavy lifting is done by [tmc-core](https://github.com/testmycode/tmc-core) and [tmc-langs](https://github.com/testmycode/tmc-langs) libraries. Tmc-cli also requires the server-side component of TestMyCode aka. [tmc-server](https://github.com/testmycode/tmc-server).
2222

23+
### Methods involved in program start up
24+
![Launch graph](https://rawgit.com/tmc-cli/tmc-cli/master/startup_control_flow.svg)
25+
2326
### Important classes
2427

2528
The `CliContext` object contains some cached data and singleton objects that are commonly used by utility classes and commands. Most importantly, it has the `Io` object which handles all user interaction via terminal. Never print anything using System.out.print(), since tests use the `TestIo` class which is dependent on the `Io` interface.

scripts/autocompletion.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ _tmc_opts()
1010
local cur
1111
local sub_command
1212
local main_args
13-
# Pointer to current completion word.
14-
# By convention, it's named "cur" but this isn't strictly necessary.
1513

1614
COMPREPLY=()
1715
cur="\${COMP_WORDS[COMP_CWORD]}"

scripts/stub.sh

Lines changed: 76 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
set -euo pipefail
44

5+
## Embeded binary magic
6+
57
MYSELF=$(which "$0" 2>/dev/null)
68
[ $? -gt 0 ] && [ -f "$0" ] && MYSELF="./$0"
79

10+
## Find the java binary and correct version
11+
812
JAVA_BIN=java
913
JAVA_HOME=${JAVA_HOME-}
1014
if [ -n "$JAVA_HOME" ]; then
@@ -22,21 +26,7 @@ if [ "$JAVA_VERSION" \< "1.7" ]; then
2226
exit 1
2327
fi
2428

25-
AUTOCOMPLETE="$HOME/.tmc-autocomplete.sh"
26-
27-
# this is used in autocompletion file
28-
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
29-
30-
tmc_update_autocomplete() {
31-
cat > "$AUTOCOMPLETE" <<- EOM
32-
TMC_AUTOCOMPLETE_SH
33-
EOM
34-
chmod +x "$AUTOCOMPLETE"
35-
}
36-
37-
tmc_update() {
38-
tmc_update_autocomplete
39-
}
29+
## find the place for running the autocomplete/alias file
4030

4131
tmc_detect_profile() {
4232
local PROFILE_ENV
@@ -89,58 +79,104 @@ tmc_detect_profile() {
8979
fi
9080
}
9181

92-
if [ ! -f "$AUTOCOMPLETE" ]; then
93-
tmc_update_autocomplete
82+
## Bash autocompletion script extraction
83+
84+
# This is used in autocompletion file
85+
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
86+
87+
tmc_autocomplete_file() {
88+
echo "${TMC_AUTOCOMPLETE_FILE-$HOME/.tmc-autocomplete.sh}"
89+
}
90+
91+
## Create the alias and autocompletion code if tmc alias not set
92+
tmc_update_autocomplete() {
93+
local AUTOCOMPLETE_FILE
94+
local PROFILE_FILE
95+
96+
AUTOCOMPLETE_FILE="$(tmc_autocomplete_file)"
97+
98+
cat > "$AUTOCOMPLETE_FILE" <<- EOM
99+
#EMBED_AUTOCOMPLETE_SH
100+
EOM
101+
chmod +x "$AUTOCOMPLETE_FILE"
102+
103+
PROFILE_FILE=$(tmc_detect_profile)
104+
105+
# get the aliases
106+
set +euo pipefail
107+
source $PROFILE_FILE
108+
set -euo pipefail
94109

95110
if type tmc &> /dev/null; then
96-
# don't add new alias if it exists already
97-
return
111+
exit
98112
fi
99113

100-
PROFILE_FILE=$(tmc_detect_profile)
101114
if [ -z "$PROFILE_FILE" ]; then
102-
echo "Profile file not found"
103-
echo "Put the \"source $AUTOCOMPLETE\" line in somewhere where"
104-
echo "it's run at terminal initialization."
115+
echo "Profile file not found" >&2
116+
echo "Put the \"source $AUTOCOMPLETE_FILE\" line in somewhere where" >&2
117+
echo "it's run at terminal initialization." >&2
105118
fi
106-
echo "source $AUTOCOMPLETE" >> "$PROFILE_FILE"
107-
fi
119+
echo "source $AUTOCOMPLETE_FILE" >> "$PROFILE_FILE"
108120

109-
if [ "${1-}" == "++internal-update" ]; then
110-
echo "Please report any error messages that may come up below."
121+
echo "To use new autocompletion run the following on command line:" >&2
122+
echo ". ~/.bashrc" >&2
123+
}
124+
125+
## Auto update code
126+
127+
##### If you MODIFY the install script then do the following:
128+
##### Enable the "THE INSTALL SCRIPT DEBUGGING LINE" at [Tmc]CliUpdater.java
129+
##### (It runs the dev script instead of the latest release script)
130+
##### And use the --force-update flag in application.
131+
132+
tmc_update() {
133+
tmc_update_autocomplete
134+
}
135+
136+
tmc_install_update() {
137+
echo "Please report any error messages that may come up below." >&2
111138
if [ ! -f tmc.new ]; then
112-
echo "Could not find the updated file."
139+
echo "Could not find the updated file." >&2
113140
exit 127
114141
fi
115142

116-
echo "Moving the tmc files..."
117-
if mv tmc tmc.orig; then
118-
echo "Failed to backup the original tmc binary"
143+
echo "Moving the tmc files..." >&2
144+
if ! mv tmc tmc.orig; then
145+
echo "Failed to backup the original tmc binary" >&2
119146
exit 128
120147
fi
121-
if mv tmc.new tmc; then
122-
echo "Failed to replace the original binary with new version"
123-
echo "You can replace manually the $PWD/tmc with $PWD/tmc.new"
148+
if ! mv tmc.new tmc; then
149+
echo "Failed to replace the original binary with new version" >&2
150+
echo "You can replace manually the $PWD/tmc with $PWD/tmc.new" >&2
124151
exit 129
125152
fi
126153

127154
rm tmc.orig &> /dev/null
128-
echo "Running the new tmc update script..."
155+
echo "Running the new tmc update script..." >&2
156+
echo "" >&2
129157
tmc_update
130158

131159
echo ""
132160
if [ -f tmc ]; then
133-
echo "Tmc cli installation was successful"
134-
#echo ""
135-
#echo "To use new autocompletion run the following on command line:"
136-
#echo ". ~/.bashrc"
161+
echo "Tmc cli installation was successful" >&2
137162
else
138-
echo "Tmc cli installation failed."
163+
echo "Tmc cli installation failed." >&2
139164
exit 127
140165
fi
141166
exit
167+
}
168+
169+
if [ "${1-}" == "++internal-update" ]; then
170+
tmc_install_update
142171
fi
143172

173+
if [ ! -f "$(tmc_autocomplete_file)" ]; then
174+
tmc_update_autocomplete
175+
exit
176+
fi
177+
178+
#EMBED_UNIT_TESTS_SH
179+
144180
export COLUMNS=$(tput cols)
145181
exec "$JAVA_BIN" -jar "$MYSELF" "$@"
146182

scripts/unit_tests.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#TODO
2+
3+
#backup the profile and the autocompletion if the exist
4+
5+
#try out the functions with different parameters
6+
7+
#restore the files
8+
9+
exit

scripts/wrapper.sh

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,23 @@ set -euo pipefail
55
# find newest jar file
66
jar_file=$(ls -t target/tmc-cli-*.jar | head -1)
77

8-
sed '/TMC_AUTOCOMPLETE_SH/ {
9-
r scripts/autocompletion.sh
10-
d }' scripts/stub.sh > tmc
8+
quotePerlSubst() {
9+
IFS= read -d '' -r < <(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[$/\]/\\&/g; s/\n/\\&/g' <<<"$1")
10+
printf %s "${REPLY%$'\n'}"
11+
}
12+
13+
content=$(cat scripts/stub.sh)
14+
15+
ac_content=$(quotePerlSubst "$(cat scripts/autocompletion.sh)")
16+
content=$(echo "$content" | sed "s/\#EMBED_AUTOCOMPLETE_SH/$ac_content/g")
17+
18+
if [ "${1-}" = "--with-unit-tests" ] ; then
19+
ac_content=$(quotePerlSubst "$(cat scripts/unit_tests.sh)")
20+
content=$(echo "$content" | \
21+
sed "s/\#EMBED_UNIT_TESTS_SH/$ac_content/g")
22+
fi
23+
24+
echo "$content" > tmc
1125

1226
cat "$jar_file" >> tmc && chmod +x tmc
1327

src/main/java/fi/helsinki/cs/tmc/cli/Application.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import fi.helsinki.cs.tmc.cli.io.HelpGenerator;
88
import fi.helsinki.cs.tmc.cli.io.Io;
99
import fi.helsinki.cs.tmc.cli.io.ShutdownHandler;
10-
import fi.helsinki.cs.tmc.cli.tmcstuff.WorkDir;
1110
import fi.helsinki.cs.tmc.cli.updater.TmcCliUpdater;
1211

1312
import org.apache.commons.cli.CommandLine;
@@ -49,6 +48,7 @@ public Application(CliContext context) {
4948

5049
options.addOption("h", "help", false, "Display help information about tmc-cli");
5150
options.addOption("v", "version", false, "Give the version of the tmc-cli");
51+
options.addOption("u", "force-update", false, "Force the auto-update");
5252

5353
//TODO implement the inTests as context.property
5454
if (!context.inTests()) {
@@ -73,7 +73,6 @@ private String[] parseArgs(String[] args) {
7373
try {
7474
line = this.parser.parse(this.options, args, true);
7575
} catch (ParseException e) {
76-
io.println("Invalid command line arguments.");
7776
io.println(e.getMessage());
7877
return null;
7978
}
@@ -90,7 +89,11 @@ private String[] parseArgs(String[] args) {
9089
return null;
9190
}
9291

93-
if (line.hasOption("h")) {
92+
boolean showHelp = line.hasOption("h");
93+
boolean showVersion = line.hasOption("v");
94+
boolean forceUpdate = line.hasOption("u");
95+
96+
if (showHelp) {
9497
// don't run the help sub-command with -h switch
9598
if (commandName.equals("help")) {
9699
runCommand("help", new String[0]);
@@ -99,10 +102,14 @@ private String[] parseArgs(String[] args) {
99102
runCommand(commandName, new String[]{"-h"});
100103
return null;
101104
}
102-
if (line.hasOption("v")) {
105+
if (showVersion) {
103106
io.println("TMC-CLI version " + EnvironmentUtil.getVersion());
104107
return null;
105108
}
109+
if (forceUpdate) {
110+
runAutoUpdate();
111+
return null;
112+
}
106113
return subArgs.toArray(new String[subArgs.size()]);
107114
}
108115

@@ -113,12 +120,12 @@ public void printHelp(String description) {
113120
public void run(String[] args) {
114121
context.setApp(this);
115122

116-
if (!context.inTests() && versionCheck()) {
123+
String[] commandArgs = parseArgs(args);
124+
if (commandArgs == null) {
117125
return;
118126
}
119127

120-
String[] commandArgs = parseArgs(args);
121-
if (commandArgs == null) {
128+
if (!context.inTests() && versionCheck()) {
122129
return;
123130
}
124131

@@ -156,8 +163,14 @@ private boolean versionCheck() {
156163
return false;
157164
}
158165

159-
TmcCliUpdater update = new TmcCliUpdater(io, EnvironmentUtil.getVersion(),
160-
EnvironmentUtil.isWindows());
166+
return runAutoUpdate();
167+
}
168+
169+
public boolean runAutoUpdate() {
170+
Map<String, String> properties = context.getProperties();
171+
Date now = new Date();
172+
TmcCliUpdater update = TmcCliUpdater.createUpdater(io,
173+
EnvironmentUtil.getVersion(), EnvironmentUtil.isWindows());
161174
boolean updated = update.run();
162175

163176
long timestamp = now.getTime();
@@ -167,7 +180,7 @@ private boolean versionCheck() {
167180
return updated;
168181
}
169182

170-
//TODO rename this as getColorProperty
183+
//TODO rename this as getColorProperty and move it somewhere else
171184
public Color.AnsiColor getColor(String propertyName) {
172185
String propertyValue = context.getProperties().get(propertyName);
173186
Color.AnsiColor color = Color.getColor(propertyValue);

src/main/java/fi/helsinki/cs/tmc/cli/CliContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public CliContext(Io io, TmcCore core, WorkDir workDir) {
6060
* constructor.
6161
*/
6262

63-
protected void setApp(Application app) {
63+
public void setApp(Application app) {
6464
this.application = app;
6565
}
6666

src/main/java/fi/helsinki/cs/tmc/cli/io/EnvironmentUtil.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.io.InputStream;
77
import java.io.IOException;
8+
import java.util.Arrays;
89
import java.util.Properties;
910

1011
public class EnvironmentUtil {
@@ -45,4 +46,50 @@ public static String getVersion() {
4546
return "n/a";
4647
}
4748
}
49+
50+
public static boolean runProcess(String[] args, boolean wait) {
51+
if (EnvironmentUtil.isWindows()) {
52+
logger.info("Launching external program " + Arrays.toString(args));
53+
try {
54+
Process proc = new ProcessBuilder(args).start();
55+
if (wait) {
56+
logger.info("(Windows) Waiting for "
57+
+ Arrays.toString(args) + " to finish executing");
58+
proc.waitFor();
59+
}
60+
} catch (Exception e) {
61+
logger.error("(Windows) Exception when running external program "
62+
+ Arrays.toString(args), e);
63+
return false;
64+
}
65+
} else {
66+
67+
StringBuilder program = new StringBuilder();
68+
for (int i = 0; i < args.length; i++) {
69+
program.append(" " + args[i]);
70+
}
71+
String[] exec = {"sh", "-c", program.toString() + " </dev/tty >/dev/tty"};
72+
// exec[0] = "sh";
73+
// exec[1] = "-c";
74+
// for (int i = 0; i < args.length; i++) {
75+
// exec[2 + i] = args[i];
76+
// }
77+
// exec[args.length + 2] = " </dev/tty >/dev/tty";
78+
// exec = {"sh -c "}
79+
try {
80+
Process proc = Runtime.getRuntime().exec(exec);
81+
if (wait) {
82+
logger.info("(Unix) Waiting for "
83+
+ Arrays.toString(exec) + " to finish executing");
84+
proc.waitFor();
85+
}
86+
return proc.exitValue() == 0;
87+
} catch (Exception e) {
88+
logger.error("(Unix) Exception when running external program "
89+
+ Arrays.toString(exec), e);
90+
return false;
91+
}
92+
}
93+
return false;
94+
}
4895
}

0 commit comments

Comments
 (0)