Extract a class that represents artifacts file path

This commit is contained in:
Grzegorz Bizon 2017-09-05 12:17:56 +02:00
parent dfb8fcbb65
commit c53f319f88
3 changed files with 236 additions and 121 deletions

View file

@ -1,5 +1,7 @@
module Gitlab module Gitlab
module Ci::Build::Artifacts module Ci
module Build
module Artifacts
class Metadata class Metadata
## ##
# Class that represents an entry (path and metadata) to a file or # Class that represents an entry (path and metadata) to a file or
@ -11,26 +13,18 @@ module Gitlab
# This class is working only with UTF-8 encoded paths. # This class is working only with UTF-8 encoded paths.
# #
class Entry class Entry
attr_reader :path, :entries attr_reader :entries
attr_accessor :name attr_accessor :name
def initialize(path, entries) def initialize(path, entries)
@path = path.dup.force_encoding('UTF-8')
@entries = entries @entries = entries
@path = Artifacts::Path.new(path)
if path.include?("\0")
raise ArgumentError, 'Path contains zero byte character!'
end
unless path.valid_encoding?
raise ArgumentError, 'Path contains non-UTF-8 byte sequence!'
end
end end
delegate :empty?, to: :children delegate :empty?, to: :children
def directory? def directory?
blank_node? || @path.end_with?('/') blank_node? || @path.directory?
end end
def file? def file?
@ -49,7 +43,7 @@ module Gitlab
def parent def parent
return nil unless has_parent? return nil unless has_parent?
self.class.new(@path.chomp(basename), @entries) self.class.new(@path.to_s.chomp(basename), @entries)
end end
def basename def basename
@ -57,14 +51,14 @@ module Gitlab
end end
def name def name
@name || @path.split('/').last.to_s @name || @path.name
end end
def children def children
return [] unless directory? return [] unless directory?
return @children if @children return @children if @children
child_pattern = %r{^#{Regexp.escape(@path)}[^/]+/?$} child_pattern = %r{^#{Regexp.escape(@path.to_s)}[^/]+/?$}
@children = select_entries { |path| path =~ child_pattern } @children = select_entries { |path| path =~ child_pattern }
end end
@ -84,38 +78,42 @@ module Gitlab
end end
def metadata def metadata
@entries[@path] || {} @entries[@path.to_s] || {}
end end
def nodes def nodes
@path.count('/') + (file? ? 1 : 0) @path.nodes + (file? ? 1 : 0)
end end
def blank_node? def blank_node?
@path.empty? # "" is considered to be './' @path.to_s.empty? # "" is considered to be './'
end end
def exists? def exists?
blank_node? || @entries.include?(@path) blank_node? || @entries.include?(@path.to_s)
end end
def total_size def total_size
descendant_pattern = %r{^#{Regexp.escape(@path)}} descendant_pattern = %r{^#{Regexp.escape(@path.to_s)}}
entries.sum do |path, entry| entries.sum do |path, entry|
(entry[:size] if path =~ descendant_pattern).to_i (entry[:size] if path =~ descendant_pattern).to_i
end end
end end
def path
@path.to_s
end
def to_s def to_s
@path @path.to_s
end end
def ==(other) def ==(other)
@path == other.path && @entries == other.entries path == other.path && @entries == other.entries
end end
def inspect def inspect
"#{self.class.name}: #{@path}" "#{self.class.name}: #{self.to_s}"
end end
private private
@ -128,3 +126,5 @@ module Gitlab
end end
end end
end end
end
end

View file

@ -0,0 +1,51 @@
module Gitlab
module Ci
module Build
module Artifacts
class Path
def initialize(path)
@path = path.dup.force_encoding('UTF-8')
end
def valid?
nonzero? && utf8?
end
def directory?
@path.end_with?('/')
end
def name
@path.split('/').last.to_s
end
def nodes
@path.count('/')
end
def to_s
@path.tap do |path|
unless nonzero?
raise ArgumentError, 'Path contains zero byte character!'
end
unless utf8?
raise ArgumentError, 'Path contains non-UTF-8 byte sequence!'
end
end
end
private
def nonzero?
@path.exclude?("\0")
end
def utf8?
@path.valid_encoding?
end
end
end
end
end
end

View file

@ -0,0 +1,64 @@
require 'spec_helper'
describe Gitlab::Ci::Build::Artifacts::Path do
describe '#valid?' do
context 'when path contains a zero character' do
it 'is not valid' do
expect(described_class.new("something/\255")).not_to be_valid
end
end
context 'when path is not utf8 string' do
it 'is not valid' do
expect(described_class.new("something/\0")).not_to be_valid
end
end
context 'when path is valid' do
it 'is valid' do
expect(described_class.new("some/file/path")).to be_valid
end
end
end
describe '#directory?' do
context 'when path ends with a directory indicator' do
it 'is a directory' do
expect(described_class.new("some/file/dir/")).to be_directory
end
end
context 'when path does not end with a directory indicator' do
it 'is not a directory' do
expect(described_class.new("some/file")).not_to be_directory
end
end
end
describe '#name' do
it 'returns a base name' do
expect(described_class.new("some/file").name).to eq 'file'
end
end
describe '#nodes' do
it 'returns number of path nodes' do
expect(described_class.new("some/dir/file").nodes).to eq 2
end
end
describe '#to_s' do
context 'when path is valid' do
it 'returns a string representation of a path' do
expect(described_class.new('some/path').to_s).to eq 'some/path'
end
end
context 'when path is invalid' do
it 'raises an error' do
expect { described_class.new("invalid/\0").to_s }
.to raise_error ArgumentError
end
end
end
end