dply 0.2.19 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +5 -5
  2. data/.rspec +4 -0
  3. data/Rakefile +0 -14
  4. data/TODO +0 -1
  5. data/code_dump/old_remote_task.rb +2 -0
  6. data/{dev_bin → dev_exe}/dplyr +0 -0
  7. data/{dev_bin → dev_exe}/drake +1 -1
  8. data/dply.gemspec +2 -2
  9. data/{bin → exe}/dplyr +0 -0
  10. data/{bin → exe}/drake +12 -14
  11. data/lib/dply/TEST_TODO +50 -0
  12. data/lib/dply/app_config.rb +108 -0
  13. data/lib/dply/base_config.rb +110 -0
  14. data/lib/dply/build.rb +17 -11
  15. data/lib/dply/build_config.rb +28 -96
  16. data/lib/dply/bundle.rb +7 -30
  17. data/lib/dply/cli/build.rb +5 -12
  18. data/lib/dply/cli/ctl.rb +7 -8
  19. data/lib/dply/cli/deploy.rb +6 -10
  20. data/lib/dply/cli/devbuild.rb +6 -10
  21. data/lib/dply/cli/install_pkgs.rb +2 -3
  22. data/lib/dply/cli/run.rb +27 -0
  23. data/lib/dply/cli/status.rb +1 -2
  24. data/lib/dply/cli/task.rb +6 -12
  25. data/lib/dply/code_archive.rb +123 -0
  26. data/lib/dply/command.rb +57 -0
  27. data/lib/dply/config_downloader.rb +3 -2
  28. data/lib/dply/curl.rb +1 -5
  29. data/lib/dply/custom_logger.rb +18 -1
  30. data/lib/dply/deplist.rb +16 -48
  31. data/lib/dply/deploy_config.rb +34 -0
  32. data/lib/dply/elf.rb +60 -0
  33. data/lib/dply/env.rb +9 -0
  34. data/lib/dply/git.rb +15 -8
  35. data/lib/dply/helper.rb +21 -33
  36. data/lib/dply/linker.rb +27 -27
  37. data/lib/dply/lock.rb +2 -9
  38. data/lib/dply/logger.rb +1 -1
  39. data/lib/dply/pkgs.rb +9 -11
  40. data/lib/dply/release.rb +2 -2
  41. data/lib/dply/{archive.rb → remote_archive.rb} +1 -1
  42. data/lib/dply/repo.rb +3 -3
  43. data/lib/dply/rpm.rb +12 -20
  44. data/lib/dply/scripts/depcheck.rb +4 -0
  45. data/lib/dply/shared_dirs.rb +1 -1
  46. data/lib/dply/strategy/archive.rb +15 -22
  47. data/lib/dply/strategy/base.rb +82 -0
  48. data/lib/dply/strategy/git.rb +18 -19
  49. data/lib/dply/task_dsl.rb +101 -0
  50. data/lib/dply/util.rb +75 -0
  51. data/lib/dply/venv.rb +53 -0
  52. data/lib/dply/version.rb +1 -1
  53. data/lib/dply/yum.rb +21 -31
  54. data/lib/dplyr/consul.rb +1 -1
  55. data/spec/dply/base_config_spec.rb +178 -0
  56. data/spec/dply/bundle_spec.rb +100 -0
  57. data/spec/dply/command_spec.rb +190 -0
  58. data/spec/dply/curl_spec.rb +41 -0
  59. data/spec/dply/deplist_spec.rb +48 -0
  60. data/spec/dply/elf_spec.rb +64 -0
  61. data/spec/dply/env_spec.rb +57 -0
  62. data/spec/dply/git_spec.rb +136 -0
  63. data/spec/dply/helper_spec.rb +168 -0
  64. data/spec/dply/linker_spec.rb +81 -0
  65. data/spec/dply/lock_spec.rb +24 -0
  66. data/spec/dply/pkgs_spec.rb +105 -0
  67. data/spec/dply/repo_spec.rb +58 -0
  68. data/spec/dply/rpm_spec.rb +32 -0
  69. data/spec/dply/yum_spec.rb +29 -0
  70. data/spec/integration/archive_flow_spec.rb +87 -0
  71. data/spec/integration/git_flow_spec.rb +63 -0
  72. data/spec/repo.rb +27 -0
  73. data/spec/spec_helper.rb +44 -0
  74. data/spec/test_data/build.tar.gz +0 -0
  75. data/spec/test_data/build.tar.gz.md5 +1 -0
  76. data/spec/test_data/bundle/gems_installed/Gemfile +1 -0
  77. data/spec/test_data/bundle/gems_not_installed/Gemfile +2 -0
  78. data/spec/test_data/bundle/no_gemfile/.gitkeep +0 -0
  79. data/spec/test_data/command/test.rb +7 -0
  80. data/spec/test_data/elf/elf +0 -0
  81. data/spec/test_data/elf/libpgtypes.so.3 +0 -0
  82. data/spec/test_data/elf/not_elf +1 -0
  83. data/spec/test_data/sample_repo/.dply.lock +0 -0
  84. data/spec/test_data/sample_repo/Gemfile +2 -0
  85. data/spec/test_data/sample_repo/Rakefile +3 -0
  86. data/spec/test_data/sample_repo/app.rb +1 -0
  87. data/spec/test_data/sample_repo/dply/app.rb +33 -0
  88. data/spec/test_data/sample_repo/lib/libacl.so.1 +0 -0
  89. data/spec/test_data/sample_repo/pkgs.yml +2 -0
  90. data/spec/webserver.rb +21 -0
  91. metadata +96 -28
  92. data/lib/dply/cli/app_task.rb +0 -38
  93. data/lib/dply/config.rb +0 -120
  94. data/lib/dply/config_struct.rb +0 -52
  95. data/lib/dply/rakelib/drake.rake +0 -33
  96. data/lib/dply/tasks.rb +0 -136
@@ -0,0 +1,41 @@
1
+ require 'dply/curl'
2
+
3
+ module Dply
4
+ describe Curl do
5
+
6
+ let(:url) { "http://127.0.0.1:8000/build.tar.gz" }
7
+
8
+ describe "#download(url, file)" do
9
+ it "downloads the url to file" do
10
+ Dir.mktmpdir do |dir|
11
+ f = "#{dir}/f"
12
+ curl = Curl.new
13
+ expect(Logger.logger).to receive(:bullet).with("downloading #{url}")
14
+ curl.download(url, f)
15
+ expect(File).to exist(f)
16
+ end
17
+ end
18
+
19
+ it "raises error when url doesn't exist" do
20
+ url = "http://127.0.0.1:8000/build"
21
+ Dir.mktmpdir do |dir|
22
+ f = "#{dir}/f"
23
+ curl = Curl.new
24
+ expect(Logger.logger).to receive(:bullet).with("downloading #{url}")
25
+ expect { curl.download(url, f) }.to raise_error(Error)
26
+ end
27
+ end
28
+
29
+ it "logs message to debug when quiet: true" do
30
+ Dir.mktmpdir do |dir|
31
+ f = "#{dir}/f"
32
+ curl = Curl.new(quiet: true)
33
+ expect(Logger.logger).to receive(:debug).with("downloading #{url}")
34
+ curl.download(url, f)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+
@@ -0,0 +1,48 @@
1
+ silence_warnings do
2
+ require 'dply/deplist'
3
+ end
4
+
5
+ module Dply
6
+ describe Deplist do
7
+
8
+ before :all do
9
+ @work_dir = "tmp/deplist"
10
+ FileUtils.rm_rf @work_dir
11
+ FileUtils.mkdir_p @work_dir
12
+ end
13
+
14
+ def tmp_dir(&block)
15
+ d = Dir.mktmpdir nil, "#{Dir.pwd}/#{@work_dir}"
16
+ FileUtils.cp "spec/test_data/sample_repo/lib/libacl.so.1", "#{d}/libacl.so.1"
17
+ Dir.chdir(d) { yield d }
18
+ end
19
+
20
+ describe "#verify!" do
21
+
22
+ it "doesn't throw an error when pkgs.yml has the required pkgs" do
23
+ pkgs_data = { "pkgs" => ["libattr"], "build_pkgs" => [] }
24
+ tmp_dir do
25
+ File.write "pkgs.yml", YAML.dump(pkgs_data)
26
+ system! "tar czf test.tar.gz pkgs.yml libacl.so.1"
27
+ deplist = Deplist.new("test.tar.gz")
28
+ silence_warnings do
29
+ expect { deplist.verify! }.to output(/all dependencies satisfied/).to_stdout_from_any_process
30
+ end
31
+ end
32
+ end
33
+
34
+ it "throws an error when pkgs.yml doesn't have the required pkgs" do
35
+ pkgs_data = { "pkgs" => [], "build_pkgs" => [] }
36
+ tmp_dir do
37
+ File.write "pkgs.yml", YAML.dump(pkgs_data)
38
+ system! "tar czf test.tar.gz pkgs.yml libacl.so.1"
39
+ deplist = Deplist.new("test.tar.gz")
40
+ silence_warnings do
41
+ expect { deplist.verify! }.to raise_error(Error).and output(/any of \["libattr"\]/).to_stdout_from_any_process
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,64 @@
1
+ silence_warnings do
2
+ require 'elf'
3
+ end
4
+ require 'dply/elf'
5
+ require 'pathname'
6
+
7
+ module Dply
8
+ describe Elf do
9
+
10
+ before :all do
11
+ @verbose = $VERBOSE
12
+ $VERBOSE = false
13
+ end
14
+
15
+ let(:work_dir) { Pathname.new("#{__dir__}/../test_data/elf").realpath }
16
+
17
+ def map_64bit(list)
18
+ list.map { |i| "#{i}()(64bit)" }
19
+ end
20
+
21
+ describe ".elf?" do
22
+ it "returns true if file is an ELF" do
23
+ expect(Elf.elf? "#{work_dir}/elf").to eq(true)
24
+ end
25
+
26
+ it "returns false if file is not an ELF" do
27
+ expect(Elf.elf? "#{work_dir}/not_elf").to eq(false)
28
+ end
29
+
30
+ it "returns false if file doesn't exist" do
31
+ expect(Elf.elf? "#{work_dir}/nofile").to eq(false)
32
+ end
33
+ end
34
+
35
+ describe "#external_libs" do
36
+ it "returns a list of external libs when elf has no rpath section" do
37
+ libs = map_64bit ["libattr.so.1", "libc.so.6"]
38
+ elf = Elf.new("#{work_dir}/elf")
39
+ expect(elf.external_libs).to match_array(libs)
40
+ end
41
+
42
+ context "when elf has absolute rpath or runpath" do
43
+ it "returns all external libs ignoring rpath" do
44
+ libs = map_64bit ["libpq.so.5", "libc.so.6", "libpgtypes.so.3", "libpthread.so.0"]
45
+ elf = Elf.new("#{work_dir}/libecpg.so")
46
+ expect(elf.external_libs).to match_array(libs)
47
+ end
48
+ end
49
+
50
+ context "when elf contains a relative rpath or runpath" do
51
+ it "returns all external libs excluding the ones found in relative rpath" do
52
+ libs = map_64bit ["libpq.so.5", "libc.so.6", "libpthread.so.0"]
53
+ elf = Elf.new("#{work_dir}/rpath_libecpg.so")
54
+ expect(elf.external_libs).to match_array(libs)
55
+ end
56
+ end
57
+ end
58
+
59
+ after :all do
60
+ $VERBOSE = @verbose
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,57 @@
1
+ require 'dply/env'
2
+
3
+ module Dply
4
+ describe Env do
5
+
6
+ ORIGINAL_ENV = { "O1" => "1", "O2" => "2" }
7
+ CURRENT_ENV = { "C1" => "1", "C2" => "2" }
8
+ EXTRA_ENV = { "O1" => "E", "C1" => "E" }
9
+
10
+ def stub_env(with_bundler:)
11
+ allow(ENV).to receive(:to_h).and_return(CURRENT_ENV)
12
+ if with_bundler
13
+ stub_const("Bundler", "bundler")
14
+ allow(Bundler).to receive(:original_env).and_return(ORIGINAL_ENV)
15
+ else
16
+ hide_const("Bundler")
17
+ end
18
+ end
19
+
20
+ describe '.build_env (extra_env, bundled_env:)' do
21
+ context "when bundler is defined and bundled_env: true" do
22
+ it "merges current_env with extra_env" do
23
+ stub_env with_bundler: true
24
+ env = Env.build_env(EXTRA_ENV, bundled_env: true)
25
+ expect(env).to eq(CURRENT_ENV.merge(EXTRA_ENV))
26
+ end
27
+ end
28
+
29
+ context "when bundler is defined and bundled_env: false" do
30
+ it "merges original_env with extra_env" do
31
+ stub_env with_bundler: true
32
+ env = Env.build_env(EXTRA_ENV, bundled_env: false)
33
+ expect(env).to eq(ORIGINAL_ENV.merge(EXTRA_ENV))
34
+ end
35
+ end
36
+
37
+ context "when bundler is not defined and bundled_env: true" do
38
+ it "merges current_env with extra_env" do
39
+ stub_env with_bundler: false
40
+ env = Env.build_env(EXTRA_ENV, bundled_env: true)
41
+ expect(env).to eq(CURRENT_ENV.merge(EXTRA_ENV))
42
+ end
43
+ end
44
+
45
+ context "when bundler is defined and bundled_env: false" do
46
+ it "merges current_env with extra_env" do
47
+ stub_env with_bundler: false
48
+ env = Env.build_env(EXTRA_ENV, bundled_env: true)
49
+ expect(env).to eq(CURRENT_ENV.merge(EXTRA_ENV))
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
57
+
@@ -0,0 +1,136 @@
1
+ require 'dply/git'
2
+ require 'repo'
3
+
4
+ module Dply
5
+ describe Git do
6
+
7
+ before :all do
8
+ @work_dir = "tmp/git"
9
+ @source_repo = "#{Dir.pwd}/tmp/repo.git"
10
+ FileUtils.rm_rf @work_dir
11
+ FileUtils.mkdir_p @work_dir
12
+ end
13
+
14
+ def source_repo
15
+ @source_repo
16
+ end
17
+
18
+ def tmp_dir(&block)
19
+ d = Dir.mktmpdir nil, "#{Dir.pwd}/#{@work_dir}"
20
+ Dir.chdir(d) { yield d }
21
+ end
22
+
23
+ describe ".pull" do
24
+ it "checks out the given branch and pulls any changes to it from upstream" do
25
+ tmp_dir do |d|
26
+ upstream = "#{d}/upstream"
27
+ system! "git clone #{source_repo} upstream"
28
+ system! "git clone #{upstream} repo"
29
+ Dir.chdir "upstream" do
30
+ system! "git checkout -b new_data"
31
+ system! "echo 'new_data' > new_data"
32
+ system! "git add new_data; git commit -m 'commit'"
33
+ end
34
+
35
+ Dir.chdir "repo" do
36
+ suppress_output { Git.pull "new_data" }
37
+ expect(`git rev-parse --abbrev-ref HEAD`.chomp).to eq('new_data')
38
+ expect(File).to exist('new_data')
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ describe ".checkout" do
45
+ it "checks out the given branch" do
46
+ upstream = source_repo
47
+ tmp_dir do
48
+ system! "git clone #{upstream} repo", quiet: true
49
+ Dir.chdir "repo" do
50
+ # local tracking branch doesn't exist
51
+ suppress_output { Git.checkout "some_branch" }
52
+ expect(`git rev-parse --abbrev-ref HEAD`.chomp).to eq("some_branch")
53
+
54
+ #local tracking branch exists
55
+ suppress_output { Git.checkout "master" }
56
+ expect(`git rev-parse --abbrev-ref HEAD`.chomp).to eq("master")
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ describe ".clone" do
63
+ it "clones the repo from mirror and sets the upstream to original" do
64
+ mirror = source_repo
65
+ original = "/some/original/repo.git"
66
+ tmp_dir do
67
+ suppress_output { Git.clone original, "repo", mirror: mirror }
68
+ Dir.chdir "repo" do
69
+ expect(Git.remote_url).to eq(original)
70
+ end
71
+ end
72
+ end
73
+
74
+ it "clones the repo from original source when mirror is not specified" do
75
+ original = source_repo
76
+ tmp_dir do
77
+ suppress_output { Git.clone original, "repo", mirror: nil }
78
+ Dir.chdir "repo" do
79
+ expect(Git.remote_url).to eq(original)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ describe ".clean" do
86
+ it "cleans any changes made to the working tree (resets HEAD state and removes untracked files)" do
87
+ upstream = source_repo
88
+ tmp_dir do
89
+ system! "git clone #{upstream} repo", quiet: true
90
+ Dir.chdir "repo" do
91
+ system! "echo asda > my_new_file" #new
92
+ system! "echo existing > Gemfile" #existing
93
+ suppress_output { Git.clean }
94
+ lines = IO.popen(%w(git status --porcelain)) { |f| f.readlines }
95
+ expect(lines.size).to eq(0)
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ describe ".tracking_branch" do
102
+ it "returns the tracking branch for given branch" do
103
+ upstream = source_repo
104
+ branch = "some_branch"
105
+ tmp_dir do
106
+ system! "git clone #{upstream} repo", quiet: true
107
+ Dir.chdir "repo" do
108
+ system! "git checkout -b #{branch} -t origin/#{branch}", quiet: true
109
+ expect(Git.tracking_branch(branch)).to eq("origin/#{branch}")
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ describe ".remote_url" do
116
+ it "returns the url for origin upstream" do
117
+ upstream = source_repo
118
+ tmp_dir do |d|
119
+ system! "git clone #{upstream} repo", quiet: true
120
+ Dir.chdir("repo") do
121
+ expect(Git.remote_url).to eq(upstream)
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ describe ".commit_id" do
128
+ it "returns the commit id" do
129
+ Dir.chdir source_repo do
130
+ commit_id = `git rev-parse HEAD`.chomp
131
+ expect(Git.commit_id).to eq(commit_id)
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,168 @@
1
+ require 'dply/helper'
2
+ require 'pathname'
3
+
4
+ module Dply
5
+ describe Helper do
6
+
7
+ before :all do
8
+ @work_dir = "tmp/helper"
9
+ FileUtils.rm_rf @work_dir
10
+ FileUtils.mkdir_p @work_dir
11
+ end
12
+
13
+ klass = Class.new { include Helper }
14
+ subject { klass.new }
15
+
16
+ def tmp_dir(&block)
17
+ d = Dir.mktmpdir nil, "#{Dir.pwd}/#{@work_dir}"
18
+ Dir.chdir(d) { yield d }
19
+ end
20
+
21
+ describe "#cmd" do
22
+ command = "touch a"
23
+ display = :a
24
+ env = {"a" => "1" }
25
+ bundled_env = :benv
26
+ new_env = {"e" => "1"}
27
+
28
+ let(:cmd_proc) do
29
+ Proc.new do |return_output|
30
+ subject.cmd command, env: env, bundled_env: bundled_env,
31
+ return_output: return_output, display: display
32
+ end
33
+ end
34
+
35
+ it "runs the command as expected when return_output: true" do
36
+ return_output = true
37
+ cmd_double = double
38
+ output = "output"
39
+ expect(Logger.logger).to receive(:command).with(command, mode: display)
40
+ expect(Env).to receive(:build_env).with(env, bundled_env: bundled_env).and_return(new_env)
41
+ expect(Command).to receive(:new).with(command, env: new_env, shell: false).and_return(cmd_double)
42
+ expect(cmd_double).to receive(:capture).and_return(output)
43
+ out = cmd_proc.call(return_output)
44
+ expect(out).to eq(output)
45
+ end
46
+
47
+ it "runs the command as expected when return_output: false" do
48
+ return_output = false
49
+ cmd_double = double
50
+ expect(Logger.logger).to receive(:command).with(command, mode: display)
51
+ expect(Env).to receive(:build_env).with(env, bundled_env: bundled_env).and_return(new_env)
52
+ expect(Command).to receive(:new).with(command, env: new_env, shell: false).and_return(cmd_double)
53
+ expect(cmd_double).to receive(:run)
54
+ cmd_proc.call(return_output)
55
+ end
56
+ end
57
+
58
+ describe "#sh" do
59
+ command = "touch a"
60
+ display = :a
61
+ env = {"a" => "1" }
62
+ bundled_env = :benv
63
+ new_env = {"e" => "1"}
64
+
65
+ let(:sh_proc) do
66
+ Proc.new do |return_output|
67
+ subject.sh command, env: env, bundled_env: bundled_env,
68
+ return_output: return_output, display: display
69
+ end
70
+ end
71
+
72
+ it "runs the command as expected when return_output: true" do
73
+ return_output = true
74
+ sh_double = double
75
+ output = "output"
76
+ expect(Logger.logger).to receive(:command).with(command, mode: display)
77
+ expect(Env).to receive(:build_env).with(env, bundled_env: bundled_env).and_return(new_env)
78
+ expect(Command).to receive(:new).with(command, env: new_env, shell: true).and_return(sh_double)
79
+ expect(sh_double).to receive(:capture).and_return(output)
80
+ out = sh_proc.call(return_output)
81
+ expect(out).to eq(output)
82
+ end
83
+
84
+ it "runs the command as expected when return_output: false" do
85
+ return_output = false
86
+ sh_double = double
87
+ expect(Logger.logger).to receive(:command).with(command, mode: display)
88
+ expect(Env).to receive(:build_env).with(env, bundled_env: bundled_env).and_return(new_env)
89
+ expect(Command).to receive(:new).with(command, env: new_env, shell: true).and_return(sh_double)
90
+ expect(sh_double).to receive(:run)
91
+ sh_proc.call(return_output)
92
+ end
93
+ end
94
+
95
+ describe "#symlink" do
96
+ it "creates symlink dst -> src" do
97
+ tmp_dir do
98
+ FileUtils.touch "src"
99
+ subject.symlink "src", "dst"
100
+ expect(File.realpath("dst")).to eq(File.realpath("src"))
101
+ end
102
+ end
103
+
104
+ it "does nothing if the link exists" do
105
+ tmp_dir do
106
+ FileUtils.touch "src"
107
+ FileUtils.ln_s "src", "dst"
108
+ expect(FileUtils).not_to receive(:ln_s)
109
+ subject.symlink "src", "dst"
110
+ end
111
+ end
112
+
113
+ it "does not call ln_s when link exists and src/dest are Pathnames" do
114
+ tmp_dir do
115
+ FileUtils.touch "src"
116
+ FileUtils.ln_s "src", "dst"
117
+ expect(FileUtils).not_to receive(:ln_s)
118
+ subject.symlink Pathname.new("src"), Pathname.new("dst")
119
+ end
120
+ end
121
+
122
+ it "replaces existing dst -> src(file) link atomically (using move op)" do
123
+ tmp_dir do
124
+ FileUtils.touch "src"
125
+ FileUtils.touch "src1"
126
+ FileUtils.ln_s "src1", "dst"
127
+ prev_inode = File.stat("dst").ino
128
+
129
+ #atomic rename
130
+ dir = File.dirname("dst")
131
+ expect(subject).to receive(:cmd).with(array_including("mv", dir), display: false).and_call_original
132
+
133
+ subject.symlink "src", "dst"
134
+ current_inode = File.stat("dst").ino
135
+ expect(File.realpath("dst")).to eq(File.realpath("src"))
136
+ expect(prev_inode).not_to eq(current_inode)
137
+ end
138
+ end
139
+
140
+ it "replaces existing dst -> src(dir) link atomically" do
141
+ tmp_dir do
142
+ FileUtils.mkdir "src1"
143
+ FileUtils.mkdir "src"
144
+ FileUtils.ln_s "src1", "dst"
145
+ prev_inode = File.stat("dst").ino
146
+
147
+ dir = File.dirname("dst")
148
+
149
+ expect(subject).to receive(:cmd).with(array_including("mv", dir), display: false).and_call_original
150
+
151
+ subject.symlink "src", "dst"
152
+ current_inode = File.stat("dst").ino
153
+ expect(File.realpath("dst")).to eq(File.realpath("src"))
154
+ expect(prev_inode).not_to eq(current_inode)
155
+ end
156
+ end
157
+
158
+ it "raises an error when dst file already exists" do
159
+ tmp_dir do
160
+ FileUtils.touch "src"
161
+ FileUtils.touch "dst"
162
+ expect { subject.symlink "src", "dst" }.to raise_error(Error)
163
+ end
164
+ end
165
+
166
+ end
167
+ end
168
+ end