spec/symphony/tasks/sshscript_spec.rb
author Mahlon E. Smith <mahlon@martini.nu>
Mon, 02 Sep 2019 08:41:08 -0700
changeset 13 8e29e051b417
parent 11 ffa70066522c
child 19 f31d60b04f8a
permissions -rw-r--r--
rdoc -> markdown for the README.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
4
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
     1
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
     2
require_relative '../../helpers'
11
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
     3
require 'symphony/tasks/ssh'
4
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
     4
require 'symphony/tasks/sshscript'
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
     5
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
     6
context Symphony::Task::SSHScript do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
     7
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
     8
	before( :each ) do
11
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
     9
		Symphony::Task::SSH.configure(
4
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    10
			key:  '/tmp/sekrit.rsa',
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    11
			user: 'symphony'
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    12
		)
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    13
	end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    14
11
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    15
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    16
	describe 'utility' do
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    17
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    18
		it "can generate an appropriate tempfile name" do
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    19
			instance = Class.new( described_class ).new( 'queue' )
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    20
			tmpname = instance.send( :make_remote_filename, "fancy-script.tmpl" )
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    21
			expect( tmpname ).to match( %r|^/tmp/fancy-script-[[:xdigit:]]{6}| )
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    22
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    23
			tmpname = instance.send( :make_remote_filename, "fancy-script.tmpl", "/var/tmp" )
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    24
			expect( tmpname ).to match( %r|/var/tmp/fancy-script-[[:xdigit:]]{6}| )
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    25
		end
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    26
	end
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    27
4
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    28
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    29
	describe 'subclassed' do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    30
		let( :instance ) { Class.new(described_class).new('queue') }
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    31
		let( :payload ) {
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    32
			{ 'template' => 'script', 'host' => 'example.com' }
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    33
		}
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    34
		let( :opts ) {
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    35
			opts = described_class::DEFAULT_SSH_OPTIONS
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    36
			opts.merge!(
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    37
				:port    => 22,
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    38
				:keys    => ['/tmp/sekrit.rsa']
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    39
			)
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    40
			opts
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    41
		}
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    42
		let( :template ) { Inversion::Template.new("Hi there, <?attr name?>!") }
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    43
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    44
		before( :each ) do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    45
			allow( Inversion::Template ).to receive( :load ).and_return( template )
11
ffa70066522c Update for Ruby 2.5. Moderize configurability usage, update dependencies.
Mahlon E. Smith <mahlon@martini.nu>
parents: 4
diff changeset
    46
			allow( instance ).to receive( :make_remote_filename ).and_return( "/tmp/script_temp" )
4
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    47
		end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    48
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    49
		it "aborts if there is no template in the payload" do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    50
			expect {
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    51
				instance.work( {}, {} )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    52
			}.to raise_exception( ArgumentError, /missing required option 'template'/i )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    53
		end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    54
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    55
		it "aborts if there is no host in the payload" do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    56
			expect {
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    57
				instance.work({ 'template' => 'boop' }, {} )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    58
			}.to raise_exception( ArgumentError, /missing required option 'host'/i )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    59
		end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    60
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    61
		it "adds debugging output if specified in the payload" do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    62
			payload[ 'debug' ] = true
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    63
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    64
			options = opts.dup
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    65
			options.merge!(
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    66
				:logger  => Loggability[ Net::SSH ],
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    67
				:verbose => :debug
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    68
			)
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    69
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    70
			expect( Net::SSH ).to receive( :start ).with( 'example.com', 'symphony', options )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    71
			instance.work( payload, {} )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    72
		end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    73
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    74
		it "attaches attributes to the scripts from the payload" do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    75
			payload[ 'attributes' ] = { :name => 'Handsome' }
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    76
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    77
			conn = double( :ssh_connection )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    78
			expect( instance ).to receive( :upload_script ).
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    79
				with( conn, "Hi there, Handsome!", "/tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    80
			expect( conn ).to receive( :exec! ).with( "/tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    81
			expect( conn ).to receive( :exec! ).with( "rm /tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    82
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    83
			expect( Net::SSH ).to receive( :start ).
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    84
				with( 'example.com', 'symphony', opts ).and_yield( conn )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    85
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    86
			instance.work( payload, {} )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    87
		end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    88
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    89
		it "uploads the file and sets it executable" do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    90
			conn = double( :ssh_connection )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    91
			sftp = double( :sftp_connection )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    92
			file = double( :remote_file_obj )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    93
			fh   = double( :remote_filehandle )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    94
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    95
			expect( conn ).to receive( :sftp ).and_return( sftp )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    96
			expect( sftp ).to receive( :file ).and_return( file )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    97
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    98
			expect( file ).to receive( :open ).
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
    99
				with( "/tmp/script_temp", "w", 0755 ).and_yield( fh )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   100
			expect( fh ).to receive( :print ).with( "Hi there, !" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   101
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   102
			expect( conn ).to receive( :exec! ).with( "/tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   103
			expect( conn ).to receive( :exec! ).with( "rm /tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   104
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   105
			expect( Net::SSH ).to receive( :start ).
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   106
				with( 'example.com', 'symphony', opts ).and_yield( conn )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   107
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   108
			instance.work( payload, {} )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   109
		end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   110
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   111
		it "leaves the remote script in place if asked" do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   112
			payload[ 'nocleanup' ] = true
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   113
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   114
			conn = double( :ssh_connection )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   115
			expect( instance ).to receive( :upload_script ).
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   116
				with( conn, "Hi there, !", "/tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   117
			expect( conn ).to receive( :exec! ).with( "/tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   118
			expect( conn ).to_not receive( :exec! ).with( "rm /tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   119
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   120
			expect( Net::SSH ).to receive( :start ).
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   121
				with( 'example.com', 'symphony', opts ).and_yield( conn )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   122
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   123
			instance.work( payload, {} )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   124
		end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   125
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   126
		it "remembers the output of the remote script" do
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   127
			conn = double( :ssh_connection )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   128
			expect( instance ).to receive( :upload_script ).
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   129
				with( conn, "Hi there, !", "/tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   130
			expect( conn ).to receive( :exec! ).with( "/tmp/script_temp" ).and_return( "Hi there, !" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   131
			expect( conn ).to receive( :exec! ).with( "rm /tmp/script_temp" )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   132
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   133
			expect( Net::SSH ).to receive( :start ).
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   134
				with( 'example.com', 'symphony', opts ).and_yield( conn )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   135
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   136
			instance.work( payload, {} )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   137
			output = instance.instance_variable_get( :@output )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   138
			expect( output ).to eq( 'Hi there, !' )
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   139
		end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   140
	end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   141
end
3972315383b3 Small cleanups, add tests.
Mahlon E. Smith <mahlon@martini.nu>
parents:
diff changeset
   142