=begin Copyright 2010, Roger Pack This file is part of Sensible Cinema. Sensible Cinema is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Sensible Cinema is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Sensible Cinema. If not, see . =end require File.expand_path(File.dirname(__FILE__) + '/common') require_relative '../lib/overlayer' require_relative '../lib/screen_tracker' describe ScreenTracker do SILENCE = /silence.*VLC/ def start_vlc unless $pid1 assert !$pid1 # enforce that we only have at most one running... begin Win32::Screenshot.window(SILENCE, 0) {} raise Exception.new('must close existing vlcs first') # isn't caught rescue silence = File.expand_path("./silence.wav").gsub("/", "\\") vlc = Dir['/program files*/videolan/VLC/vlc.exe'][0] Dir.chdir(File.dirname(vlc)) do; IO.popen("vlc.exe #{silence}").pid; end # includes a work around for JRUBY-4959 /4958 # now also work around JRUBY-5756 until $pid1 $pid1 = GetPid.get_process_id_from_window(Win32::Screenshot::Util.window_hwnd(SILENCE)) rescue nil sleep 0.01 end end end end before(:all) do start_vlc end before do @a = ScreenTracker.new(SILENCE,10,10,20,20) end it "can take a regex or string" do ScreenTracker.new(SILENCE,10,10,20,20) ScreenTracker.new("silence",10,10,20,20) end it "should be able to grab a picture from screen coords...probably from the current active window" do @a.get_bmp.should_not be_nil end it "should loop if unable to find the right window" do proc { Timeout::timeout(1) do ScreenTracker.new("this is supposed to be not running",10,10,20,20) end }.should raise_error(Timeout::Error) end it "should be at least somewhat fast" do Benchmark.realtime { @a.get_bmp }.should be < 0.3 end it "should not allow for negative widths" do proc {ScreenTracker.new("VLC",10,10,-20,20)}.should raise_error end it "should disallow size 0 widths" do proc {ScreenTracker.new("VLC",10,10,0,0)}.should raise_error end it "should have different bmp if sizes different" do a = ScreenTracker.new("VLC",10,10,5,5) assert a.get_relative_coords == [10,10,15,15] b = ScreenTracker.new("VLC",10,10,50,50) assert a.get_bmp != b.get_bmp end it "should allow for straight desktop if they specify Desktop or desktop" do a = ScreenTracker.new("desktop",0,0,100,100) end context "negative numbers should be interpreted as an offset" do it "should allow for negative size parameter" do a = ScreenTracker.new("VLC",-10,10,5,5) a.get_bmp a = ScreenTracker.new("VLC",-10,-10,10,10) # right to the edge a.get_bmp a = ScreenTracker.new("VLC",10,-10,5,5) a.get_bmp end it "should assign right offsets" do a = ScreenTracker.new("VLC",-10,-10,5,5) a.get_bmp x,y,x2,y2=a.get_relative_coords hwnd = Win32::Screenshot::BitmapMaker.hwnd("VLC") max_x, max_y = Win32::Screenshot::Util.dimensions_for(hwnd) x.should == max_x-10 y.should == max_y-10 x2.should == x+5 y2.should == y+5 end it "should take negative coords and appear to use them" do a = ScreenTracker.new("VLC",10,10,50,50) b = ScreenTracker.new("VLC",10,10,50,50) c = ScreenTracker.new("VLC",-99,-99,50,50) assert a.get_bmp == b.get_bmp assert c.get_relative_coords != b.get_relative_coords cb = c.get_bmp bb = b.get_bmp c.get_bmp.length == b.get_bmp.length assert c.get_bmp != b.get_bmp end it "should fail with out of bounds or zero sizes" do proc { a = ScreenTracker.new(SILENCE,-10,10,20,20) }.should raise_error proc { a = ScreenTracker.new(SILENCE,10,-10,20,20) }.should raise_error proc { a = ScreenTracker.new(SILENCE,-10,10,0,2) }.should raise_error proc { a = ScreenTracker.new(SILENCE,10,10,2,0) }.should raise_error end end # lodo: this 7 looks rather redundant... it "should parse yaml appropro" do yaml = <<-YAML name: VLC x: 32 y: 34 width: 100 height: 20 digits: :hours: :minute_tens: - -90 - 7 :minute_ones: - -82 - 7 :second_tens: - -72 - 7 :second_ones: - -66 - 7 YAML a = ScreenTracker.new_from_yaml(yaml,nil) a.get_relative_coords.should == [32,34,132,54] end it "should be able to dump images it uses" do @a.dump_bmp assert File.exist?('dump.bmp') && File.exist?('all.dump.bmp') end context "tracking a real player that is moving" do before do @a = ScreenTracker.new(SILENCE, -111, -16, 86, 13) end it "should be able to poll the screen to know when something changes" do @a.wait_till_next_change # it updates every 1 second... Benchmark.realtime { @a.wait_till_next_change }.should be > 0.2 @a.dump_bmp # for debugging... end it "should be able to track via class_name" do a = ScreenTracker.new(SILENCE,10,10,20,20) b = ScreenTracker.new(/qwidget/i,10,10,20,20, true, {:second_ones => [-66, 7]}) a.hwnd.should == b.hwnd end end context "using hulu" do it "should be able to parse all digits" do pending "completion" a = ScreenTracker.new_from_yaml File.read("../zamples/players/hulu/total_length_over_an_hour.txt"), nil end end context "using youtube" do it "should be able to parse" end context "using a digit capable setting" do before do @a = ScreenTracker.new_from_yaml File.read("../zamples/players/vlc/windowed_total_length_under_an_hour.txt"), nil end it "should use OCR against the changes appropriately" do sleep 1.1 # sleep until it should show something for sure output = @a.wait_till_next_change # grab a real change output[0].should be_a(String) output[0].should include("00:") # like 00:09 or what not... output[0].should match(/[1-9]/) output[1].should be_a(Float) end it "should be able to scan for/identify new windows, since VLC creates entirely new windows when it transition from menu -> player" do output = @a.wait_till_next_change output[0].should_not be_nil old_handle = @a.hwnd kill_vlc start_vlc output = @a.wait_till_next_change output[0].should_not be_nil old_handle.should_not == @a.hwnd end it "should be able to disk dump its digit images" do @a.dump_bmp # what is the right number here? Pathname.new('minute_tens.1.bmp').should exist Pathname.new('minute_tens.1.bmp').size.should be > 0 # doesn't have hours Pathname.new('hours.1.bmp').should_not exist end it "should be ok with a non-existent (non-returned) hours image" do @a.stub!(:get_digits_as_bitmaps) do four = File.binread('images/vlc_4.bmp') black = File.binread('images/black.bmp') {:minute_tens=>four,:second_tens => four, :second_ones => four, :minute_ones => four, :hours => black} end @a.attempt_to_get_time_from_screen(Time.now)[0].should == "0:44:44" # hour is zero end it "should track the screen until it stabilizes, as VLC has a slight lag when seconds turn but minutes don't for a tidge" do time_through = 0 four = File.binread('images/vlc_4.bmp') black = File.binread('images/black.bmp') times_read=0 @a.stub!(:get_digits_as_bitmaps) do time_through += 1 if time_through == 1 {:minute_tens=>four} elsif time_through == 2 {:minute_tens=>black} else # just return the same thing times_read += 1 {:minute_tens=>four,:second_tens => four, :second_ones => four, :minute_ones => four, :hours => four} end end got = @a.attempt_to_get_time_from_screen(Time.now) time_through.should == 4 times_read.should == 2 got[0].should == "4:44:44" end context "with an OCR that can change from hour to minutes during ads" do it "should detect this change and act appropriately" end it "should be able to pass through the option to invert images" do @a = ScreenTracker.new(SILENCE, 100, 100, 10, 10, false, {:should_invert => true, :second_ones => [-66, 7]} ) # VLC descriptor is messed currently... got_it = nil OCR.stub!(:identify_digit) {|*args| got_it = args } @a.identify_digit('some binary bitmap data') got_it[1][:should_invert].should be_true end end def kill_vlc assert $pid1 # jruby... system("taskkill /pid #{$pid1}") Process.kill 9, $pid1 # MRI...sigh. FileUtils.rm_rf Dir['*.bmp'] $pid1 = nil sleep 1 end after(:all) do kill_vlc end end