Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
+104
View File
@@ -0,0 +1,104 @@
# encoding: utf-8
# frozen_string_literal: true
require 'ethon'
require 'open-uri'
require 'patron'
require 'curb'
require 'net/http'
require 'cgi'
require 'benchmark/ips'
require_relative '../spec/support/server'
require_relative '../spec/support/localhost_server'
LocalhostServer.new(TESTSERVER.new, 3000)
LocalhostServer.new(TESTSERVER.new, 3001)
LocalhostServer.new(TESTSERVER.new, 3002)
url = 'http://localhost:3000/'.freeze
uri = URI.parse('http://localhost:3000/').freeze
ethon = Ethon::Easy.new(url: url)
patron = Patron::Session.new
patron_url = Patron::Session.new(base_url: url)
curb = Curl::Easy.new(url)
puts '[Creation]'
Benchmark.ips do |x|
x.report('String.new') { '' }
x.report('Easy.new') { Ethon::Easy.new }
end
puts '[Escape]'
Benchmark.ips do |x|
x.report('CGI.escape') { CGI.escape("まつもと") }
x.report('Easy.escape') { ethon.escape("まつもと") }
end
puts '[Requests]'
Benchmark.ips do |x|
x.report('net/http') { Net::HTTP.get_response(uri) }
x.report('open-uri') { open url }
x.report('patron') do
patron.base_url = url
patron.get('/')
end
x.report('patron reuse') { patron_url.get('/') }
x.report('curb') do
curb.url = url
curb.perform
end
x.report('curb reuse') { curb.perform }
x.report('Easy.perform') do
ethon.url = url
ethon.perform
end
x.report('Easy.perform reuse') { ethon.perform }
end
puts "[ 4 delayed Requests ]"
Benchmark.ips do |x|
x.report('net/http') do
3.times do |i|
uri = URI.parse("http://localhost:300#{i}/?delay=1")
Net::HTTP.get_response(uri)
end
end
x.report("open-uri") do
3.times do |i|
open("http://localhost:300#{i}/?delay=1")
end
end
x.report("patron") do
sess = Patron::Session.new
3.times do |i|
sess.base_url = "http://localhost:300#{i}/?delay=1"
sess.get("/")
end
end
x.report("Easy.perform") do
easy = Ethon::Easy.new
3.times do |i|
easy.url = "http://localhost:300#{i}/?delay=1"
easy.perform
end
end
x.report("Multi.perform") do
multi = Ethon::Multi.new
3.times do |i|
easy = Ethon::Easy.new
easy.url = "http://localhost:300#{i}/?delay=1"
multi.add(easy)
end
multi.perform
end
end
@@ -0,0 +1,114 @@
# frozen_string_literal: true
require 'ethon'
require 'ethon/easy'
require_relative 'perf_spec_helper'
require 'rspec/autorun'
describe "low-level interactions with libcurl" do
describe Ethon::Multi do
memory_leak_test("init") do
Ethon::Multi.new
end
memory_leak_test("handle") do
Ethon::Multi.new.handle
end
end
describe Ethon::Easy do
memory_leak_test("init") do
Ethon::Easy.new
end
memory_leak_test("handle") do
Ethon::Easy.new.handle
end
memory_leak_test("headers") do
Ethon::Easy.new.headers = { "a" => 1, "b" => 2, "c" => 3, "d" => 4}
end
memory_leak_test("escape") do
Ethon::Easy.new.escape("the_sky&is_blue")
end
end
describe Ethon::Easy::Form do
memory_leak_test("init") do
Ethon::Easy::Form.new(nil, {})
end
memory_leak_test("first") do
Ethon::Easy::Form.new(nil, {}).first
end
memory_leak_test("last") do
Ethon::Easy::Form.new(nil, {}).last
end
memory_leak_test("materialized with some params") do
form = Ethon::Easy::Form.new(nil, { "a" => "1" })
form.materialize
end
memory_leak_test("materialized with a file") do
File.open(__FILE__, "r") do |file|
form = Ethon::Easy::Form.new(nil, { "a" => file })
form.materialize
end
end
end
end
describe "higher level operations" do
memory_leak_test("a simple request") do
Ethon::Easy.new(:url => "http://localhost:3001/",
:forbid_reuse => true).perform
end
memory_leak_test("a request with headers") do
Ethon::Easy.new(:url => "http://localhost:3001/",
:headers => { "Content-Type" => "application/json",
"Something" => "1",
"Else" => "qwerty",
"Long-String" => "aassddffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz"},
:forbid_reuse => true).perform
end
memory_leak_test("a request with headers and params") do
easy = Ethon::Easy.new(:url => "http://localhost:3001/",
:headers => { "Content-Type" => "application/json",
"Something" => "1",
"Else" => "qwerty",
"Long-String" => "aassddffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz"},
:forbid_reuse => true)
easy.http_request("http://localhost:3001/",
:get,
:params => { "param1" => "value1",
"param2" => "value2",
"param3" => "value3",
"param4" => "value4"})
end
memory_leak_test("a request with headers, params, and body") do
easy = Ethon::Easy.new(:url => "http://localhost:3001/",
:headers => { "Content-Type" => "application/json",
"Something" => "1",
"Else" => "qwerty",
"Long-String" => "aassddffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz"},
:forbid_reuse => true)
easy.http_request("http://localhost:3001/",
:get,
:params => { "param1" => "value1",
"param2" => "value2",
"param3" => "value3",
"param4" => "value4"},
:body => {
"body1" => "value1",
"body2" => "value2",
"body3" => "value3"
})
end
end
@@ -0,0 +1,37 @@
# frozen_string_literal: true
#### SETUP
require 'bundler'
Bundler.setup
require 'rspec'
require 'support/localhost_server'
require 'support/server'
require_relative 'support/memory_test_helpers'
require 'logger'
if ENV['VERBOSE']
Ethon.logger = Logger.new($stdout)
Ethon.logger.level = Logger::DEBUG
end
RSpec.configure do |config|
config.before(:suite) do
LocalhostServer.new(TESTSERVER.new, 3001)
end
config.include(MemoryTestHelpers)
config.extend(MemoryTestHelpers::TestMethods)
end
MemoryTestHelpers.setup
MemoryTestHelpers.logger = Logger.new($stdout)
MemoryTestHelpers.logger.level = Logger::INFO
MemoryTestHelpers.logger.formatter = proc do |severity, datetime, progname, msg|
"\t\t#{msg}\n"
end
if ENV['VERBOSE']
MemoryTestHelpers.logger.level = Logger::DEBUG
end
MemoryTestHelpers.iterations = ENV.fetch("ITERATIONS", 10_000).to_i
@@ -0,0 +1,76 @@
# frozen_string_literal: true
require_relative 'ruby_object_leak_tracker'
require_relative 'os_memory_leak_tracker'
module MemoryTestHelpers
class << self
attr_accessor :gc_proc, :iterations, :logger
def setup
if RUBY_PLATFORM == "java"
# for leak detection
JRuby.objectspace = true if defined?(JRuby)
# for gc
require 'java'
java_import 'java.lang.System'
self.gc_proc = proc { System.gc }
else
self.gc_proc = proc { GC.start }
end
end
end
module TestMethods
def memory_leak_test(description, &block)
context(description) do
it "doesn't leak ruby objects" do
object_leak_tracker = RubyObjectLeakTracker.new
track_memory_usage(object_leak_tracker, &block)
object_leak_tracker.total_difference_between_runs.should be <= 10
end
it "doesn't leak OS memory (C interop check)" do
os_memory_leak_tracker = OSMemoryLeakTracker.new
track_memory_usage(os_memory_leak_tracker, &block)
os_memory_leak_tracker.total_difference_between_runs.should be <= 10
end
end
end
end
def track_memory_usage(tracker)
# Intentionally do all this setup before we do any testing
logger = MemoryTestHelpers.logger
iterations = MemoryTestHelpers.iterations
checkpoint_frequency = (iterations / 10.0).to_i
gc_frequency = 20
warmup_iterations = [(iterations / 3.0).to_i, 500].min
logger.info "Performing #{warmup_iterations} warmup iterations"
warmup_iterations.times do
yield
MemoryTestHelpers.gc_proc.call
end
tracker.capture_initial_memory_usage
logger.info "Performing #{iterations} iterations (checkpoint every #{checkpoint_frequency})"
iterations.times do |i|
yield
last_iteration = (i == iterations - 1)
checkpoint = last_iteration || (i % checkpoint_frequency == 0)
if checkpoint || (i % gc_frequency == 0)
MemoryTestHelpers.gc_proc.call
end
if checkpoint
logger.info "Iteration #{i} checkpoint"
tracker.capture_memory_usage
tracker.dump_status(logger)
end
end
end
end
@@ -0,0 +1,48 @@
# frozen_string_literal: true
class OSMemoryLeakTracker
attr_reader :current_run
def initialize
@previous_run = @current_run = 0
end
def difference_between_runs(basis=@previous_run)
@current_run - basis
end
def total_difference_between_runs
difference_between_runs(@initial_count_run)
end
def capture_initial_memory_usage
capture_memory_usage
@initial_count_run = @current_run
end
def capture_memory_usage
@previous_run = @current_run
@current_run = rss_bytes
end
def dump_status(logger)
delta = difference_between_runs
logger.add(log_level(delta), sprintf("\tTotal memory usage (kb): %d (%+d)", current_run, delta))
end
private
# amount of memory the current process "is using", in RAM
# (doesn't include any swap memory that it may be using, just that in actual RAM)
# Code loosely based on https://github.com/rdp/os/blob/master/lib/os.rb
# returns 0 on windows
def rss_bytes
if ENV['OS'] == 'Windows_NT'
0
else
`ps -o rss= -p #{Process.pid}`.to_i # in kilobytes
end
end
def log_level(delta)
delta > 0 ? Logger::WARN : Logger::DEBUG
end
end
@@ -0,0 +1,49 @@
# frozen_string_literal: true
class RubyObjectLeakTracker
attr_reader :previous_count_hash, :current_count_hash
def initialize
@previous_count_hash = @current_count_hash = {}
end
def difference_between_runs(basis=@previous_count_hash)
@difference_between_runs ||= Hash[@current_count_hash.map do |object_class, count|
[object_class, count - (basis[object_class] || 0)]
end]
end
def total_difference_between_runs
difference_between_runs(@initial_count_hash).values.inject(0) { |sum, count| sum + count }
end
def capture_initial_memory_usage
capture_memory_usage
@initial_count_hash = @current_count_hash
end
def capture_memory_usage
@difference_between_runs = nil
@previous_count_hash = @current_count_hash
class_to_count = Hash.new { |hash, key| hash[key] = 0 }
ObjectSpace.each_object { |obj| class_to_count[obj.class] += 1 }
sorted_class_to_count = class_to_count.sort_by { |k, v| -v }
@current_count_hash = Hash[sorted_class_to_count]
end
def dump_status(logger)
diff = difference_between_runs
most_used_objects = current_count_hash.to_a.sort_by(&:last).reverse[0, 20]
most_used_objects.each do |object_class, count|
delta = diff[object_class]
logger.add(log_level(delta), sprintf("\t%s: %d (%+d)", object_class, count, delta))
end
end
private
def log_level(delta)
delta > 0 ? Logger::WARN : Logger::DEBUG
end
end