emit(MysqldConfig config, IExtractedFileSet exe) throws IOException {
24 | File baseDir = exe.baseDir();
25 | return Collections.newArrayList(
26 | exe.executable().getAbsolutePath(),
27 | "--no-defaults",
28 | "--log-output=NONE",
29 | format("--basedir=%s", baseDir),
30 | format("--datadir=%s/data", baseDir),
31 | format("--plugin-dir=%s/lib/plugin", baseDir),
32 | format("--lc-messages-dir=%s/share", baseDir),
33 | format("--port=%s", config.getPort()),
34 | format("--socket=%s", sockFile()),
35 | format("--user=%s", System.getProperty("user.name")),
36 | "--console",
37 | format("--character-set-server=%s", config.getCharset().getCharset()),
38 | format("--collation-server=%s", config.getCharset().getCollate()),
39 | format("--default-time-zone=%s", Utils.asHHmmOffset(config.getTimeZone())));
40 | }
41 |
42 | /**
43 | * Helper for getting stable sock classPathFile. Saving to local instance variable on service start does not work due
44 | * to the way flapdoodle process library works - it does all init in {@link de.flapdoodle.embed.process.runtime.AbstractProcess} and instance of
45 | * {@link com.wix.mysql.MysqldProcess} is not yet present, so vars are not initialized.
46 | * This algo gives stable sock classPathFile based on single executeCommands profile, but can leave trash sock classPathFiles in tmp dir.
47 | *
48 | * Notes:
49 | * .sock classPathFile needs to be in system temp dir and not in ex. target/...
50 | * This is due to possible problems with existing mysql installation and apparmor profiles
51 | * in linuxes.
52 | */
53 | private String sockFile() throws IOException {
54 | return Files.createTempFile(null, ".sock").toString();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/CommandEmitter.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.service;
2 |
3 | import com.wix.mysql.config.MysqldConfig;
4 | import com.wix.mysql.distribution.Version;
5 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
6 |
7 | import java.io.IOException;
8 | import java.util.List;
9 |
10 | public interface CommandEmitter {
11 | boolean matches(final Version version);
12 |
13 | List emit(final MysqldConfig config, final IExtractedFileSet exe) throws IOException;
14 | }
15 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/Mysql57CommandEmitter.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.service;
2 |
3 | import com.wix.mysql.config.MysqldConfig;
4 | import com.wix.mysql.distribution.Version;
5 | import de.flapdoodle.embed.process.collections.Collections;
6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
7 |
8 | import java.io.IOException;
9 | import java.util.List;
10 |
11 | public class Mysql57CommandEmitter implements CommandEmitter {
12 | @Override
13 | public boolean matches(Version version) {
14 | return version.getMajorVersion().equals("5.7");
15 | }
16 |
17 | @Override
18 | public List emit(MysqldConfig config, IExtractedFileSet exe) throws IOException {
19 | return Collections.newArrayList("--show_compatibility_56=ON", "--log_syslog=0");
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/Mysql8CommandEmitter.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.service;
2 |
3 | import com.wix.mysql.config.MysqldConfig;
4 | import com.wix.mysql.distribution.Version;
5 | import de.flapdoodle.embed.process.collections.Collections;
6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
7 |
8 | import java.io.IOException;
9 | import java.util.List;
10 |
11 | public class Mysql8CommandEmitter implements CommandEmitter {
12 | @Override
13 | public boolean matches(Version version) {
14 | return version.getMajorVersion().equals("8.0");
15 | }
16 |
17 | @Override
18 | public List emit(MysqldConfig config, IExtractedFileSet exe) throws IOException {
19 | return Collections
20 | .newArrayList("--default_authentication_plugin=mysql_native_password");
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/Pre57CommandEmitter.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.service;
2 |
3 | import com.wix.mysql.config.MysqldConfig;
4 | import com.wix.mysql.distribution.Version;
5 | import de.flapdoodle.embed.process.collections.Collections;
6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
7 |
8 | import java.io.IOException;
9 | import java.util.List;
10 |
11 | public class Pre57CommandEmitter implements CommandEmitter {
12 | @Override
13 | public boolean matches(Version version) {
14 | return version.getMajorVersion().equals("5.5") || version.getMajorVersion().equals("5.6");
15 | }
16 |
17 | @Override
18 | public List emit(MysqldConfig config, IExtractedFileSet exe) throws IOException {
19 | return Collections.newArrayList("--skip-name-resolve");
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/ServiceCommandBuilder.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.service;
2 |
3 | import de.flapdoodle.embed.process.collections.Collections;
4 |
5 | import java.util.Collection;
6 | import java.util.HashSet;
7 | import java.util.List;
8 | import java.util.Set;
9 |
10 | import static java.lang.String.format;
11 |
12 | public class ServiceCommandBuilder {
13 | private final String version;
14 | private final List args = Collections.newArrayList();
15 | private final Set keys = new HashSet<>();
16 |
17 | public ServiceCommandBuilder(final String version) {
18 | this.version = version;
19 | }
20 |
21 | public ServiceCommandBuilder addAll(Collection args) {
22 | for (String arg : args) {
23 | String argName = argName(arg);
24 | if (!keys.add(argName)) {
25 | throw new RuntimeException(format("argument with name '%s' is already present in argument list.", argName));
26 | }
27 | }
28 | this.args.addAll(args);
29 | return this;
30 | }
31 |
32 | public List emit() {
33 | if (args.isEmpty()) {
34 | throw new RuntimeException("mysqld startup command was not populated for version: " + version);
35 | }
36 |
37 | return args;
38 | }
39 |
40 | private String argName(String arg) {
41 | return arg.split("=")[0];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/service/UserProvidedArgumentsEmitter.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.service;
2 |
3 | import com.wix.mysql.config.MysqldConfig;
4 | import com.wix.mysql.distribution.Version;
5 | import de.flapdoodle.embed.process.collections.Collections;
6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
7 |
8 | import java.io.IOException;
9 | import java.util.List;
10 |
11 | public class UserProvidedArgumentsEmitter implements CommandEmitter {
12 | @Override
13 | public boolean matches(Version version) {
14 | return true;
15 | }
16 |
17 | @Override
18 | public List emit(MysqldConfig config, IExtractedFileSet exe) throws IOException {
19 | List result = Collections.newArrayList();
20 | for (MysqldConfig.ServerVariable var: config.getServerVariables()) {
21 | result.add(var.toCommandLineArgument());
22 | }
23 | return result;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/FilePermissionsInitializer.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.setup;
2 |
3 | import com.wix.mysql.config.MysqldConfig;
4 | import com.wix.mysql.distribution.Version;
5 | import de.flapdoodle.embed.process.config.IRuntimeConfig;
6 | import de.flapdoodle.embed.process.config.store.FileType;
7 | import de.flapdoodle.embed.process.distribution.Platform;
8 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 |
13 | public class FilePermissionsInitializer implements Initializer {
14 | @Override
15 | public boolean matches(Version version) {
16 | return Platform.detect().isUnixLike();
17 | }
18 |
19 | @Override
20 | public void apply(IExtractedFileSet fileSet, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException {
21 | for (File f : fileSet.files(FileType.Library)) {
22 | f.setExecutable(true);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/Initializer.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.setup;
2 |
3 |
4 | import com.wix.mysql.config.MysqldConfig;
5 | import com.wix.mysql.distribution.Version;
6 | import de.flapdoodle.embed.process.config.IRuntimeConfig;
7 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
8 |
9 | import java.io.IOException;
10 |
11 | public interface Initializer {
12 | boolean matches(Version version);
13 |
14 | void apply(IExtractedFileSet files, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException;
15 | }
16 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/Mysql57Initializer.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.setup;
2 |
3 | import com.wix.mysql.config.MysqldConfig;
4 | import com.wix.mysql.distribution.Version;
5 | import de.flapdoodle.embed.process.config.IRuntimeConfig;
6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
7 | import org.apache.commons.io.FileUtils;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 |
12 | import static java.lang.String.format;
13 | import static java.util.concurrent.TimeUnit.NANOSECONDS;
14 |
15 | public class Mysql57Initializer implements Initializer {
16 | @Override
17 | public boolean matches(Version version) {
18 | return version.getMajorVersion().equals("5.7");
19 | }
20 |
21 | @Override
22 | public void apply(IExtractedFileSet files, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException {
23 | File baseDir = files.baseDir();
24 | FileUtils.deleteDirectory(new File(baseDir, "data"));
25 |
26 | Process p = Runtime.getRuntime().exec(new String[] {
27 | files.executable().getAbsolutePath(),
28 | "--no-defaults",
29 | "--initialize-insecure",
30 | "--ignore-db-dir",
31 | format("--basedir=%s", baseDir),
32 | format("--datadir=%s/data", baseDir)
33 | });
34 |
35 | new ProcessRunner(files.executable().getAbsolutePath()).run(p, runtimeConfig, config.getTimeout(NANOSECONDS));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/Mysql8Initializer.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.setup;
2 |
3 | import com.wix.mysql.config.MysqldConfig;
4 | import com.wix.mysql.distribution.Version;
5 | import de.flapdoodle.embed.process.config.IRuntimeConfig;
6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
7 | import org.apache.commons.io.FileUtils;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 |
12 | import static java.lang.String.format;
13 | import static java.util.concurrent.TimeUnit.NANOSECONDS;
14 |
15 | public class Mysql8Initializer implements Initializer {
16 | @Override
17 | public boolean matches(Version version) {
18 | return version.getMajorVersion().equals("8.0");
19 | }
20 |
21 | @Override
22 | public void apply(IExtractedFileSet files, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException {
23 | File baseDir = files.baseDir();
24 | FileUtils.deleteDirectory(new File(baseDir, "data"));
25 |
26 | Process p = Runtime.getRuntime().exec(new String[]{
27 | files.executable().getAbsolutePath(),
28 | "--no-defaults",
29 | "--initialize-insecure",
30 | format("--basedir=%s", baseDir),
31 | format("--datadir=%s/data", baseDir)});
32 |
33 | new ProcessRunner(files.executable().getAbsolutePath()).run(p, runtimeConfig, config.getTimeout(NANOSECONDS));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/NixBefore57Initializer.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.setup;
2 |
3 | import com.wix.mysql.config.MysqldConfig;
4 | import com.wix.mysql.distribution.Version;
5 | import de.flapdoodle.embed.process.config.IRuntimeConfig;
6 | import de.flapdoodle.embed.process.distribution.Platform;
7 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 |
12 | import static java.lang.String.format;
13 | import static java.util.concurrent.TimeUnit.NANOSECONDS;
14 |
15 | public class NixBefore57Initializer implements Initializer {
16 | @Override
17 | public boolean matches(Version version) {
18 | return Platform.detect().isUnixLike() &&
19 | (version.getMajorVersion().equals("5.6") || version.getMajorVersion().equals("5.5"));
20 | }
21 |
22 | @Override
23 | public void apply(IExtractedFileSet files, IRuntimeConfig runtimeConfig, MysqldConfig config) throws IOException {
24 | File baseDir = files.baseDir();
25 | Process p = Runtime.getRuntime().exec(new String[]{
26 | "scripts/mysql_install_db",
27 | "--force",
28 | "--no-defaults",
29 | format("--basedir=%s", baseDir),
30 | format("--datadir=%s/data", baseDir)},
31 | null,
32 | baseDir);
33 |
34 | new ProcessRunner("scripts/mysql_install_db").run(p, runtimeConfig, config.getTimeout(NANOSECONDS));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/distribution/setup/ProcessRunner.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.setup;
2 |
3 | import com.wix.mysql.exceptions.MissingDependencyException;
4 | import com.wix.mysql.io.TimingOutProcessExecutor;
5 | import de.flapdoodle.embed.process.config.IRuntimeConfig;
6 | import de.flapdoodle.embed.process.io.IStreamProcessor;
7 | import de.flapdoodle.embed.process.io.Processors;
8 | import de.flapdoodle.embed.process.io.ReaderProcessor;
9 | import de.flapdoodle.embed.process.io.StreamToLineProcessor;
10 |
11 | import java.io.IOException;
12 | import java.io.InputStreamReader;
13 |
14 | import static java.lang.String.format;
15 |
16 | final class ProcessRunner {
17 |
18 | private final TimingOutProcessExecutor tope;
19 |
20 | ProcessRunner(String cmd) {
21 | this.tope = new TimingOutProcessExecutor(cmd);
22 | }
23 |
24 | void run(Process p, IRuntimeConfig runtimeConfig, long timeoutNanos) throws IOException {
25 | CollectingAndForwardingStreamProcessor wrapped =
26 | new CollectingAndForwardingStreamProcessor(runtimeConfig.getProcessOutput().getOutput());
27 | IStreamProcessor loggingWatch = StreamToLineProcessor.wrap(wrapped);
28 |
29 | try {
30 | ReaderProcessor processorOne = Processors.connect(new InputStreamReader(p.getInputStream()), loggingWatch);
31 | ReaderProcessor processorTwo = Processors.connect(new InputStreamReader(p.getErrorStream()), loggingWatch);
32 |
33 | int retCode = tope.waitFor(p, timeoutNanos);
34 |
35 | if (retCode != 0) {
36 | processorOne.join(10000);
37 | processorTwo.join(10000);
38 | resolveException(retCode, wrapped.getOutput());
39 | }
40 |
41 | } catch (InterruptedException e) {
42 | throw new RuntimeException(e);
43 | }
44 | }
45 |
46 | private static void resolveException(int retCode, String output) {
47 | if (output.contains("error while loading shared libraries: libaio.so")) {
48 | throw new MissingDependencyException(
49 | "System library 'libaio.so.1' missing. " +
50 | "Please install it via system package manager, ex. 'sudo apt-get install libaio1'.\n" +
51 | "For details see: http://bugs.mysql.com/bug.php?id=60544");
52 | } else {
53 | throw new RuntimeException(format("Command exited with error code: '%s' and output: '%s'", retCode, output));
54 | }
55 | }
56 |
57 | public static class CollectingAndForwardingStreamProcessor implements IStreamProcessor {
58 | volatile String output = "";
59 | final IStreamProcessor forwardTo;
60 |
61 | CollectingAndForwardingStreamProcessor(IStreamProcessor forwardTo) {
62 | this.forwardTo = forwardTo;
63 | }
64 |
65 | public void process(String block) {
66 | output = output + block;
67 | forwardTo.process(block);
68 | }
69 |
70 | public void onProcessed() {
71 | forwardTo.onProcessed();
72 | }
73 |
74 | String getOutput() {
75 | return output;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/exceptions/CommandFailedException.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.exceptions;
2 |
3 | public class CommandFailedException extends RuntimeException {
4 | public CommandFailedException(String cmd, String schema, int errorCode, String errorMessage) {
5 | super(String.format("Command '%s' on schema '%s' failed with errCode '%s' and output '%s'",
6 | cmd, schema, errorCode, errorMessage));
7 | }
8 |
9 | public CommandFailedException(String cmd, String schema, String errorMessage, Throwable e) {
10 | super(String.format("Command '%s' on schema '%s' failed with message '%s'", cmd, schema, errorMessage), e);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/exceptions/MissingDependencyException.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.exceptions;
2 |
3 | public class MissingDependencyException extends RuntimeException {
4 | public MissingDependencyException(String message) {
5 | super(message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/exceptions/UnsupportedPlatformException.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.exceptions;
2 |
3 | public class UnsupportedPlatformException extends RuntimeException {
4 | public UnsupportedPlatformException(String message) {
5 | super(message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/io/ConsoleProgressListener.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.io;
2 |
3 | import de.flapdoodle.embed.process.io.progress.StandardConsoleProgressListener;
4 |
5 | import static java.lang.String.format;
6 |
7 | public class ConsoleProgressListener extends StandardConsoleProgressListener {
8 |
9 | private ProgressStepper progressStepper = new ProgressStepper();
10 |
11 | @Override
12 | public void progress(String label, int percent) {
13 | if (progressStepper.hasNext(percent)) {
14 | int percentageToReport = progressStepper.setAndGet(percent);
15 | System.out.println(format("%s %d%%", label, percentageToReport));
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/io/NotifyingStreamProcessor.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.io;
2 |
3 | import de.flapdoodle.embed.process.io.IStreamProcessor;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | public class NotifyingStreamProcessor implements IStreamProcessor {
10 |
11 | private final List listeners = new ArrayList<>();
12 | private final IStreamProcessor delegate;
13 |
14 | public NotifyingStreamProcessor(IStreamProcessor processor) {
15 | this.delegate = processor;
16 | }
17 |
18 | public ResultMatchingListener addListener(ResultMatchingListener listener) {
19 | listeners.add(listener);
20 | return listener;
21 | }
22 |
23 | @Override
24 | public void process(String block) {
25 | for (ResultMatchingListener listener : listeners) {
26 | listener.onMessage(block);
27 | }
28 | delegate.process(block);
29 | }
30 |
31 | @Override
32 | public void onProcessed() {
33 |
34 | }
35 |
36 | public static class ResultMatchingListener {
37 |
38 | private final String successPattern;
39 | private final String failurePattern = "[ERROR]";
40 | private final StringBuilder output = new StringBuilder();
41 | private boolean initWithSuccess = false;
42 | private String failureFound = null;
43 | private boolean completed = false;
44 |
45 | public ResultMatchingListener(String successPattern) {
46 | this.successPattern = successPattern;
47 | }
48 |
49 | public void onMessage(final String message) {
50 | output.append(message);
51 | if (containsPattern()) {
52 | gotResult(true, null);
53 | } else {
54 | int failureIndex = output.indexOf(failurePattern);
55 | if (failureIndex != -1) {
56 | gotResult(false, output.substring(failureIndex));
57 | }
58 | }
59 | }
60 |
61 | private boolean containsPattern() {
62 | return output.indexOf(this.successPattern) != -1;
63 | }
64 |
65 | private synchronized void gotResult(boolean success, String message) {
66 | initWithSuccess = success;
67 | failureFound = message;
68 | completed = true;
69 | notify();
70 | }
71 |
72 | public synchronized void waitForResult(long timeoutMs) {
73 | try {
74 | wait(timeoutMs);
75 | if (!completed) {
76 | throw new RuntimeException(String.format("Timeout of %s sec exceeded while waiting for process to complete", TimeUnit.MILLISECONDS.toSeconds(timeoutMs)));
77 | }
78 | } catch (InterruptedException e) {
79 | e.printStackTrace();
80 | }
81 | }
82 |
83 | public boolean isInitWithSuccess() {
84 | return initWithSuccess;
85 | }
86 |
87 | public String getFailureFound() {
88 | return failureFound;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/io/ProgressStepper.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.io;
2 |
3 | public class ProgressStepper {
4 |
5 | int lastReported = -1;
6 |
7 | public boolean hasNext(int value) {
8 | return (nextCandidate(value) != lastReported);
9 | }
10 |
11 | public int setAndGet(int value) {
12 | lastReported = nextCandidate(value);
13 | return lastReported;
14 | }
15 |
16 | private int nextCandidate(int value) {
17 | return (int)(5 * (Math.floor(Math.abs(value / 5))));
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/io/TimingOutProcessExecutor.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.io;
2 |
3 | import org.apache.commons.io.IOUtils;
4 |
5 | import java.io.IOException;
6 |
7 | import static java.lang.String.format;
8 | import static java.util.concurrent.TimeUnit.NANOSECONDS;
9 |
10 | public class TimingOutProcessExecutor {
11 |
12 | private final String cmd;
13 |
14 | public TimingOutProcessExecutor(String cmd) {
15 | this.cmd = cmd;
16 | }
17 |
18 | public int waitFor(Process p, long timeoutNanos) throws InterruptedException, IOException {
19 | long startTime = System.nanoTime();
20 | long rem = timeoutNanos;
21 |
22 | do {
23 | try {
24 | return p.exitValue();
25 | } catch (IllegalThreadStateException ex) {
26 | if (rem > 0) {
27 | Thread.sleep(Math.min(NANOSECONDS.toMillis(rem) + 1, 100));
28 | }
29 | }
30 | rem = timeoutNanos - (System.nanoTime() - startTime);
31 | } while (rem > 0);
32 | String collectedOutput = IOUtils.toString(p.getInputStream()) + IOUtils.toString(p.getErrorStream());
33 | p.destroy();
34 | throw new InterruptedException(format("Timeout of %s sec exceeded while waiting for '%s' to complete. Collected output: %s",
35 | NANOSECONDS.toSeconds(timeoutNanos),
36 | this.cmd,
37 | collectedOutput));
38 | }
39 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/store/SafeExtractedArtifactStore.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.store;
2 |
3 | import de.flapdoodle.embed.process.config.store.IDownloadConfig;
4 | import de.flapdoodle.embed.process.distribution.Distribution;
5 | import de.flapdoodle.embed.process.extract.DirectoryAndExecutableNaming;
6 | import de.flapdoodle.embed.process.extract.IExtractedFileSet;
7 | import de.flapdoodle.embed.process.store.ExtractedArtifactStore;
8 | import de.flapdoodle.embed.process.store.IDownloader;
9 | import org.apache.commons.io.FileUtils;
10 |
11 | import java.io.File;
12 | import java.io.IOException;
13 |
14 | /**
15 | * This is a wrapper around `ExtractedArtifactStore` which deletes the temp directory BEFORE extracting
16 | * just in case we have left overs from last crashed run.
17 | */
18 | class SafeExtractedArtifactStore extends ExtractedArtifactStore {
19 | private String directory;
20 |
21 | SafeExtractedArtifactStore(IDownloadConfig downloadConfig, IDownloader downloader, DirectoryAndExecutableNaming extraction, DirectoryAndExecutableNaming directory) {
22 | super(downloadConfig, downloader, extraction, directory);
23 | this.directory = directory.getDirectory().asFile().getAbsolutePath();
24 | }
25 |
26 | @Override
27 | public IExtractedFileSet extractFileSet(Distribution distribution) throws IOException {
28 | FileUtils.deleteDirectory(new File(directory));
29 |
30 | IExtractedFileSet extractedFiles = super.extractFileSet(distribution);
31 |
32 | // Files.createDirectory(new File(directory, "data").toPath());
33 |
34 | return extractedFiles;
35 | }
36 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/store/SafeExtractedArtifactStoreBuilder.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.store;
2 |
3 | import com.wix.mysql.config.DownloadConfig;
4 | import com.wix.mysql.config.DownloadConfigBuilder;
5 | import com.wix.mysql.config.MysqldConfig;
6 | import com.wix.mysql.config.directories.TargetGeneratedFixedPath;
7 | import com.wix.mysql.config.extract.NoopNaming;
8 | import com.wix.mysql.config.extract.PathPrefixingNaming;
9 | import de.flapdoodle.embed.process.extract.DirectoryAndExecutableNaming;
10 | import de.flapdoodle.embed.process.io.directories.FixedPath;
11 | import de.flapdoodle.embed.process.io.directories.IDirectory;
12 | import de.flapdoodle.embed.process.store.Downloader;
13 | import de.flapdoodle.embed.process.store.IArtifactStore;
14 |
15 | import java.io.File;
16 | import java.util.UUID;
17 |
18 | public class SafeExtractedArtifactStoreBuilder extends de.flapdoodle.embed.process.store.ExtractedArtifactStoreBuilder {
19 |
20 | public SafeExtractedArtifactStoreBuilder defaults(
21 | final MysqldConfig mysqldConfig,
22 | final DownloadConfig downloadConfig) {
23 |
24 | String tempExtractDir = String.format("mysql-%s-%s", mysqldConfig.getVersion().getMajorVersion(), UUID.randomUUID());
25 | String combinedPath = new File(mysqldConfig.getTempDir(), tempExtractDir).getPath();
26 | IDirectory preExtractDir = new FixedPath(new File(downloadConfig.getCacheDir(), "extracted").getPath());
27 |
28 | executableNaming().setDefault(new PathPrefixingNaming("bin/"));
29 | download().setDefault(new DownloadConfigBuilder().defaults(downloadConfig).build());
30 | downloader().setDefault(new Downloader());
31 | extractDir().setDefault(preExtractDir);
32 | extractExecutableNaming().setDefault(new NoopNaming());
33 |
34 | tempDir().setDefault(new TargetGeneratedFixedPath(combinedPath));
35 |
36 | return this;
37 | }
38 |
39 | @Override
40 | public IArtifactStore build() {
41 | DirectoryAndExecutableNaming extract = new DirectoryAndExecutableNaming(get(EXTRACT_DIR_FACTORY), get(EXTRACT_EXECUTABLE_NAMING));
42 | DirectoryAndExecutableNaming temp = new DirectoryAndExecutableNaming(tempDir().get(), executableNaming().get());
43 |
44 | return new SafeExtractedArtifactStore(get(DOWNLOAD_CONFIG), get(DOWNLOADER), extract, temp);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/main/java/com/wix/mysql/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.utils;
2 |
3 | import java.io.IOException;
4 | import java.io.Reader;
5 | import java.nio.CharBuffer;
6 | import java.util.Calendar;
7 | import java.util.List;
8 | import java.util.TimeZone;
9 |
10 | import static java.lang.String.format;
11 | import static java.util.concurrent.TimeUnit.HOURS;
12 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
13 |
14 | public class Utils {
15 | public static void closeCloseables(Reader... readers) {
16 |
17 | for (Reader reader : readers) {
18 | try {
19 | if (reader != null) reader.close();
20 | } catch (IOException ignored) {
21 | }
22 | }
23 | }
24 |
25 | public static String asHHmmOffset(TimeZone timeZone) {
26 | long offsetInMillis = timeZone.getOffset(Calendar.getInstance().getTimeInMillis());
27 | return format("%s%02d:%02d",
28 | offsetInMillis >= 0 ? "+" : "-",
29 | Math.abs(MILLISECONDS.toHours(offsetInMillis)),
30 | MILLISECONDS.toMinutes(offsetInMillis) - HOURS.toMinutes(MILLISECONDS.toHours(offsetInMillis)));
31 | }
32 |
33 | public static T or(T arg1, T arg2) {
34 | return arg1 != null ? arg1 : arg2;
35 | }
36 |
37 | public static boolean isNullOrEmpty(String str) {
38 | return str == null || str.isEmpty();
39 | }
40 |
41 | public static String join(List> list, String delim) {
42 | if (list.isEmpty()) return "";
43 | StringBuilder sb = new StringBuilder(list.get(0).toString());
44 | for (int i = 1; i < list.size(); i++) {
45 | sb.append(delim).append(list.get(i).toString());
46 | }
47 | return sb.toString();
48 | }
49 |
50 | public static String readToString(Reader reader) throws IOException {
51 | StringBuilder sb = new StringBuilder();
52 | CharBuffer buf = CharBuffer.allocate(1024);
53 | while (reader.read(buf) != -1) {
54 | buf.flip();
55 | sb.append(buf);
56 | buf.clear();
57 | }
58 | return sb.toString();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/_maven.repositories:
--------------------------------------------------------------------------------
1 | #NOTE: This is an internal implementation file, its format can be changed without prior notice.
2 | #Tue Mar 22 18:10:48 EET 2016
3 | external-jar-with-schemas-1.0.1.jar>in-project=
4 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/_remote.repositories:
--------------------------------------------------------------------------------
1 | #NOTE: This is an Aether internal implementation file, its format can be changed without prior notice.
2 | #Sat May 14 14:58:31 EEST 2016
3 | external-jar-with-schemas-1.0.1.jar>=
4 | external-jar-with-schemas-1.0.1.pom>=
5 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/external-jar-with-schemas-1.0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/wix-embedded-mysql/2a86b59ecd66505a7f080d30c3578f84b3521afd/wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/external-jar-with-schemas-1.0.1.jar
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/1.0.1/external-jar-with-schemas-1.0.1.pom:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | jar
6 | external-jar-with-schemas
7 | 1.0.1
8 | POM was created from install:install-file
9 |
10 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/lib/jar/external-jar-with-schemas/maven-metadata-local.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | jar
4 | external-jar-with-schemas
5 |
6 | 1.0.1
7 |
8 | 1.0.1
9 |
10 | 20160516115831
11 |
12 |
13 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/resources/data/arbitrary_script.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO t1 values(20)
2 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/resources/data/arbitrary_script2.sql:
--------------------------------------------------------------------------------
1 | SELECT * FROM t1
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/resources/db/001_init.sql:
--------------------------------------------------------------------------------
1 | create table t1 (col1 INTEGER NOT NULL,col2 VARCHAR(10));
2 | INSERT INTO t1 values(10, "zzz");
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/resources/db/002_update1.sql:
--------------------------------------------------------------------------------
1 | create table t2 (col1 INTEGER NOT NULL,col2 VARCHAR(10));
2 | INSERT INTO t2 values(20, "zzz");
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/resources/db/003_update2.sql:
--------------------------------------------------------------------------------
1 | create table t3 (col1 INTEGER NOT NULL,col2 VARCHAR(10));
2 | INSERT INTO t3 values(30, "zzz");
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/resources/db/004_update3.sql:
--------------------------------------------------------------------------------
1 | create table t1 (col1 INTEGER, col2 VARCHAR(10));
2 | INSERT INTO t1 values(1, '你好!');
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/EmbeddedMysqlTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql
2 |
3 | import java.io.File
4 | import java.util.concurrent.TimeUnit
5 |
6 | import com.wix.mysql.EmbeddedMysql._
7 | import com.wix.mysql.ScriptResolver.classPathScript
8 | import com.wix.mysql.config.Charset._
9 | import com.wix.mysql.config.DownloadConfig.aDownloadConfig
10 | import com.wix.mysql.config.MysqldConfig.{SystemDefaults, aMysqldConfig}
11 | import com.wix.mysql.config.ProxyFactory.aHttpProxy
12 | import com.wix.mysql.config.SchemaConfig.aSchemaConfig
13 | import com.wix.mysql.distribution.Version
14 | import com.wix.mysql.exceptions.CommandFailedException
15 | import com.wix.mysql.support.IntegrationTest._
16 | import com.wix.mysql.support.{HttpProxyServerSupport, IntegrationTest}
17 |
18 | import scala.collection.JavaConverters._
19 |
20 |
21 | class EmbeddedMysqlTest extends IntegrationTest with HttpProxyServerSupport {
22 |
23 | "EmbeddedMysql instance" should {
24 |
25 | "start with default values" in {
26 | val config = testConfigBuilder().build
27 |
28 | val mysqld = start(anEmbeddedMysql(config))
29 |
30 | mysqld must
31 | haveCharsetOf(UTF8MB4) and
32 | beAvailableOn(3310, "auser", "sa", SystemDefaults.SCHEMA) and
33 | haveServerTimezoneMatching("UTC") and
34 | haveSystemVariable("basedir", contain(pathFor("/target/", "/mysql-5.7-")))
35 | }
36 |
37 | "use custom values provided via MysqldConfig" in {
38 | val tempDir = System.getProperty("java.io.tmpdir")
39 | val config = testConfigBuilder()
40 | .withCharset(LATIN1)
41 | .withUser("zeUser", "zePassword")
42 | .withPort(1112)
43 | .withTimeZone("US/Michigan")
44 | .withTempDir(tempDir)
45 | .build
46 |
47 | val mysqld = start(anEmbeddedMysql(config))
48 |
49 | mysqld must
50 | haveCharsetOf(LATIN1) and
51 | beAvailableOn(1112, "zeUser", "zePassword", SystemDefaults.SCHEMA) and
52 | haveServerTimezoneMatching("US/Michigan") and
53 | haveSystemVariable("basedir", contain(pathFor(tempDir, "/mysql-5.7-")))
54 | }
55 |
56 |
57 | "allow to provide network proxy" in {
58 | val targetVersion = Version.v5_6_latest
59 | anEmbeddedMysql(targetVersion).start().stop()
60 |
61 | withProxyOn(3210, 3220, targetVersion) { (proxy, proxyPort, targetPort, version) =>
62 | val config = aMysqldConfig(version).build
63 |
64 | val mysqld = start(anEmbeddedMysql(config)
65 | .withDownloadConfig(aDownloadConfig()
66 | .withProxy(aHttpProxy("127.0.0.1", proxyPort))
67 | .withBaseUrl(s"http://localhost:$targetPort")
68 | .build())
69 | .addSchema("aschema"))
70 |
71 | mysqld must beAvailableOn(config, "aschema")
72 | proxy.wasDownloaded must beTrue
73 | }
74 | }
75 |
76 |
77 | "accept system variables" in {
78 | val config = testConfigBuilder()
79 | .withServerVariable("max_connect_errors", 666)
80 | .build
81 |
82 | val mysqld = start(anEmbeddedMysql(config))
83 |
84 | mysqld must haveSystemVariable("max_connect_errors", ===("666"))
85 | }
86 |
87 | "not allow to override library-managed system variables" in {
88 | val config = testConfigBuilder()
89 | .withTimeZone("US/Michigan")
90 | .withServerVariable("default-time-zone", "US/Eastern")
91 | .build
92 |
93 | start(anEmbeddedMysql(config)) must throwA[RuntimeException]
94 | }
95 |
96 | "respect provided timeout" in {
97 | start(anEmbeddedMysql(aMysqldConfig(targetTestVersion).withTimeout(10, TimeUnit.MILLISECONDS).build)) must
98 | throwA[RuntimeException].like { case e => e.getMessage must contain("0 sec") }
99 | }
100 | }
101 |
102 | "EmbeddedMysql schema reload" should {
103 | "reset schema" in {
104 | val mysqldConfig = testConfigBuilder().build
105 | val schemaConfig = aSchemaConfig("aSchema")
106 | .withScripts(aMigrationWith("create table t1 (col1 INTEGER);"))
107 | .build
108 |
109 | val mysqld = start(anEmbeddedMysql(mysqldConfig).addSchema(schemaConfig))
110 |
111 | anUpdate(mysqld, onSchema = "aSchema", sql = "insert into t1 values(10)") must beSuccessful
112 | aSelect[java.lang.Long](mysqld, onSchema = "aSchema", sql = "select col1 from t1 where col1 = 10;") mustEqual 10
113 |
114 | mysqld.reloadSchema(schemaConfig)
115 |
116 | aSelect[java.lang.Long](mysqld, onSchema = "aSchema", sql = "select col1 from t1 where col1 = 10;") must
117 | failWith("Incorrect result size: expected 1, actual 0")
118 | }
119 | }
120 |
121 | "EmbeddedMysql schema creation" should {
122 | "use defaults" in {
123 | val config = testConfigBuilder().build
124 |
125 | val mysqld = start(anEmbeddedMysql(config).addSchema("aSchema"))
126 |
127 | mysqld must
128 | haveSchemaCharsetOf(UTF8MB4, "aSchema") and
129 | beAvailableOn(3310, "auser", "sa", andSchema = "aSchema")
130 | }
131 |
132 | "use custom values" in {
133 | val config = testConfigBuilder().build
134 | val schema = aSchemaConfig("aSchema")
135 | .withCharset(LATIN1)
136 | .build
137 |
138 | val mysqld = start(anEmbeddedMysql(config).addSchema(schema))
139 |
140 | mysqld must
141 | haveSchemaCharsetOf(LATIN1, "aSchema") and
142 | beAvailableOn(3310, "auser", "sa", andSchema = "aSchema")
143 | }
144 |
145 | "inherit schema charset from instance" in {
146 | val config = testConfigBuilder().withCharset(LATIN1).build
147 | val schema = aSchemaConfig("aSchema").build
148 |
149 | val mysqld = start(anEmbeddedMysql(config).addSchema(schema))
150 |
151 | mysqld must
152 | haveSchemaCharsetOf(LATIN1, "aSchema") and
153 | beAvailableOn(3310, "auser", "sa", andSchema = "aSchema")
154 | }
155 |
156 | "apply migrations when providing single file" in {
157 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build)
158 | .addSchema("aSchema", aMigrationWith("create table t1 (col1 INTEGER);")))
159 |
160 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t1;") must beSuccessful
161 | }
162 |
163 | "apply migrations from multiple files" in {
164 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build)
165 | .addSchema("aSchema",
166 | aMigrationWith("create table t1 (col1 INTEGER);"),
167 | aMigrationWith("create table t2 (col1 INTEGER);"))
168 | )
169 |
170 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t1;") must beSuccessful
171 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t2;") must beSuccessful
172 | }
173 |
174 | "apply migrations via SchemaConfig" in {
175 | val config = aSchemaConfig("aSchema")
176 | .withScripts(
177 | aMigrationWith("create table t1 (col1 INTEGER);"),
178 | aMigrationWith("create table t2 (col1 INTEGER);"))
179 | .withCommands(
180 | "create table t3 (col1 INTEGER);\n" +
181 | "create table t4 (col2 INTEGER)")
182 | .build
183 |
184 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build).addSchema(config))
185 |
186 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t1;") must beSuccessful
187 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t2;") must beSuccessful
188 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col1) from t3;") must beSuccessful
189 | aQuery(mysqld, onSchema = "aSchema", sql = "select count(col2) from t4;") must beSuccessful
190 | }
191 |
192 | "quote created table name" in {
193 | val config = aSchemaConfig("a-table")
194 | .withScripts(aMigrationWith("create table t1 (col1 INTEGER);"))
195 | .build
196 |
197 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build).addSchema(config))
198 |
199 | aQuery(mysqld, onSchema = "a-table", sql = "select count(col1) from t1;") must beSuccessful
200 | }
201 |
202 | }
203 |
204 | "EmbeddedMysql schema modification" should {
205 |
206 | "drop existing schema" in {
207 | val schemaConfig = aSchemaConfig("aSchema").build
208 |
209 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build).addSchema(schemaConfig))
210 |
211 | mysqld must haveSchemaCharsetOf(UTF8MB4, schemaConfig.getName)
212 |
213 | mysqld.dropSchema(schemaConfig)
214 |
215 | mysqld must notHaveSchema(schemaConfig.getName)
216 | }
217 |
218 | "fail on dropping of non existing schema" in {
219 | val schemaConfig = aSchemaConfig("aSchema").build
220 |
221 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build))
222 |
223 | mysqld must notHaveSchema(schemaConfig.getName)
224 |
225 | mysqld.dropSchema(schemaConfig) must throwA[CommandFailedException]
226 | }
227 |
228 | "add schema after mysqld start" in {
229 | val schemaConfig = aSchemaConfig("aSchema").build
230 |
231 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build))
232 |
233 | mysqld must notHaveSchema(schemaConfig.getName)
234 |
235 | mysqld.addSchema(schemaConfig)
236 |
237 | mysqld must haveSchemaCharsetOf(UTF8MB4, schemaConfig.getName)
238 | }
239 |
240 | "fail on adding existing schema" in {
241 | val schemaConfig = aSchemaConfig("aSchema").build
242 |
243 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build).addSchema(schemaConfig))
244 |
245 | mysqld must haveSchemaCharsetOf(UTF8MB4, schemaConfig.getName)
246 |
247 | mysqld.addSchema(schemaConfig) must throwA[CommandFailedException]
248 | }
249 | }
250 |
251 | "EmbeddedMysql sql execution" should {
252 |
253 | "Execute arbitrary scripts without dropping existing data" in {
254 | val schemaName = "aSchema"
255 |
256 | val config = aSchemaConfig(schemaName)
257 | .withScripts(aMigrationWith("create table t1 (col1 INTEGER);"))
258 | .build
259 |
260 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build))
261 | .addSchema(config)
262 |
263 | def rowCountInTable() = aSelect[java.lang.Long](mysqld, onSchema = schemaName, sql = "select count(*) from t1;")
264 |
265 | rowCountInTable() mustEqual 0
266 |
267 | mysqld.executeScripts(schemaName, classPathScript("data/arbitrary_script.sql")).asScala must contain(exactly(""))
268 | rowCountInTable() mustEqual 1
269 |
270 | mysqld.executeScripts(schemaName, classPathScript("data/arbitrary_script.sql"))
271 | rowCountInTable() mustEqual 2
272 |
273 | mysqld.executeScripts(schemaName, classPathScript("data/arbitrary_script2.sql")).asScala must haveSize(1) and contain(contain("col1"))
274 |
275 | rowCountInTable() mustEqual 2
276 | }
277 | }
278 |
279 | def pathFor(basedir: String, subdir: String): String = {
280 | new File(basedir, subdir).getPath
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/ExtendedCharsetTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql
2 |
3 | import com.wix.mysql.EmbeddedMysql._
4 | import com.wix.mysql.ScriptResolver.classPathScript
5 | import com.wix.mysql.config.Charset
6 | import com.wix.mysql.config.MysqldConfig.SystemDefaults
7 | import com.wix.mysql.support.IntegrationTest
8 | import com.wix.mysql.support.IntegrationTest._
9 |
10 | class ExtendedCharsetTest extends IntegrationTest {
11 |
12 | "EmbeddedMysql instance" should {
13 |
14 | "support non-latin characters in login/password" in {
15 | skipped("not really supported for windows")
16 | // val config = testConfigBuilder
17 | // .withCharset(Charset.UTF8MB4)
18 | // .withUser("你", "好").build
19 | //
20 | // val mysqld = start(anEmbeddedMysql(config))
21 | //
22 | // mysqld must beAvailableOn(3310, "你", "好", SystemDefaults.SCHEMA)
23 | }
24 |
25 | "support migration from file with extended charset" in {
26 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().withCharset(Charset.UTF8MB4).build)
27 | .addSchema("aSchema", classPathScript("/db/004_update3.sql")))
28 |
29 | aSelect[java.lang.String](mysqld, onSchema = "aSchema", sql = "select col2 from t1 where col1 = 1;") mustEqual "你好!"
30 | }
31 |
32 | "support inline migration with extended charset" in {
33 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().withCharset(Charset.UTF8MB4).build)
34 | .addSchema(
35 | "aSchema",
36 | aMigrationWith("create table t1 (col1 INTEGER, col2 VARCHAR(10));\nINSERT INTO t1 values(1, '你好!');")))
37 |
38 | aSelect[java.lang.String](mysqld, onSchema = "aSchema", sql = "select col2 from t1 where col1 = 1;") mustEqual "你好!"
39 | }
40 |
41 | }
42 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/JavaUsageExamplesTest.java:
--------------------------------------------------------------------------------
1 | package com.wix.mysql;
2 |
3 | import com.wix.mysql.config.DownloadConfig;
4 | import com.wix.mysql.config.MysqldConfig;
5 | import com.wix.mysql.config.SchemaConfig;
6 | import org.junit.Ignore;
7 | import org.junit.Test;
8 |
9 | import java.util.concurrent.TimeUnit;
10 |
11 | import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql;
12 | import static com.wix.mysql.ScriptResolver.classPathScript;
13 | import static com.wix.mysql.ScriptResolver.classPathScripts;
14 | import static com.wix.mysql.config.Charset.LATIN1;
15 | import static com.wix.mysql.config.Charset.UTF8;
16 | import static com.wix.mysql.config.DownloadConfig.aDownloadConfig;
17 | import static com.wix.mysql.config.MysqldConfig.aMysqldConfig;
18 | import static com.wix.mysql.config.ProxyFactory.aHttpProxy;
19 | import static com.wix.mysql.config.SchemaConfig.aSchemaConfig;
20 | import static com.wix.mysql.distribution.Version.v5_6_latest;
21 | import static com.wix.mysql.distribution.Version.v5_7_latest;
22 |
23 | @Ignore
24 | public class JavaUsageExamplesTest {
25 |
26 | @Test
27 | public void defaultConfigurationAndASingleSchema() {
28 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest)
29 | .addSchema("aschema", classPathScript("db/001_init.sql"))
30 | .start();
31 |
32 | //do work
33 |
34 | mysqld.stop(); //optional, as there is a shutdown hook
35 | }
36 |
37 | @Test
38 | public void defaultConfigurationAndASingleSchemaWithMultipleMigrations() {
39 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest)
40 | .addSchema("aschema", classPathScripts("db/*.sql"))
41 | .start();
42 |
43 | //do work
44 |
45 | mysqld.stop(); //optional, as there is a shutdown hook
46 | }
47 |
48 | @Test
49 | public void mysqldConfigAndMultipleSchemas() {
50 | MysqldConfig config = aMysqldConfig(v5_7_latest)
51 | .withCharset(UTF8)
52 | .withPort(2215)
53 | .withUser("differentUser", "anotherPasword")
54 | .build();
55 |
56 | EmbeddedMysql mysqld = anEmbeddedMysql(config)
57 | .addSchema("aschema", classPathScript("db/001_init.sql"))
58 | .addSchema("aschema2", classPathScripts("db/*.sql"))
59 | .start();
60 |
61 | //do work
62 |
63 | mysqld.stop(); //optional, as there is a shutdown hook
64 | }
65 |
66 | @Test
67 | public void schemaConfigViaBuilder() {
68 | SchemaConfig schema = aSchemaConfig("aSchema")
69 | .withScripts(classPathScript("db/001_init.sql"))
70 | .withCharset(LATIN1)
71 | .build();
72 |
73 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest)
74 | .addSchema(schema)
75 | .addSchema("aschema2", classPathScripts("db/*.sql"))
76 | .start();
77 |
78 | //do work
79 |
80 | mysqld.stop(); //optional, as there is a shutdown hook
81 | }
82 |
83 | @Test
84 | public void schemaReset() {
85 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest)
86 | .addSchema("aschema", classPathScript("db/001_init.sql"))
87 | .start();
88 |
89 | //do work
90 |
91 | SchemaConfig schema = aSchemaConfig("aschema")
92 | .withScripts(classPathScript("db/001_init.sql"))
93 | .build();
94 | mysqld.reloadSchema(schema);
95 |
96 | //continue on doing work
97 |
98 | mysqld.stop(); //optional, as there is a shutdown hook
99 | }
100 |
101 | @Test
102 | public void customTimeout() {
103 | MysqldConfig config = aMysqldConfig(v5_6_latest)
104 | .withTimeout(2, TimeUnit.MINUTES)
105 | .build();
106 |
107 | EmbeddedMysql mysqld = anEmbeddedMysql(config)
108 | .addSchema("aschema", classPathScript("db/001_init.sql"))
109 | .start();
110 |
111 | //do work
112 |
113 | mysqld.stop(); //optional, as there is a shutdown hook
114 | }
115 |
116 | @Test
117 | public void customTempDir() {
118 | MysqldConfig config = aMysqldConfig(v5_6_latest)
119 | .withTempDir(System.getProperty("java.io.tmpdir"))
120 | .build();
121 |
122 | EmbeddedMysql mysqld = anEmbeddedMysql(config)
123 | .addSchema("aschema", classPathScript("db/001_init.sql"))
124 | .start();
125 |
126 | //do work
127 |
128 | mysqld.stop(); //optional, as there is a shutdown hook
129 | }
130 |
131 | @Test
132 | public void httpProxy() {
133 | DownloadConfig downloadConfig = aDownloadConfig()
134 | .withProxy(aHttpProxy("remote.host", 8080))
135 | .build();
136 |
137 | EmbeddedMysql mysqld = anEmbeddedMysql(v5_6_latest, downloadConfig)
138 | .addSchema("aschema", classPathScript("db/001_init.sql"))
139 | .start();
140 |
141 | //do work
142 |
143 | mysqld.stop(); //optional, as there is a shutdown hook
144 | }
145 |
146 |
147 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/MysqlTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql
2 |
3 | import com.wix.mysql.EmbeddedMysql.anEmbeddedMysql
4 | import com.wix.mysql.config.Charset
5 | import com.wix.mysql.exceptions.CommandFailedException
6 | import com.wix.mysql.support.IntegrationTest
7 | import com.wix.mysql.support.IntegrationTest.targetTestVersion
8 |
9 | class MysqlTest extends IntegrationTest {
10 |
11 | "mysql should emit exception info with message from 'mysql' command output'" in {
12 | val mysqld = start(anEmbeddedMysql(targetTestVersion))
13 | val mysql = new MysqlClient(mysqld.getConfig, mysqld.executable, "information_schema", Charset.UTF8MB4)
14 |
15 | mysql.executeCommands("sele qwe from zz;") must throwA[CommandFailedException].like {
16 | case e: CommandFailedException => e.getMessage must contain(
17 | "ERROR 1064 (42000) at line 1: You have an error in your SQL syntax;")
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/ParallelEmbeddedMysqlTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql
2 |
3 | import java.util.concurrent.TimeUnit
4 |
5 | import com.wix.mysql.EmbeddedMysql._
6 | import com.wix.mysql.config.MysqldConfig.SystemDefaults
7 | import com.wix.mysql.support.IntegrationTest
8 | import com.wix.mysql.support.IntegrationTest.testConfigBuilder
9 |
10 | import scala.concurrent.ExecutionContext.Implicits.global
11 | import scala.concurrent.duration._
12 | import scala.concurrent.{Await, Future}
13 |
14 | class ParallelEmbeddedMysqlTest extends IntegrationTest {
15 |
16 | "EmbeddedMysql instance" should {
17 |
18 | "run 2 instances in parallel" in {
19 | withCleanRepo {
20 | Seq(runMysql(onPort = 3310), runMysql(onPort = 3311)).map(Await.result(_, 15 minutes)) must beSuccessful
21 | }
22 | }
23 | }
24 |
25 | def runMysql(onPort: Int) = Future {
26 | val config = testConfigBuilder().withPort(onPort).withTimeout(2, TimeUnit.MINUTES).build
27 | val mysqld = start(anEmbeddedMysql(config))
28 |
29 | mysqld must beAvailableOn(onPort, "auser", "sa", SystemDefaults.SCHEMA)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/SchemaConfigTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql
2 |
3 | import java.io.File
4 |
5 | import com.wix.mysql.config.Charset.aCharset
6 | import com.wix.mysql.config.SchemaConfig.aSchemaConfig
7 | import org.specs2.mutable.SpecWithJUnit
8 |
9 | import scala.collection.convert.wrapAll._
10 |
11 | class SchemaConfigTest extends SpecWithJUnit {
12 |
13 | "SchemaConfig" should {
14 | "build with defaults" in {
15 | val schemaConfig = aSchemaConfig("aschema").build
16 |
17 | schemaConfig.getName mustEqual "aschema"
18 | schemaConfig.getCharset must beNull
19 | schemaConfig.getScripts must beEmpty
20 | }
21 |
22 | "build with custom charset" in {
23 | val charset = aCharset("charset", "collate")
24 |
25 | val schemaConfig = aSchemaConfig("aschema")
26 | .withCharset(charset)
27 | .build
28 |
29 | schemaConfig.getCharset mustEqual charset
30 | }
31 |
32 | "build with sources" in {
33 | val sources = Seq(Sources.fromFile(new File("/some")), Sources.fromFile(new File("/some/other")))
34 |
35 | val schemaConfig = aSchemaConfig("aschema")
36 | .withScripts(sources)
37 | .build
38 |
39 | schemaConfig.getScripts.toSeq mustEqual sources
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/ScriptResolverTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql
2 |
3 | import java.util
4 |
5 | import com.wix.mysql.ScriptResolver._
6 | import org.specs2.matcher.{FileMatchers, Matcher}
7 | import org.specs2.mutable.SpecificationWithJUnit
8 |
9 | import scala.collection.convert.decorateAsScala._
10 |
11 | class ScriptResolverTest extends SpecificationWithJUnit with FileMatchers {
12 | val contentsOf001Init = "create table t1 "
13 | val contentsOf002Update = "create table t2 "
14 | val contentsOf003Update = "create table t3 "
15 | val contentsOf004Update = "create table t1 "
16 | val contentsOf001InitInJar = "create table t1_jar"
17 | val contentsOf002UpdateInJar = "create table t2_jar"
18 |
19 | "classPathFile" should {
20 |
21 | "resolve a single classPath file" in {
22 | classPathFile("/db/001_init.sql") must beAScriptWith(contentsOf001Init)
23 | }
24 |
25 | "resolve a single classPath file without preceding '/'" in {
26 | classPathFile("db/001_init.sql") must beAScriptWith(contentsOf001Init)
27 | }
28 |
29 | "throw a ScriptResolutionException for a non-existent script" in {
30 | classPathFile("db/not-exists.sql") must throwA[ScriptResolutionException]
31 | }
32 | }
33 |
34 | "classPathFiles" should {
35 |
36 | "resolve multiple classPath files" in {
37 | classPathFiles("/db/*.sql") must containScripts(
38 | contentsOf001Init,
39 | contentsOf002Update,
40 | contentsOf003Update,
41 | contentsOf004Update)
42 | }
43 |
44 | "resolve multiple classPath files without preceding '/'" in {
45 | classPathFiles("db/*.sql") must containScripts(
46 | contentsOf001Init,
47 | contentsOf002Update,
48 | contentsOf003Update,
49 | contentsOf004Update)
50 | }
51 |
52 | "throw a ScriptResolutionException if no classPathFiles are found" in {
53 | classPathFiles("does-not-exist/*.sql") must throwA[ScriptResolutionException]
54 | }
55 | }
56 |
57 | "classPathScript" should {
58 |
59 | "resolve a single classPath file" in {
60 | classPathScript("/db/001_init.sql") must beAScriptWith(contentsOf001Init)
61 | }
62 |
63 | "resolve a single classPath file without preceding '/'" in {
64 | classPathScript("db/001_init.sql") must beAScriptWith(contentsOf001Init)
65 | }
66 |
67 | "resolve a single classPath file within packaged jar" in {
68 | classPathScript("/db-jar/001_jar-init.sql") must beAScriptWith(contentsOf001InitInJar)
69 | }
70 |
71 | "resolve a single classPath file within packaged jar without preceding '/'" in {
72 | classPathScript("db-jar/001_jar-init.sql") must beAScriptWith(contentsOf001InitInJar)
73 | }
74 |
75 | "throw a ScriptResolutionException for a non-existent script" in {
76 | classPathScript("db/not-exists.sql") must throwA[ScriptResolutionException]
77 | }
78 | }
79 |
80 | "classPathScripts" should {
81 |
82 | "resolve multiple classPath files" in {
83 | classPathScripts("/db/*.sql") must containScripts(
84 | contentsOf001Init,
85 | contentsOf002Update,
86 | contentsOf003Update,
87 | contentsOf004Update)
88 | }
89 |
90 | "resolve multiple classPath files without preceding '/'" in {
91 | classPathScripts("db/*.sql") must containScripts(
92 | contentsOf001Init,
93 | contentsOf002Update,
94 | contentsOf003Update,
95 | contentsOf004Update)
96 | }
97 |
98 | "resolve multiple classPath scripts within jar in classpath" in {
99 | classPathScripts("/db-jar/*.sql") must containScripts(contentsOf001InitInJar, contentsOf002UpdateInJar)
100 | }
101 |
102 | "resolve multiple classPath scripts within jar in classpath without preceding '/'" in {
103 | classPathScripts("/db-jar/*.sql") must containScripts(contentsOf001InitInJar, contentsOf002UpdateInJar)
104 | }
105 |
106 | "throw a ScriptResolutionException if no classPathFiles are found" in {
107 | classPathScripts("does-not-exist/*.sql") must throwA[ScriptResolutionException]
108 | }
109 | }
110 |
111 | def aScriptWith(fragment: String): Matcher[SqlScriptSource] =
112 | beAScriptWith(fragment)
113 |
114 | def beAScriptWith(fragment: String): Matcher[SqlScriptSource] =
115 | startWith(fragment) ^^ {
116 | (_: SqlScriptSource).read aka "script fragment mismatch"
117 | }
118 |
119 | def containScripts(fragments: String*): Matcher[util.List[SqlScriptSource]] =
120 | contain(exactly(fragments.map(aScriptWith): _*)).inOrder ^^ {
121 | (_: java.util.List[SqlScriptSource]).asScala
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/SupportedVersionsTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql
2 |
3 | import com.wix.mysql.EmbeddedMysql.anEmbeddedMysql
4 | import com.wix.mysql.config.MysqldConfig
5 | import com.wix.mysql.distribution.Version
6 | import com.wix.mysql.support.IntegrationTest
7 | import com.wix.mysql.support.IntegrationTest.testConfigBuilder
8 | import org.specs2.matcher.Scope
9 | import org.specs2.specification.core.Fragment
10 |
11 | class SupportedVersionsTest extends IntegrationTest {
12 |
13 | val versionsToTest: Seq[Version] = Seq(
14 | Version.v5_5_latest,
15 | Version.v5_6_latest,
16 | Version.v5_7_latest,
17 | Version.v8_0_11,
18 | Version.v8_latest) filter (_.supportsCurrentPlatform)
19 |
20 | trait Context extends Scope {
21 | val log: Iterable[String] = aLogFor("root")
22 | }
23 |
24 | Fragment.foreach(versionsToTest) { version =>
25 | s"$version should work on ${System.getProperty("os.name")}" in new Context {
26 | val config: MysqldConfig = testConfigBuilder(version).build
27 |
28 | val mysqld: EmbeddedMysql = start(anEmbeddedMysql(config).addSchema("aschema"))
29 |
30 | mysqld must beAvailableOn(config, "aschema")
31 |
32 | log must not(contain("Something bad happened."))
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/UtilsTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql
2 |
3 | import java.util.{Calendar, Date, SimpleTimeZone}
4 |
5 | import com.wix.mysql.utils.Utils
6 | import org.specs2.mutable.SpecWithJUnit
7 |
8 | class UtilsTest extends SpecWithJUnit {
9 | "Utils" should {
10 | "take daylight savings into account" in {
11 | val cal = Calendar.getInstance()
12 | val millisInHour = 60 * 60 * 1000
13 | // this fake tz should always be in daylight time:
14 | val tz = new SimpleTimeZone(millisInHour * -5, "EDT", Calendar.JANUARY, 1, 0, 0, Calendar.DECEMBER, 31, 0, millisInHour * 24 - 1, millisInHour)
15 | val offset = Utils.asHHmmOffset(tz)
16 | tz.observesDaylightTime() mustEqual true
17 | tz.inDaylightTime(new Date()) mustEqual true
18 | offset mustEqual "-04:00"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/config/DownloadConfigTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.config
2 |
3 | import java.io.File
4 | import java.nio.file.Files
5 |
6 | import com.wix.mysql.EmbeddedMysql.anEmbeddedMysql
7 | import com.wix.mysql.config.DownloadConfig.aDownloadConfig
8 | import com.wix.mysql.support.IntegrationTest.testConfigBuilder
9 | import com.wix.mysql.support.{IntegrationTest, MysqlCacheServingHttpServer}
10 | import de.flapdoodle.embed.process.exceptions.DistributionException
11 | import org.apache.commons.io.FileUtils.deleteDirectory
12 | import org.specs2.matcher.FileMatchers
13 | import org.specs2.mutable.BeforeAfter
14 |
15 | class DownloadConfigTest extends IntegrationTest with FileMatchers {
16 |
17 | "EmbeddedMysql download config" should {
18 |
19 | "store download cache in custom location" in {
20 | withTempDir { tempDir =>
21 | val defaultCachePath = aDownloadConfig().build().getCacheDir
22 | val downloadConfig = aDownloadConfig().withCacheDir(tempDir).build()
23 | val mysqld = start(anEmbeddedMysql(testConfigBuilder().build, downloadConfig))
24 |
25 | tempDir must not(beEqualToIgnoringSep(defaultCachePath))
26 | aPath(tempDir, "extracted") must beADirectory and exist
27 | }
28 | }
29 |
30 | "uses custom download base url" in {
31 | withTempDir { tempDir =>
32 | val downloadConfig = aDownloadConfig()
33 | .withCacheDir(tempDir)
34 | .withBaseUrl(s"http://localhost:2222")
35 | .build()
36 |
37 | start(anEmbeddedMysql(testConfigBuilder().build, downloadConfig)) must throwA[DistributionException].like {
38 | case e => e.getMessage must contain("Could not open inputStream for http://localhost:2222/MySQL-5.7")
39 | }
40 | }
41 | }
42 |
43 | "downloads via custom download base url" in new context {
44 | val mysqldConfig = testConfigBuilder().build
45 |
46 | ensureVersionPresentInCache(mysqldConfig)
47 |
48 | withTempDir { tempDir =>
49 | val downloadConfig = aDownloadConfig()
50 | .withDownloadCacheDir(tempDir)
51 | .withBaseUrl(s"http://localhost:${httpServer.port}")
52 | .build()
53 |
54 | start(anEmbeddedMysql(mysqldConfig, downloadConfig))
55 |
56 | aPath(tempDir, "extracted") must beADirectory and exist
57 | }
58 | }
59 |
60 | }
61 |
62 | class context extends BeforeAfter {
63 | val httpServer = new MysqlCacheServingHttpServer
64 |
65 | override def before: Any = {
66 | if (httpServer != null) {
67 | httpServer.start()
68 | }
69 | }
70 |
71 | override def after: Any = {
72 | if (httpServer != null) {
73 | httpServer.stop()
74 | }
75 | }
76 | }
77 |
78 | def aPath(basedir: String, subdir: String): File = {
79 | new File(basedir, subdir)
80 | }
81 |
82 | def withTempDir[T](f: String => T): T = {
83 | val tempDir = Files.createTempDirectory("embed-mysql-test").toFile
84 |
85 | try {
86 | f(tempDir.getAbsolutePath)
87 | } finally {
88 | deleteDirectory(tempDir)
89 | }
90 | }
91 |
92 | def ensureVersionPresentInCache(config: MysqldConfig): Unit = {
93 | anEmbeddedMysql(config).start().stop()
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/config/MysqldConfigTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.config
2 |
3 | import java.util.TimeZone
4 | import java.util.concurrent.TimeUnit
5 |
6 | import com.wix.mysql.config.Charset.{LATIN1, defaults}
7 | import com.wix.mysql.config.MysqldConfig.aMysqldConfig
8 | import com.wix.mysql.distribution.Version._
9 | import org.specs2.mutable.SpecWithJUnit
10 |
11 | import scala.collection.JavaConverters._
12 |
13 | class MysqldConfigTest extends SpecWithJUnit {
14 |
15 | "MysqldConfig" should {
16 |
17 | "build with defaults" in {
18 | val mysqldConfig = aMysqldConfig(v5_6_latest).build()
19 |
20 | mysqldConfig.getPort mustEqual 3310
21 | mysqldConfig.getVersion mustEqual v5_6_latest
22 | mysqldConfig.getCharset mustEqual defaults()
23 | mysqldConfig.getUsername mustEqual "auser"
24 | mysqldConfig.getPassword mustEqual "sa"
25 | mysqldConfig.getTimeZone mustEqual TimeZone.getTimeZone("UTC")
26 | mysqldConfig.getTimeout(TimeUnit.SECONDS) mustEqual 30
27 | }
28 |
29 | "accept custom port, user, charset, timezone" in {
30 | val mysqldConfig = aMysqldConfig(v5_6_latest)
31 | .withPort(1111)
32 | .withCharset(LATIN1)
33 | .withUser("otheruser", "otherpassword")
34 | .withTimeZone("Europe/Vilnius")
35 | .withTimeout(20, TimeUnit.SECONDS)
36 | .build()
37 |
38 | mysqldConfig.getPort mustEqual 1111
39 | mysqldConfig.getCharset mustEqual LATIN1
40 | mysqldConfig.getUsername mustEqual "otheruser"
41 | mysqldConfig.getPassword mustEqual "otherpassword"
42 | mysqldConfig.getTimeZone mustEqual TimeZone.getTimeZone("Europe/Vilnius")
43 | mysqldConfig.getTimeout(TimeUnit.MILLISECONDS) mustEqual 20000
44 | }
45 |
46 | "accept custom system variables" in {
47 | val mysqldConfig = aMysqldConfig(v5_6_latest)
48 | .withServerVariable("some-int", 123)
49 | .withServerVariable("some-string", "one")
50 | .withServerVariable("some-boolean", false)
51 | .build
52 |
53 | mysqldConfig.getServerVariables.asScala.map(_.toCommandLineArgument) mustEqual
54 | Seq("--some-int=123", "--some-string=one", "--some-boolean=false")
55 | }
56 |
57 | "accept free port" in {
58 | val mysqldConfig = aMysqldConfig(v5_6_latest)
59 | .withFreePort()
60 | .build()
61 |
62 | mysqldConfig.getPort mustNotEqual 3310
63 | }
64 |
65 | "fail if building with user 'root'" in {
66 | aMysqldConfig(v5_6_latest)
67 | .withUser("root", "doesnotmatter")
68 | .build() must throwA[IllegalArgumentException](message = "Usage of username 'root' is forbidden")
69 | }
70 |
71 | }
72 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/distribution/MacOsSierraTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution
2 |
3 | import java.lang.System.{getProperty, setProperty}
4 |
5 | import com.wix.mysql.exceptions.UnsupportedPlatformException
6 | import org.specs2.execute.{AsResult, Result}
7 | import org.specs2.mutable.{Around, SpecWithJUnit}
8 | import org.specs2.specification.AroundEach
9 | import org.specs2.specification.core.Fragment
10 |
11 | class MacOsSierraTest extends SpecWithJUnit with Around with AroundEach {
12 | sequential
13 |
14 | val unsupportedVersions: Array[Version] = Version.values filter (!_.supportsCurrentPlatform)
15 |
16 | Fragment.foreach(unsupportedVersions) { version =>
17 |
18 | s"$version should fail on Sierra with helpful message" in {
19 |
20 | version.asInDownloadPath() must throwAn[UnsupportedPlatformException](
21 | message = s"$version is not supported on Mac OS Sierra. Minimum supported version is 5.7.15"
22 | )
23 | }
24 | }
25 |
26 | def around[R: AsResult](r: => R): Result = {
27 | val currentOsName = getProperty("os.name")
28 | val currentOsVersion = getProperty("os.version")
29 |
30 | setProperty("os.name", "Mac OS X")
31 | setProperty("os.version", "10.12")
32 |
33 | try AsResult(r)
34 | finally {
35 | setProperty("os.name", currentOsName)
36 | setProperty("os.version", currentOsVersion)
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/distribution/VersionTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution
2 |
3 | import java.lang.System._
4 |
5 | import com.wix.mysql.distribution.Version._
6 | import com.wix.mysql.exceptions.UnsupportedPlatformException
7 | import de.flapdoodle.embed.process.distribution.Platform
8 | import de.flapdoodle.embed.process.distribution.Platform._
9 | import org.specs2.execute.{AsResult, Result}
10 | import org.specs2.mutable.SpecWithJUnit
11 | import org.specs2.specification.AroundEach
12 |
13 | class VersionTest extends SpecWithJUnit with AroundEach {
14 | sequential
15 |
16 | "platform detection should detect" >> {
17 | "OS X" in {
18 | givenPlatformSetTo(OS_X)
19 | v5_7_15.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.15-osx10.11"
20 | }
21 |
22 | "OS X 5.7.17+, 5.6.35+ and use different file scheme" in {
23 | givenPlatformSetTo(OS_X)
24 |
25 | v5_7_17.asInDownloadPath must contain( "macos" )
26 | v5_6_35.asInDownloadPath must contain( "macos" )
27 | }
28 |
29 |
30 | "Windows" in {
31 | givenPlatformSetTo(Windows)
32 | v5_7_15.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.15"
33 | }
34 |
35 | "Linux" in {
36 | givenPlatformSetTo(Linux)
37 | v5_7_15.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.15-linux-glibc2.5"
38 | v5_7_18.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.18-linux-glibc2.5"
39 | v5_7_19.asInDownloadPath mustEqual "/MySQL-5.7/mysql-5.7.19-linux-glibc2.12"
40 | }
41 |
42 | }
43 |
44 | "verify that" >> {
45 | "windows for 5.5.X is not supported" in {
46 | givenPlatformSetTo(Windows)
47 | v5_5_latest.asInDownloadPath must throwA[UnsupportedPlatformException]
48 | }
49 |
50 | "solaris is not supported" in {
51 | givenPlatformSetTo(Solaris)
52 | v5_5_latest.asInDownloadPath must throwA[UnsupportedPlatformException]
53 | }
54 |
55 | "freebsd is not supported" in {
56 | givenPlatformSetTo(FreeBSD)
57 | v5_5_latest.asInDownloadPath must throwA[UnsupportedPlatformException]
58 | }
59 | }
60 |
61 | def givenPlatformSetTo(platform: Platform): String = platform match {
62 | case Windows => setProperty("os.name", "Windows")
63 | case OS_X => setProperty("os.name", "Mac OS X")
64 | case Linux => setProperty("os.name", "Linux")
65 | case Solaris => setProperty("os.name", "SunOS")
66 | case FreeBSD => setProperty("os.name", "FreeBSD")
67 | case _ => throw new UnsupportedPlatformException("Unrecognized platform, currently not supported")
68 | }
69 |
70 | def around[R: AsResult](r: => R): Result = {
71 | val current = getProperty("os.name")
72 | try AsResult(r)
73 | finally setProperty("os.name", current)
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/distribution/service/ServiceCommandBuilderTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.distribution.service
2 |
3 | import org.specs2.mutable.SpecWithJUnit
4 |
5 | import scala.collection.JavaConverters._
6 | import scala.collection.convert.wrapAll._
7 |
8 | class ServiceCommandBuilderTest extends SpecWithJUnit {
9 | "ServiceCommandBuilder" should {
10 |
11 | "build a command" in {
12 | new ServiceCommandBuilder("v1")
13 | .addAll(Seq("one", "two"))
14 | .emit().asScala mustEqual Seq("one", "two")
15 | }
16 |
17 | "throw an exception if emitting empty command" in {
18 | new ServiceCommandBuilder("v1").emit() must
19 | throwA[RuntimeException].like { case e => e.getMessage must contain("was not populated for version: v1") }
20 | }
21 |
22 | "throw an exception if adding duplicate command" in {
23 | new ServiceCommandBuilder("v1")
24 | .addAll(Seq("--some", "--another=12"))
25 | .addAll(Seq("--some")) must
26 | throwA[RuntimeException].like { case e => e.getMessage must contain("argument with name '--some' is already present") }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/io/ProgressStepperTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.io
2 |
3 | import org.specs2.mutable.SpecWithJUnit
4 |
5 | class ProgressStepperTest extends SpecWithJUnit {
6 |
7 | "Progress Stepper setAndGet" should {
8 |
9 | "return 0 initially" in {
10 | new ProgressStepper().setAndGet(0) mustEqual 0
11 | }
12 |
13 | "return 5 for 5" in {
14 | new ProgressStepper().setAndGet(5) mustEqual 5
15 | }
16 |
17 | "return 5 for 9" in {
18 | new ProgressStepper().setAndGet(9) mustEqual 5
19 | }
20 |
21 | "return 10 for 10" in {
22 | new ProgressStepper().setAndGet(10) mustEqual 10
23 | }
24 | }
25 |
26 | "Progress Stepper hasNext" should {
27 |
28 | "is true for initial 0" in {
29 | new ProgressStepper().hasNext(0) must beTrue
30 | }
31 |
32 | "is false for already set value" in {
33 | val stepper = new ProgressStepper()
34 | stepper.setAndGet(0)
35 | stepper.hasNext(0) must beFalse
36 | }
37 |
38 | "is false for less than +5" in {
39 | val stepper = new ProgressStepper()
40 | stepper.setAndGet(0)
41 | stepper.hasNext(4) must beFalse
42 | }
43 |
44 | "is true for +5" in {
45 | val stepper = new ProgressStepper()
46 | stepper.setAndGet(0)
47 | stepper.hasNext(5) must beTrue
48 | }
49 |
50 | }
51 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/io/ResultMatchingListenerTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.io
2 |
3 | import com.wix.mysql.io.NotifyingStreamProcessor.ResultMatchingListener
4 | import org.specs2.mutable.SpecWithJUnit
5 |
6 | import scala.concurrent.ExecutionContext.Implicits.global
7 | import scala.concurrent.Future
8 |
9 | class ResultMatchingListenerTest extends SpecWithJUnit {
10 |
11 | "ResultMatchingListenerTest" should {
12 |
13 | "should return success given output matched provided pattern" in {
14 | val listener = new ResultMatchingListener("SUCCESS")
15 |
16 | Future {
17 | listener.waitForResult(4000)
18 | }
19 |
20 | listener.onMessage("SUCCESS: completed")
21 |
22 | listener.isInitWithSuccess must beTrue
23 | listener.getFailureFound must beNull
24 | }
25 |
26 | "throw a timeout exception if command does not complete within provided timeout" in {
27 | new ResultMatchingListener("SUCCESS").waitForResult(1000) must
28 | throwA[RuntimeException].like { case e => e.getMessage must contain("Timeout of 1 sec exceeded") }
29 | }
30 |
31 | "should return error output given error expression matched" in {
32 | val listener = new ResultMatchingListener("SUCCESS")
33 |
34 | Future {
35 | Thread.sleep(500)
36 | listener.onMessage("[ERROR] woops")
37 | }
38 |
39 | listener.waitForResult(5000)
40 |
41 | listener.isInitWithSuccess must beFalse
42 | listener.getFailureFound mustEqual "[ERROR] woops"
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/io/TimingOutProcessExecutorTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.io
2 |
3 | import java.io.{InputStream, OutputStream, StringReader}
4 | import java.util.concurrent.TimeUnit
5 | import java.util.concurrent.atomic.AtomicInteger
6 |
7 | import org.specs2.mutable.SpecWithJUnit
8 |
9 | class TimingOutProcessExecutorTest extends SpecWithJUnit {
10 |
11 | "TimingOutProcessExecutor" should {
12 |
13 | "throw an exception if command does not complete within provided timeout" in {
14 | new TimingOutProcessExecutor("cmd").waitFor(new FakeProcess(Integer.MAX_VALUE), TimeUnit.MILLISECONDS.toNanos(1000)) must
15 | throwA[InterruptedException].like { case e => e.getMessage must contain("Timeout of 1 sec exceeded while waiting for 'cmd'")}
16 | }
17 |
18 | "return process exit code if command does complete within execution bounds" in {
19 | new TimingOutProcessExecutor("").waitFor(new FakeProcess(3), TimeUnit.MILLISECONDS.toNanos(2000)) mustEqual 0
20 | }
21 | }
22 | }
23 |
24 | class FakeProcess(val completeAfterNumberOfCalls: Int) extends Process {
25 | val exitValueInvoctionCounter = new AtomicInteger(completeAfterNumberOfCalls)
26 |
27 | override def exitValue(): Int = {
28 | exitValueInvoctionCounter.decrementAndGet() match {
29 | case 0 => 0
30 | case _ => throw new IllegalThreadStateException()
31 | }
32 | }
33 |
34 | override def destroy(): Unit = {}
35 |
36 | override def waitFor(): Int = ???
37 |
38 | override def getOutputStream: OutputStream = ???
39 |
40 | override def getErrorStream: InputStream = new FakeInputStream("err")
41 |
42 | override def getInputStream: InputStream = new FakeInputStream("err")
43 | }
44 |
45 | class FakeInputStream(collectedOutput: String) extends InputStream {
46 | val output = new StringReader(collectedOutput)
47 | override def read(): Int = output.read()
48 | }
49 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/support/HttpProxyServerSupport.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.support
2 |
3 | import java.io.{File, FileInputStream}
4 | import java.nio.file.Files
5 | import java.util.UUID
6 |
7 | import com.wix.mysql.PackagePaths
8 | import com.wix.mysql.distribution.Version
9 | import de.flapdoodle.embed.process.distribution.Distribution
10 | import de.flapdoodle.embed.process.io.directories.UserHome
11 | import fi.iki.elonen.NanoHTTPD
12 | import org.littleshoot.proxy.impl.DefaultHttpProxyServer
13 | import org.littleshoot.proxy.{ActivityTrackerAdapter, FlowContext, HttpProxyServer}
14 |
15 | trait HttpProxyServerSupport {
16 |
17 | def withProxyOn[T](proxyPort: Int, targetPort: Int, servedVersion: Version)(f: (ConnectedActivityTracker, Int, Int, Version) => T): T = {
18 | val tracker = new ConnectedActivityTracker()
19 | val proxyBootstrap = DefaultHttpProxyServer
20 | .bootstrap()
21 | .plusActivityTracker(tracker)
22 | .withPort(proxyPort)
23 |
24 | var proxyServer: Option[HttpProxyServer] = None
25 | var staticsServer: Option[StaticsServer] = None
26 | val (fileToProxy, restoreFiles) = ProxyFiles(servedVersion)
27 | try {
28 | staticsServer = new StaticsServer(targetPort, fileToProxy).doStart()
29 | proxyServer = Some(proxyBootstrap.start())
30 |
31 | f(tracker, proxyPort, targetPort, servedVersion)
32 | } finally {
33 | proxyServer.foreach(_.stop())
34 | staticsServer.foreach(_.stop())
35 | restoreFiles()
36 | }
37 | }
38 | }
39 |
40 | class ConnectedActivityTracker extends ActivityTrackerAdapter {
41 | val fiftyMegabytesInBytes = 50000000
42 | var bytesSent: Int = 0
43 |
44 | override def bytesSentToClient(flowContext: FlowContext, numberOfBytes: Int): Unit = {
45 | bytesSent += numberOfBytes
46 | }
47 |
48 | def wasDownloaded: Boolean = bytesSent > fiftyMegabytesInBytes
49 | }
50 |
51 | class StaticsServer(port: Int, fileToServe: File) extends NanoHTTPD(port) {
52 |
53 | def doStart(): Option[StaticsServer] = {
54 | start()
55 | Some(this)
56 | }
57 |
58 | override def serve(session: NanoHTTPD.IHTTPSession): NanoHTTPD.Response = {
59 | NanoHTTPD.newChunkedResponse(NanoHTTPD.Response.Status.ACCEPTED, "application/tar+gzip", new FileInputStream(fileToServe))
60 | }
61 | }
62 |
63 | object ProxyFiles {
64 | val tmpDir: File = new UserHome(".embedmysql").asFile()
65 | type Restore = (File, () => Unit)
66 |
67 | def apply(version: Version): Restore = {
68 | val source = new File(tmpDir, new PackagePaths().getPath(Distribution.detectFor(version)))
69 | val target = new File(tmpDir, UUID.randomUUID().toString)
70 | if (!target.exists()) {
71 | Files.copy(source.toPath, target.toPath)
72 | }
73 | Files.delete(source.toPath)
74 |
75 | (target, () => {
76 | if (!source.exists()) {
77 | Files.copy(target.toPath, source.toPath)
78 | }
79 | Files.delete(target.toPath)
80 | })
81 |
82 | }
83 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/support/InstanceMatchers.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.support
2 |
3 | import java.util.TimeZone
4 |
5 | import com.wix.mysql.EmbeddedMysql
6 | import com.wix.mysql.config.Charset._
7 | import com.wix.mysql.config.MysqldConfig.SystemDefaults
8 | import com.wix.mysql.config.{Charset, MysqldConfig}
9 | import com.wix.mysql.utils.Utils
10 | import org.specs2.matcher.{Matcher, Matchers}
11 |
12 | trait InstanceMatchers extends Matchers {
13 | self: IntegrationTest =>
14 |
15 | def haveSystemVariable(name: String, value: Matcher[String]): Matcher[EmbeddedMysql] =
16 | value ^^ { mySql: EmbeddedMysql =>
17 | val ds = aDataSource(mySql.getConfig, SystemDefaults.SCHEMA)
18 | aSelect[String](ds, sql = s"SELECT variable_value FROM information_schema.global_variables WHERE variable_name = '$name';")
19 | }
20 |
21 | def haveCharsetOf(charset: Charset): Matcher[EmbeddedMysql] =
22 | ===(charset) ^^ { mySql: EmbeddedMysql =>
23 | val ds = aDataSource(mySql.getConfig, SystemDefaults.SCHEMA)
24 | aCharset(
25 | aSelect[String](ds, sql = "SELECT variable_value FROM information_schema.global_variables WHERE variable_name = 'character_set_server';"),
26 | aSelect[String](ds, sql = "SELECT variable_value FROM information_schema.global_variables WHERE variable_name = 'collation_server';"))
27 | }
28 |
29 | def haveSchemaCharsetOf(charset: Charset, onSchema: String): Matcher[EmbeddedMysql] =
30 | ===(charset) ^^ { mySql: EmbeddedMysql =>
31 | val ds = aDataSource(mySql.getConfig, onSchema)
32 | aCharset(
33 | aSelect[String](ds, sql = s"SELECT default_character_set_name FROM information_schema.SCHEMATA where SCHEMA_NAME = '$onSchema';"),
34 | aSelect[String](ds, sql = s"SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA where SCHEMA_NAME = '$onSchema';"))
35 | }
36 |
37 | def notHaveSchema(onSchema: String): Matcher[EmbeddedMysql] =
38 | ===(0) ^^ { mySql: EmbeddedMysql =>
39 | val ds = aDataSource(mySql.getConfig, "information_schema")
40 | aSelect[java.lang.Integer](ds, sql = s"SELECT COUNT(1) FROM information_schema.SCHEMATA where SCHEMA_NAME = '$onSchema';").toInt
41 | }
42 |
43 | def haveServerTimezoneMatching(timeZoneId: String): Matcher[EmbeddedMysql] =
44 | ===(Utils.asHHmmOffset(TimeZone.getTimeZone(timeZoneId))) ^^ { mySql: EmbeddedMysql =>
45 | val ds = aDataSource(mySql.getConfig, SystemDefaults.SCHEMA)
46 | aSelect[String](ds, sql = s"SELECT @@global.time_zone;")
47 | }
48 |
49 | def beAvailableOn(port: Int, withUser: String, password: String, andSchema: String): Matcher[EmbeddedMysql] =
50 | beTrue ^^ { mySql: EmbeddedMysql =>
51 | aSelect[java.lang.Long](aDataSource(withUser, password, port, andSchema), "select 1;") == 1
52 | }
53 |
54 | def beAvailableOn(config: MysqldConfig, andSchema: String): Matcher[EmbeddedMysql] =
55 | beAvailableOn(config.getPort, config.getUsername, config.getPassword, andSchema)
56 | }
57 |
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/support/IntegrationTest.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.support
2 |
3 | import java.util.UUID
4 | import java.util.concurrent.TimeUnit
5 |
6 | import ch.qos.logback.classic.Level.INFO
7 | import ch.qos.logback.classic.spi.ILoggingEvent
8 | import ch.qos.logback.classic.{Logger, LoggerContext}
9 | import ch.qos.logback.core.read.ListAppender
10 | import com.wix.mysql.config.MysqldConfig
11 | import com.wix.mysql.distribution.Version
12 | import com.wix.mysql.{EmbeddedMysql, Sources, SqlScriptSource}
13 | import de.flapdoodle.embed.process.io.directories.UserHome
14 | import javax.sql.DataSource
15 | import org.apache.commons.dbcp2.BasicDataSource
16 | import org.apache.commons.io.FileUtils._
17 | import org.slf4j
18 | import org.slf4j.LoggerFactory
19 | import org.slf4j.LoggerFactory.getLogger
20 | import org.specs2.mutable.SpecWithJUnit
21 | import org.specs2.specification.BeforeAfterEach
22 | import org.springframework.dao.DataAccessException
23 | import org.springframework.jdbc.core.JdbcTemplate
24 |
25 | import scala.collection.JavaConversions._
26 | import scala.reflect._
27 |
28 | abstract class IntegrationTest extends SpecWithJUnit with BeforeAfterEach
29 | with InstanceMatchers with TestResourceSupport with JdbcSupport {
30 |
31 | sequential
32 |
33 | var mysqldInstances: Seq[EmbeddedMysql] = Seq()
34 | val log: slf4j.Logger = getLogger(this.getClass)
35 |
36 | def before: Any = mysqldInstances = Seq()
37 |
38 | def after: Any = mysqldInstances.foreach(_.stop)
39 |
40 | def start(mysqld: EmbeddedMysql.Builder): EmbeddedMysql = {
41 | val instance = mysqld.start
42 | mysqldInstances = mysqldInstances :+ instance
43 | instance
44 | }
45 |
46 |
47 | def aLogFor(app: String): Iterable[String] = {
48 | val appender: ListAppender[ILoggingEvent] = new ListAppender[ILoggingEvent]
49 | val context: LoggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]
50 |
51 | val logger: Logger = context.getLogger(app)
52 |
53 | logger.setAdditive(false)
54 | logger.setLevel(INFO)
55 | logger.detachAppender(appender.getName)
56 | logger.addAppender(appender)
57 |
58 | appender.start()
59 |
60 | new Iterable[String] {
61 | def iterator = appender.list map (_.getMessage) iterator
62 | }
63 | }
64 |
65 | def withCleanRepo[T](f: => T): T = {
66 | val repository = new UserHome(".embedmysql").asFile
67 | val backupFolder = new UserHome(s".embedmysql-${UUID.randomUUID()}").asFile
68 |
69 | if (!repository.exists()) {
70 | f
71 | } else {
72 | moveDirectory(repository, backupFolder)
73 | try {
74 | f
75 | } finally {
76 | deleteDirectory(repository)
77 | moveDirectory(backupFolder, repository)
78 | }
79 | }
80 | }
81 | }
82 |
83 | object IntegrationTest {
84 | val targetTestVersion: Version = Version.v5_7_latest
85 |
86 | def testConfigBuilder(version: Version = targetTestVersion): MysqldConfig.Builder = MysqldConfig
87 | .aMysqldConfig(version)
88 | .withTimeout(60, TimeUnit.SECONDS)
89 | }
90 |
91 | trait JdbcSupport {
92 | self: IntegrationTest =>
93 |
94 | def aDataSource(config: MysqldConfig, schema: String): DataSource =
95 | aDataSource(config.getUsername, config.getPassword, config.getPort, schema)
96 |
97 | def aDataSource(user: String, password: String, port: Int, schema: String): DataSource = {
98 | val dataSource: BasicDataSource = new BasicDataSource
99 | dataSource.setDriverClassName("com.mysql.jdbc.Driver")
100 | dataSource.setUrl(s"jdbc:mysql://localhost:$port/$schema?useSSL=false")
101 | dataSource.setUsername(user)
102 | dataSource.setPassword(password)
103 | dataSource
104 | }
105 |
106 | def aJdbcTemplate(mysqld: EmbeddedMysql, forSchema: String): JdbcTemplate =
107 | new JdbcTemplate(aDataSource(mysqld.getConfig, forSchema))
108 |
109 | def aSelect[T: ClassTag](ds: DataSource, sql: String): T =
110 | new JdbcTemplate(ds).queryForObject(sql, classTag[T].runtimeClass.asInstanceOf[Class[T]])
111 |
112 | def aSelect[T: ClassTag](mysqld: EmbeddedMysql, onSchema: String, sql: String): T =
113 | aJdbcTemplate(mysqld, onSchema).queryForObject(sql, classTag[T].runtimeClass.asInstanceOf[Class[T]])
114 |
115 | def aQuery(mysqld: EmbeddedMysql, onSchema: String, sql: String): Unit =
116 | aJdbcTemplate(mysqld, forSchema = onSchema).execute(sql)
117 |
118 | def anUpdate(mysqld: EmbeddedMysql, onSchema: String, sql: String): Unit =
119 | aJdbcTemplate(mysqld, forSchema = onSchema).execute(sql)
120 |
121 | def aMigrationWith(sql: String): SqlScriptSource = Sources.fromFile(createTempFile(sql))
122 |
123 | def beSuccessful = not(throwAn[Exception])
124 |
125 | def failWith(fragment: String) = throwA[DataAccessException].like { case e =>
126 | e.getMessage must contain(fragment)
127 | }
128 |
129 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/support/MysqlCacheServingHttpServer.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.support
2 |
3 | import java.io.{BufferedInputStream, File}
4 |
5 | import com.wix.mysql.support.MysqlCacheServingHttpServer.PORT
6 | import fi.iki.elonen.router.RouterNanoHTTPD
7 |
8 | class MysqlCacheServingHttpServer extends RouterNanoHTTPD(PORT) {
9 | val port = PORT
10 |
11 | val embeddedMysqlCacheDir: File = new File(System.getProperty("user.home"), ".embedmysql").getAbsoluteFile
12 | addRoute("/(.)+", classOf[StaticPageTestHandler], embeddedMysqlCacheDir)
13 | }
14 |
15 | class StaticPageTestHandler extends RouterNanoHTTPD.StaticPageHandler {
16 | protected override def fileToInputStream(fileOrdirectory: File): BufferedInputStream = {
17 | super.fileToInputStream(fileOrdirectory)
18 | }
19 | }
20 |
21 | object MysqlCacheServingHttpServer {
22 | val PORT = 3000
23 | }
--------------------------------------------------------------------------------
/wix-embedded-mysql/src/test/scala/com/wix/mysql/support/TestResourceSupport.scala:
--------------------------------------------------------------------------------
1 | package com.wix.mysql.support
2 |
3 | import java.io.{File => JFile}
4 | import java.nio.charset.Charset
5 | import java.util.UUID
6 |
7 | import org.apache.commons.io.FileUtils
8 |
9 | import scala.reflect.io.File
10 |
11 | trait TestResourceSupport {
12 |
13 | def createTempFile(content: String): JFile = {
14 | testResourceDirExists()
15 | val file = new JFile(s"$targetDir/classes/${UUID.randomUUID}")
16 | FileUtils.writeStringToFile(file, content, Charset.forName("UTF-8"))
17 | file
18 | }
19 |
20 | private def testResourceDirExists() {
21 | val f = File(s"$targetDir/classes")
22 | if (!f.exists || f.isFile)
23 | throw new RuntimeException("Could not generate file in test resources")
24 | }
25 |
26 | private val targetDir = {
27 | val target = new JFile(getClass.getProtectionDomain.getCodeSource.getLocation.getFile).getParentFile
28 | if (!File(s"$target/classes").exists) new JFile("target") else target
29 | }
30 | }
--------------------------------------------------------------------------------