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

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

465 lines
12 KiB
Ruby
Raw Normal View History

2019-12-26 22:10:19 +05:30
# frozen_string_literal: true
2017-08-17 22:00:37 +05:30
require 'spec_helper'
2020-07-28 23:09:34 +05:30
RSpec.describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do
2020-04-08 14:13:33 +05:30
let_it_be(:build) { create(:ci_build, :running) }
2018-10-15 14:42:47 +05:30
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
2019-12-04 20:38:33 +05:30
# See https://gitlab.com/gitlab-org/gitlab-foss/issues/30796
2018-10-15 14:42:47 +05:30
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
2019-09-04 21:01:54 +05:30
expect(result).to eq(
2019-09-30 21:07:59 +05:30
"<span>ヾ(´༎ຶД༎ຶ`)ノ<br/></span>"\
"<span class=\"term-fg-green\">許功蓋</span>"\
"<span><br/></span>")
2018-10-15 14:42:47 +05:30
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
2020-10-24 23:57:45 +05:30
it "truncates and appends content" do
2019-12-26 22:10:19 +05:30
stream.append(+"89", 4)
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(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
2019-12-26 22:10:19 +05:30
(+'😺').force_encoding('ASCII-8BIT').each_char.with_index do |byte, offset|
2018-10-15 14:42:47 +05:30
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
2020-11-24 15:15:51 +05:30
describe 'metrics' do
let(:metrics) { spy('metrics') }
let(:io) { StringIO.new }
let(:stream) { described_class.new(metrics) { io } }
it 'increments trace streamed operation' do
stream.append(+'123456', 0)
expect(metrics)
.to have_received(:increment_trace_operation)
.with(operation: :streamed)
end
it 'increments trace bytes counter' do
stream.append(+'123456', 0)
expect(metrics)
.to have_received(:increment_trace_bytes)
.with(6)
end
end
2017-08-17 22:00:37 +05:30
end
describe '#set' do
2018-10-15 14:42:47 +05:30
shared_examples_for 'sets' do
before do
2019-12-26 22:10:19 +05:30
stream.set(+"8901")
2018-10-15 14:42:47 +05:30
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
2019-12-26 22:10:19 +05:30
StringIO.new(+"12345678")
2018-10-15 14:42:47 +05:30
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' do
2018-10-15 14:42:47 +05:30
shared_examples_for 'htmls' do
it "returns html" do
2019-09-30 21:07:59 +05:30
expect(stream.html).to eq("<span>12<br/>34<br/>56</span>")
2018-10-15 14:42:47 +05:30
end
it "returns html for last line only" do
2019-09-30 21:07:59 +05:30
expect(stream.html(last_lines: 1)).to eq("<span>56</span>")
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