debian-mirror-gitlab/spec/lib/gitlab/ci/trace/stream_spec.rb

492 lines
12 KiB
Ruby
Raw Normal View History

2017-08-17 22:00:37 +05:30
require 'spec_helper'
2018-10-15 14:42:47 +05:30
describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do
set(:build) { create(:ci_build, :running) }
before do
stub_feature_flags(ci_enable_live_trace: true)
end
2017-08-17 22:00:37 +05:30
describe 'delegates' do
subject { described_class.new { nil } }
it { is_expected.to delegate_method(:close).to(:stream) }
it { is_expected.to delegate_method(:tell).to(:stream) }
it { is_expected.to delegate_method(:seek).to(:stream) }
it { is_expected.to delegate_method(:size).to(:stream) }
it { is_expected.to delegate_method(:path).to(:stream) }
it { is_expected.to delegate_method(:truncate).to(:stream) }
it { is_expected.to delegate_method(:valid?).to(:stream).as(:present?) }
end
describe '#limit' do
2018-10-15 14:42:47 +05:30
shared_examples_for 'limits' do
it 'if size is larger we start from beginning' do
stream.limit(20)
expect(stream.tell).to eq(0)
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
it 'if size is smaller we start from the end' do
stream.limit(2)
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
expect(stream.raw).to eq("8")
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'when the trace contains ANSI sequence and Unicode' do
let(:stream) do
described_class.new do
File.open(expand_fixture_path('trace/ansi-sequence-and-unicode'))
end
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it 'forwards to the next linefeed, case 1' do
stream.limit(7)
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
result = stream.raw
expect(result).to eq('')
expect(result.encoding).to eq(Encoding.default_external)
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
it 'forwards to the next linefeed, case 2' do
stream.limit(29)
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
result = stream.raw
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
expect(result).to eq("\e[01;32m許功蓋\e[0m\n")
expect(result.encoding).to eq(Encoding.default_external)
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/30796
it 'reads in binary, output as Encoding.default_external' do
stream.limit(52)
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
result = stream.html
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
expect(result).to eq("ヾ(´༎ຶД༎ຶ`)ノ<br><span class=\"term-fg-green\">許功蓋</span><br>")
expect(result.encoding).to eq(Encoding.default_external)
end
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'when stream is StringIO' do
let(:stream) do
described_class.new do
StringIO.new((1..8).to_a.join("\n"))
end
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it_behaves_like 'limits'
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'when stream is ChunkedIO' do
let(:stream) do
described_class.new do
Gitlab::Ci::Trace::ChunkedIO.new(build).tap do |chunked_io|
chunked_io.write((1..8).to_a.join("\n"))
chunked_io.seek(0, IO::SEEK_SET)
end
end
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
it_behaves_like 'limits'
2017-08-17 22:00:37 +05:30
end
end
describe '#append' do
2018-10-15 14:42:47 +05:30
shared_examples_for 'appends' do
it "truncates and append content" do
stream.append("89", 4)
stream.seek(0)
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
expect(stream.size).to eq(6)
expect(stream.raw).to eq("123489")
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
it 'appends in binary mode' do
'😺'.force_encoding('ASCII-8BIT').each_char.with_index do |byte, offset|
stream.append(byte, offset)
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
stream.seek(0)
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
expect(stream.size).to eq(4)
expect(stream.raw).to eq('😺')
end
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'when stream is Tempfile' do
let(:tempfile) { Tempfile.new }
let(:stream) do
described_class.new do
tempfile.write("12345678")
tempfile.rewind
tempfile
end
end
after do
tempfile.unlink
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
it_behaves_like 'appends'
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'when stream is ChunkedIO' do
let(:stream) do
described_class.new do
Gitlab::Ci::Trace::ChunkedIO.new(build).tap do |chunked_io|
chunked_io.write('12345678')
chunked_io.seek(0, IO::SEEK_SET)
end
end
end
it_behaves_like 'appends'
2017-08-17 22:00:37 +05:30
end
end
describe '#set' do
2018-10-15 14:42:47 +05:30
shared_examples_for 'sets' do
before do
stream.set("8901")
end
it "overwrite content" do
stream.seek(0)
expect(stream.size).to eq(4)
expect(stream.raw).to eq("8901")
2017-08-17 22:00:37 +05:30
end
end
2018-10-15 14:42:47 +05:30
context 'when stream is StringIO' do
let(:stream) do
described_class.new do
StringIO.new("12345678")
end
end
it_behaves_like 'sets'
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'when stream is ChunkedIO' do
let(:stream) do
described_class.new do
Gitlab::Ci::Trace::ChunkedIO.new(build).tap do |chunked_io|
chunked_io.write('12345678')
chunked_io.seek(0, IO::SEEK_SET)
end
end
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it_behaves_like 'sets'
2017-08-17 22:00:37 +05:30
end
end
describe '#raw' do
2018-10-15 14:42:47 +05:30
shared_examples_for 'sets' do
it 'returns all contents if last_lines is not specified' do
result = stream.raw
expect(result).to eq(lines.join)
expect(result.encoding).to eq(Encoding.default_external)
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'limit max lines' do
before do
# specifying BUFFER_SIZE forces to seek backwards
allow(described_class).to receive(:BUFFER_SIZE)
.and_return(2)
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it 'returns last few lines' do
result = stream.raw(last_lines: 2)
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
expect(result).to eq(lines.last(2).join)
expect(result.encoding).to eq(Encoding.default_external)
end
it 'returns everything if trying to get too many lines' do
result = stream.raw(last_lines: lines.size * 2)
expect(result).to eq(lines.join)
expect(result.encoding).to eq(Encoding.default_external)
end
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
let(:path) { __FILE__ }
let(:lines) { File.readlines(path) }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'when stream is File' do
let(:stream) do
described_class.new do
File.open(path)
end
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
it_behaves_like 'sets'
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'when stream is ChunkedIO' do
let(:stream) do
described_class.new do
Gitlab::Ci::Trace::ChunkedIO.new(build).tap do |chunked_io|
chunked_io.write(File.binread(path))
chunked_io.seek(0, IO::SEEK_SET)
end
end
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
it_behaves_like 'sets'
2017-08-17 22:00:37 +05:30
end
end
describe '#html_with_state' do
2018-10-15 14:42:47 +05:30
shared_examples_for 'html_with_states' do
it 'returns html content with state' do
result = stream.html_with_state
expect(result.html).to eq("1234")
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'follow-up state' do
let!(:last_result) { stream.html_with_state }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
before do
2019-02-15 15:39:39 +05:30
data_stream.seek(4, IO::SEEK_SET)
data_stream.write("5678")
2018-10-15 14:42:47 +05:30
stream.seek(0)
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it "returns appended trace" do
result = stream.html_with_state(last_result.state)
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
expect(result.append).to be_truthy
expect(result.html).to eq("5678")
end
end
end
context 'when stream is StringIO' do
2019-02-15 15:39:39 +05:30
let(:data_stream) do
StringIO.new("1234")
end
2018-10-15 14:42:47 +05:30
let(:stream) do
2019-02-15 15:39:39 +05:30
described_class.new { data_stream }
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
it_behaves_like 'html_with_states'
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'when stream is ChunkedIO' do
2019-02-15 15:39:39 +05:30
let(:data_stream) do
Gitlab::Ci::Trace::ChunkedIO.new(build).tap do |chunked_io|
chunked_io.write("1234")
chunked_io.seek(0, IO::SEEK_SET)
2019-01-03 12:48:30 +05:30
end
2018-12-23 12:14:25 +05:30
end
2019-02-15 15:39:39 +05:30
let(:stream) do
described_class.new { data_stream }
end
2018-10-15 14:42:47 +05:30
it_behaves_like 'html_with_states'
2017-08-17 22:00:37 +05:30
end
end
describe '#html' do
2018-10-15 14:42:47 +05:30
shared_examples_for 'htmls' do
it "returns html" do
expect(stream.html).to eq("12<br>34<br>56")
end
it "returns html for last line only" do
expect(stream.html(last_lines: 1)).to eq("56")
2017-08-17 22:00:37 +05:30
end
end
2018-10-15 14:42:47 +05:30
context 'when stream is StringIO' do
let(:stream) do
described_class.new do
StringIO.new("12\n34\n56")
end
end
it_behaves_like 'htmls'
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'when stream is ChunkedIO' do
let(:stream) do
described_class.new do
Gitlab::Ci::Trace::ChunkedIO.new(build).tap do |chunked_io|
chunked_io.write("12\n34\n56")
chunked_io.seek(0, IO::SEEK_SET)
end
end
end
it_behaves_like 'htmls'
2017-08-17 22:00:37 +05:30
end
end
describe '#extract_coverage' do
2018-10-15 14:42:47 +05:30
shared_examples_for 'extract_coverages' do
context 'valid content & regex' do
let(:data) { 'Coverage 1033 / 1051 LOC (98.29%) covered' }
let(:regex) { '\(\d+.\d+\%\) covered' }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it { is_expected.to eq("98.29") }
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'valid content & bad regex' do
let(:data) { 'Coverage 1033 / 1051 LOC (98.29%) covered\n' }
let(:regex) { 'very covered' }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it { is_expected.to be_nil }
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'no coverage content & regex' do
let(:data) { 'No coverage for today :sad:' }
let(:regex) { '\(\d+.\d+\%\) covered' }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it { is_expected.to be_nil }
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'multiple results in content & regex' do
let(:data) do
<<~HEREDOC
(98.39%) covered
(98.29%) covered
HEREDOC
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
let(:regex) { '\(\d+.\d+\%\) covered' }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it 'returns the last matched coverage' do
is_expected.to eq("98.29")
end
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'when BUFFER_SIZE is smaller than stream.size' do
let(:data) { 'Coverage 1033 / 1051 LOC (98.29%) covered\n' }
let(:regex) { '\(\d+.\d+\%\) covered' }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
before do
stub_const('Gitlab::Ci::Trace::Stream::BUFFER_SIZE', 5)
end
it { is_expected.to eq("98.29") }
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'when regex is multi-byte char' do
let(:data) { '95.0 ゴッドファット\n' }
let(:regex) { '\d+\.\d+ ゴッドファット' }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
before do
stub_const('Gitlab::Ci::Trace::Stream::BUFFER_SIZE', 5)
end
it { is_expected.to eq('95.0') }
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'when BUFFER_SIZE is equal to stream.size' do
let(:data) { 'Coverage 1033 / 1051 LOC (98.29%) covered\n' }
let(:regex) { '\(\d+.\d+\%\) covered' }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
before do
stub_const('Gitlab::Ci::Trace::Stream::BUFFER_SIZE', data.length)
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it { is_expected.to eq("98.29") }
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'using a regex capture' do
let(:data) { 'TOTAL 9926 3489 65%' }
let(:regex) { 'TOTAL\s+\d+\s+\d+\s+(\d{1,3}\%)' }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it { is_expected.to eq("65") }
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
context 'malicious regexp' do
let(:data) { malicious_text }
2019-04-03 18:18:56 +05:30
let(:regex) { malicious_regexp_re2 }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
include_examples 'malicious regexp'
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'multi-line data with rooted regexp' do
let(:data) { "\n65%\n" }
let(:regex) { '^(\d+)\%$' }
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
it { is_expected.to eq('65') }
end
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
context 'long line' do
let(:data) { 'a' * 80000 + '100%' + 'a' * 80000 }
let(:regex) { '\d+\%' }
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
it { is_expected.to eq('100') }
end
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
context 'many lines' do
let(:data) { "foo\n" * 80000 + "100%\n" + "foo\n" * 80000 }
let(:regex) { '\d+\%' }
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
it { is_expected.to eq('100') }
end
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
context 'empty regex' do
let(:data) { 'foo' }
let(:regex) { '' }
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
it 'skips processing' do
expect(stream).not_to receive(:read)
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
is_expected.to be_nil
end
end
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
context 'nil regex' do
let(:data) { 'foo' }
let(:regex) { nil }
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
it 'skips processing' do
expect(stream).not_to receive(:read)
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
is_expected.to be_nil
end
2017-09-10 17:25:29 +05:30
end
end
2018-10-15 14:42:47 +05:30
subject { stream.extract_coverage(regex) }
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
context 'when stream is StringIO' do
let(:stream) do
described_class.new do
StringIO.new(data)
end
end
it_behaves_like 'extract_coverages'
end
2017-09-10 17:25:29 +05:30
2018-10-15 14:42:47 +05:30
context 'when stream is ChunkedIO' do
let(:stream) do
described_class.new do
Gitlab::Ci::Trace::ChunkedIO.new(build).tap do |chunked_io|
chunked_io.write(data)
chunked_io.seek(0, IO::SEEK_SET)
end
end
2017-09-10 17:25:29 +05:30
end
2018-10-15 14:42:47 +05:30
it_behaves_like 'extract_coverages'
2017-09-10 17:25:29 +05:30
end
2017-08-17 22:00:37 +05:30
end
end