[Groonga-commit] ranguba/chupa-text-decomposer-libreoffice at 6c10ac0 [master] Add "libreoffice" decomposer for compatibility

Back to archive index
Kouhei Sutou null+****@clear*****
Tue Feb 26 14:54:14 JST 2019


Kouhei Sutou	2019-02-26 14:54:14 +0900 (Tue, 26 Feb 2019)

  Revision: 6c10ac067ac2f087317fcaeb8362f069c7914579
  https://github.com/ranguba/chupa-text-decomposer-libreoffice/commit/6c10ac067ac2f087317fcaeb8362f069c7914579

  Message:
    Add "libreoffice" decomposer for compatibility

  Added files:
    chupa-text-decomposer-libreoffice/test/tests/doc-tests.rb
    chupa-text-decomposer-libreoffice/test/tests/docx-tests.rb
    chupa-text-decomposer-libreoffice/test/tests/odp-tests.rb
    chupa-text-decomposer-libreoffice/test/tests/ods-tests.rb
    chupa-text-decomposer-libreoffice/test/tests/odt-tests.rb
    chupa-text-decomposer-libreoffice/test/tests/ppt-tests.rb
    chupa-text-decomposer-libreoffice/test/tests/pptx-tests.rb
    chupa-text-decomposer-libreoffice/test/tests/xls-tests.rb
    chupa-text-decomposer-libreoffice/test/tests/xlsx-tests.rb
  Removed files:
    chupa-text-decomposer-libreoffice-excel/test/fixture/attributes.xls
    chupa-text-decomposer-libreoffice-excel/test/fixture/multi-sheets.xls
    chupa-text-decomposer-libreoffice-excel/test/fixture/one-sheet.xls
    chupa-text-decomposer-libreoffice-general/test/helper.rb
    chupa-text-decomposer-libreoffice-office-open-xml-document/test/fixture/attributes.docx
    chupa-text-decomposer-libreoffice-office-open-xml-document/test/fixture/multi-pages.docx
    chupa-text-decomposer-libreoffice-office-open-xml-document/test/fixture/one-page.docx
    chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/fixture/attributes.pptx
    chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/fixture/multi-slides.pptx
    chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/fixture/one-slide.pptx
    chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/fixture/attributes.xlsx
    chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/fixture/multi-sheets.xlsx
    chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/fixture/one-sheet.xlsx
    chupa-text-decomposer-libreoffice-opendocument-presentation/test/fixture/attributes.odp
    chupa-text-decomposer-libreoffice-opendocument-presentation/test/fixture/multi-slides.odp
    chupa-text-decomposer-libreoffice-opendocument-presentation/test/fixture/one-slide.odp
    chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/fixture/attributes.ods
    chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/fixture/multi-sheets.ods
    chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/fixture/one-sheet.ods
    chupa-text-decomposer-libreoffice-opendocument-text/test/fixture/attributes.odt
    chupa-text-decomposer-libreoffice-opendocument-text/test/fixture/multi-pages.odt
    chupa-text-decomposer-libreoffice-opendocument-text/test/fixture/one-page.odt
    chupa-text-decomposer-libreoffice-powerpoint/test/fixture/attributes.ppt
    chupa-text-decomposer-libreoffice-powerpoint/test/fixture/multi-slides.ppt
    chupa-text-decomposer-libreoffice-powerpoint/test/fixture/one-slide.ppt
    chupa-text-decomposer-libreoffice-word/test/fixture/attributes.doc
    chupa-text-decomposer-libreoffice-word/test/fixture/multi-pages.doc
    chupa-text-decomposer-libreoffice-word/test/fixture/one-page.doc
  Modified files:
    chupa-text-decomposer-libreoffice-excel/lib/chupa-text/decomposers/libreoffice-excel.rb
    chupa-text-decomposer-libreoffice-excel/test/run-test.rb
    chupa-text-decomposer-libreoffice-excel/test/test-xls.rb
    chupa-text-decomposer-libreoffice-general/lib/chupa-text/decomposers/libreoffice-general.rb
    chupa-text-decomposer-libreoffice-office-open-xml-document/lib/chupa-text/decomposers/libreoffice-office-open-xml-document.rb
    chupa-text-decomposer-libreoffice-office-open-xml-document/test/run-test.rb
    chupa-text-decomposer-libreoffice-office-open-xml-document/test/test-docx.rb
    chupa-text-decomposer-libreoffice-office-open-xml-presentation/lib/chupa-text/decomposers/libreoffice-office-open-xml-presentation.rb
    chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/run-test.rb
    chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/test-pptx.rb
    chupa-text-decomposer-libreoffice-office-open-xml-workbook/lib/chupa-text/decomposers/libreoffice-office-open-xml-workbook.rb
    chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/run-test.rb
    chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/test-xlsx.rb
    chupa-text-decomposer-libreoffice-opendocument-presentation/lib/chupa-text/decomposers/libreoffice-opendocument-presentation.rb
    chupa-text-decomposer-libreoffice-opendocument-presentation/test/run-test.rb
    chupa-text-decomposer-libreoffice-opendocument-presentation/test/test-odp.rb
    chupa-text-decomposer-libreoffice-opendocument-spreadsheet/lib/chupa-text/decomposers/libreoffice-opendocument-spreadsheet.rb
    chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/run-test.rb
    chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/test-ods.rb
    chupa-text-decomposer-libreoffice-opendocument-text/lib/chupa-text/decomposers/libreoffice-opendocument-text.rb
    chupa-text-decomposer-libreoffice-opendocument-text/test/run-test.rb
    chupa-text-decomposer-libreoffice-opendocument-text/test/test-odt.rb
    chupa-text-decomposer-libreoffice-powerpoint/lib/chupa-text/decomposers/libreoffice-powerpoint.rb
    chupa-text-decomposer-libreoffice-powerpoint/test/run-test.rb
    chupa-text-decomposer-libreoffice-powerpoint/test/test-ppt.rb
    chupa-text-decomposer-libreoffice-word/lib/chupa-text/decomposers/libreoffice-word.rb
    chupa-text-decomposer-libreoffice-word/test/run-test.rb
    chupa-text-decomposer-libreoffice-word/test/test-doc.rb
    chupa-text-decomposer-libreoffice/Gemfile
    chupa-text-decomposer-libreoffice/chupa-text-decomposer-libreoffice.gemspec
    chupa-text-decomposer-libreoffice/lib/chupa-text/decomposers/libreoffice.rb
    chupa-text-decomposer-libreoffice/test/helper.rb
    chupa-text-decomposer-libreoffice/test/test-doc.rb
    chupa-text-decomposer-libreoffice/test/test-docx.rb
    chupa-text-decomposer-libreoffice/test/test-odp.rb
    chupa-text-decomposer-libreoffice/test/test-ods.rb
    chupa-text-decomposer-libreoffice/test/test-odt.rb
    chupa-text-decomposer-libreoffice/test/test-ppt.rb
    chupa-text-decomposer-libreoffice/test/test-pptx.rb
    chupa-text-decomposer-libreoffice/test/test-xls.rb
    chupa-text-decomposer-libreoffice/test/test-xlsx.rb

  Modified: chupa-text-decomposer-libreoffice-excel/lib/chupa-text/decomposers/libreoffice-excel.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-excel/lib/chupa-text/decomposers/libreoffice-excel.rb    2019-02-26 14:17:18 +0900 (0a19873)
+++ chupa-text-decomposer-libreoffice-excel/lib/chupa-text/decomposers/libreoffice-excel.rb    2019-02-26 14:54:14 +0900 (98406c7)
@@ -23,8 +23,8 @@ module ChupaText
 
       def initialize(options)
         super
-        @extension = "xls"
-        @mime_type = "application/vnd.ms-excel"
+        @extensions = ["xls"]
+        @mime_types = ["application/vnd.ms-excel"]
       end
 
       private

  Deleted: chupa-text-decomposer-libreoffice-excel/test/fixture/attributes.xls (+0 -24) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-excel/test/fixture/attributes.xls    2019-02-26 14:17:18 +0900 (fe1cd21)
+++ /dev/null
@@ -1,24 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.ms-excel
-uri: file:/tmp/y43Hob_attributes.xls
-path: /tmp/y43Hob_attributes.xls
-size: 5632
-Content-Type: multipart/mixed; boundary=e4236d7d474731c29d06d579ebdcf9db66530242
-
---e4236d7d474731c29d06d579ebdcf9db66530242
-mime-type: text/plain
-uri: file:/tmp/y43Hob_attributes.txt
-path: /tmp/y43Hob_attributes.txt
-size: 12
-source-mime-types: ["application/vnd.ms-excel"]
-name: Sheet1
-digest: b6906a8292c58517cf73036ad0ace729518f79b4
-size: 12
-first-row: 1
-last-row: 1
-first-column: A
-last-column: A
-
-"Sheet1 A1"
-
---e4236d7d474731c29d06d579ebdcf9db66530242--

  Deleted: chupa-text-decomposer-libreoffice-excel/test/fixture/multi-sheets.xls (+0 -59) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-excel/test/fixture/multi-sheets.xls    2019-02-26 14:17:18 +0900 (3c9fc6a)
+++ /dev/null
@@ -1,59 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.ms-excel
-uri: file:/tmp/sYicof_multi-sheets.xls
-path: /tmp/sYicof_multi-sheets.xls
-size: 6656
-Content-Type: multipart/mixed; boundary=ed6573ac3816bc70a1b8940b99f522c770c4945d
-
---ed6573ac3816bc70a1b8940b99f522c770c4945d
-mime-type: text/plain
-uri: file:/tmp/sYicof_multi-sheets.txt
-path: /tmp/sYicof_multi-sheets.txt
-size: 56
-source-mime-types: ["application/vnd.ms-excel"]
-name: Sheet1
-digest: 9011cb9443faf652afa3746acfae5c623ec5c7c9
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet1 - A1","Sheet1 - B1"
-"Sheet1 - A2","Sheet1 - B2"
-
---ed6573ac3816bc70a1b8940b99f522c770c4945d
-mime-type: text/plain
-uri: file:/tmp/sYicof_multi-sheets.txt
-path: /tmp/sYicof_multi-sheets.txt
-size: 56
-source-mime-types: ["application/vnd.ms-excel"]
-name: Sheet2
-digest: be99be632ea81d909f2d44b26e3dde28214cb126
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet2 - A1","Sheet2 - B1"
-"Sheet2 - A2","Sheet2 - B2"
-
---ed6573ac3816bc70a1b8940b99f522c770c4945d
-mime-type: text/plain
-uri: file:/tmp/sYicof_multi-sheets.txt
-path: /tmp/sYicof_multi-sheets.txt
-size: 56
-source-mime-types: ["application/vnd.ms-excel"]
-name: Sheet3
-digest: c03c013aba6e69703ba402967fc2a85cebbe9a28
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet3 - A1","Sheet3 - B1"
-"Sheet3 - A2","Sheet3 - B2"
-
---ed6573ac3816bc70a1b8940b99f522c770c4945d--

  Deleted: chupa-text-decomposer-libreoffice-excel/test/fixture/one-sheet.xls (+0 -25) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-excel/test/fixture/one-sheet.xls    2019-02-26 14:17:18 +0900 (b80e3ab)
+++ /dev/null
@@ -1,25 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.ms-excel
-uri: file:/tmp/tIwjYq_one-sheet.xls
-path: /tmp/tIwjYq_one-sheet.xls
-size: 5632
-Content-Type: multipart/mixed; boundary=3bbfd70c5cee667a6742dd95d599741b884e170b
-
---3bbfd70c5cee667a6742dd95d599741b884e170b
-mime-type: text/plain
-uri: file:/tmp/tIwjYq_one-sheet.txt
-path: /tmp/tIwjYq_one-sheet.txt
-size: 56
-source-mime-types: ["application/vnd.ms-excel"]
-name: Sheet1
-digest: 9011cb9443faf652afa3746acfae5c623ec5c7c9
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet1 - A1","Sheet1 - B1"
-"Sheet1 - A2","Sheet1 - B2"
-
---3bbfd70c5cee667a6742dd95d599741b884e170b--

  Modified: chupa-text-decomposer-libreoffice-excel/test/run-test.rb (+1 -1)
===================================================================
--- chupa-text-decomposer-libreoffice-excel/test/run-test.rb    2019-02-26 14:17:18 +0900 (6ebf1c3)
+++ chupa-text-decomposer-libreoffice-excel/test/run-test.rb    2019-02-26 14:54:14 +0900 (6d4a729)
@@ -26,6 +26,6 @@ require "chupa-text"
 
 ChupaText::Decomposers.load
 
-require_relative "../../chupa-text-decomposer-libreoffice-general/test/helper"
+require_relative "../../chupa-text-decomposer-libreoffice/test/helper"
 
 exit(Test::Unit::AutoRunner.run(true))

  Modified: chupa-text-decomposer-libreoffice-excel/test/test-xls.rb (+2 -115)
===================================================================
--- chupa-text-decomposer-libreoffice-excel/test/test-xls.rb    2019-02-26 14:17:18 +0900 (19a8bb8)
+++ chupa-text-decomposer-libreoffice-excel/test/test-xls.rb    2019-02-26 14:54:14 +0900 (93c1b01)
@@ -15,122 +15,9 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 class TestXls < Test::Unit::TestCase
+  include XlsTests
+
   def setup
     @decomposer = ChupaText::Decomposers::LibreOfficeExcel.new({})
   end
-
-  def fixture_path(*components)
-    base_path = Pathname(__dir__) + "fixture"
-    base_path.join(*components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_xls
-        assert_true(@decomposer.target?(create_data("document.xls")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_ms_excel
-        mime_type = "application/vnd.ms-excel"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Calc"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.xls")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one sheet") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-sheet.xls"))
-      end
-    end
-
-    sub_test_case("multi sheets") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-Sheet2 - A1
-Sheet2 - A2
-Sheet2 - B1
-Sheet2 - B2
-Sheet3 - A1
-Sheet3 - A2
-Sheet3 - B1
-Sheet3 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-sheets.xls"))
-      end
-    end
-  end
 end

  Modified: chupa-text-decomposer-libreoffice-general/lib/chupa-text/decomposers/libreoffice-general.rb (+3 -25)
===================================================================
--- chupa-text-decomposer-libreoffice-general/lib/chupa-text/decomposers/libreoffice-general.rb    2019-02-26 14:17:18 +0900 (fe05338)
+++ chupa-text-decomposer-libreoffice-general/lib/chupa-text/decomposers/libreoffice-general.rb    2019-02-26 14:54:14 +0900 (3ee4cc1)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014-2017  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -34,32 +34,10 @@ module ChupaText
         end
       end
 
-      TARGET_EXTENSIONS = [
-        "odt",
-        "ods",
-        "odp",
-        "doc",
-        "xls",
-        "ppt",
-        "docx",
-        "xlsx",
-        "pptx",
-      ]
-      TARGET_MIME_TYPES = [
-        "application/vnd.oasis.opendocument.text",
-        "application/vnd.oasis.opendocument.presentation",
-        "application/vnd.oasis.opendocument.spreadsheet",
-        "application/msword",
-        "application/vnd.ms-excel",
-        "application/vnd.ms-powerpoint",
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
-        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-        "application/vnd.openxmlformats-officedocument.presentationml.presentation",
-      ]
       def target?(data)
         return false if****@comma*****?
-        data.extension == @extension or
-          data.mime_type == @mime_type
+        @extensions.include?(data.extension) or
+          @mime_types.include?(data.mime_type)
       end
 
       def decompose(data)

  Deleted: chupa-text-decomposer-libreoffice-general/test/helper.rb (+0 -50) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-general/test/helper.rb    2019-02-26 14:17:18 +0900 (662329c)
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-require "pathname"
-
-module DecomposeHelper
-  def decompose(path)
-    data = ChupaText::InputData.new(path)
-
-    pdf_decomposer = ChupaText::Decomposers::PDF.new({})
-    decomposed = []
-    @decomposer.decompose(data) do |decomposed_data|
-      if pdf_decomposer.target?(decomposed_data)
-        pdf_decomposer.decompose(decomposed_data) do |pdf_decomposed_data|
-          decomposed << pdf_decomposed_data
-        end
-      else
-        decomposed << decomposed_data
-      end
-    end
-    decomposed
-  end
-
-  def normalize_producers(producers)
-    producers.collect do |producer|
-      normalize_producer(producer)
-    end
-  end
-
-  def normalize_producer(producer)
-    if /\ALibreOffice \d\.\d\z/ =~ producer
-      "LibreOffice X.Y"
-    else
-      producer
-    end
-  end
-end

  Modified: chupa-text-decomposer-libreoffice-office-open-xml-document/lib/chupa-text/decomposers/libreoffice-office-open-xml-document.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-document/lib/chupa-text/decomposers/libreoffice-office-open-xml-document.rb    2019-02-26 14:17:18 +0900 (67967cd)
+++ chupa-text-decomposer-libreoffice-office-open-xml-document/lib/chupa-text/decomposers/libreoffice-office-open-xml-document.rb    2019-02-26 14:54:14 +0900 (4b160fb)
@@ -23,8 +23,8 @@ module ChupaText
 
       def initialize(options)
         super
-        @extension = "docx"
-        @mime_type = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
+        @extensions = ["docx"]
+        @mime_types = ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"]
       end
 
       private

  Deleted: chupa-text-decomposer-libreoffice-office-open-xml-document/test/fixture/attributes.docx (+0 -23) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-document/test/fixture/attributes.docx    2019-02-26 14:17:18 +0900 (601c30d)
+++ /dev/null
@@ -1,23 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
-uri: file:/tmp/u6q8uC_attributes.docx
-path: /tmp/u6q8uC_attributes.docx
-size: 3937
-Content-Type: multipart/mixed; boundary=b52f8717b0f13056cc5a6a694d01507e7b6e8d22
-
---b52f8717b0f13056cc5a6a694d01507e7b6e8d22
-mime-type: text/plain
-uri: file:/tmp/u6q8uC_attributes.txt
-path: /tmp/u6q8uC_attributes.txt
-size: 6
-title: Title
-created_time: 2019-02-26 05:54:41 UTC
-source-mime-types: ["application/pdf", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"]
-subject: Subject
-keywords: Keyword1, Keyword2
-creator: Writer
-producer: LibreOffice 5.2
-
-Page1
-
---b52f8717b0f13056cc5a6a694d01507e7b6e8d22--

  Deleted: chupa-text-decomposer-libreoffice-office-open-xml-document/test/fixture/multi-pages.docx (+0 -21) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-document/test/fixture/multi-pages.docx    2019-02-26 14:17:18 +0900 (14b17ff)
+++ /dev/null
@@ -1,21 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
-uri: file:/tmp/B5gmUe_multi-pages.docx
-path: /tmp/B5gmUe_multi-pages.docx
-size: 3889
-Content-Type: multipart/mixed; boundary=b9d57f42347d3add445efcbc9c8b538f03a67f33
-
---b9d57f42347d3add445efcbc9c8b538f03a67f33
-mime-type: text/plain
-uri: file:/tmp/B5gmUe_multi-pages.txt
-path: /tmp/B5gmUe_multi-pages.txt
-size: 12
-created_time: 2019-02-26 05:54:43 UTC
-source-mime-types: ["application/pdf", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"]
-creator: Writer
-producer: LibreOffice 5.2
-
-Page1
-Page2
-
---b9d57f42347d3add445efcbc9c8b538f03a67f33--

  Deleted: chupa-text-decomposer-libreoffice-office-open-xml-document/test/fixture/one-page.docx (+0 -20) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-document/test/fixture/one-page.docx    2019-02-26 14:17:18 +0900 (0b90a5e)
+++ /dev/null
@@ -1,20 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
-uri: file:/tmp/Hb7JhJ_one-page.docx
-path: /tmp/Hb7JhJ_one-page.docx
-size: 3871
-Content-Type: multipart/mixed; boundary=3704f2ec3bef6be26e60f54478574eaafc006d8c
-
---3704f2ec3bef6be26e60f54478574eaafc006d8c
-mime-type: text/plain
-uri: file:/tmp/Hb7JhJ_one-page.txt
-path: /tmp/Hb7JhJ_one-page.txt
-size: 6
-created_time: 2019-02-26 05:54:44 UTC
-source-mime-types: ["application/pdf", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"]
-creator: Writer
-producer: LibreOffice 5.2
-
-Page1
-
---3704f2ec3bef6be26e60f54478574eaafc006d8c--

  Modified: chupa-text-decomposer-libreoffice-office-open-xml-document/test/run-test.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-document/test/run-test.rb    2019-02-26 14:17:18 +0900 (6ebf1c3)
+++ chupa-text-decomposer-libreoffice-office-open-xml-document/test/run-test.rb    2019-02-26 14:54:14 +0900 (be67c7d)
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 #
-# Copyright (C) 2013  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2013-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,6 @@ require "chupa-text"
 
 ChupaText::Decomposers.load
 
-require_relative "../../chupa-text-decomposer-libreoffice-general/test/helper"
+require_relative "../../chupa-text-decomposer-libreoffice/test/helper"
 
 exit(Test::Unit::AutoRunner.run(true))

  Modified: chupa-text-decomposer-libreoffice-office-open-xml-document/test/test-docx.rb (+3 -101)
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-document/test/test-docx.rb    2019-02-26 14:17:18 +0900 (a420f34)
+++ chupa-text-decomposer-libreoffice-office-open-xml-document/test/test-docx.rb    2019-02-26 14:54:14 +0900 (b41f160)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -15,107 +15,9 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 class TestDocx < Test::Unit::TestCase
+  include DocxTests
+
   def setup
     @decomposer = ChupaText::Decomposers::LibreOfficeOfficeOpenXMLDocument.new({})
   end
-
-  def fixture_path(*components)
-    base_path = Pathname(__dir__) + "fixture"
-    base_path.join(*components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_docx
-        assert_true(@decomposer.target?(create_data("document.docx")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_openxml_document
-        mime_type = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Writer"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.docx")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one page") do
-      def test_body
-        assert_equal(["Page1\n"], decompose.collect(&:body))
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-page.docx"))
-      end
-    end
-
-    sub_test_case("multi pages") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Page1
-Page2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-pages.docx"))
-      end
-    end
-  end
 end

  Modified: chupa-text-decomposer-libreoffice-office-open-xml-presentation/lib/chupa-text/decomposers/libreoffice-office-open-xml-presentation.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-presentation/lib/chupa-text/decomposers/libreoffice-office-open-xml-presentation.rb    2019-02-26 14:17:18 +0900 (222b587)
+++ chupa-text-decomposer-libreoffice-office-open-xml-presentation/lib/chupa-text/decomposers/libreoffice-office-open-xml-presentation.rb    2019-02-26 14:54:14 +0900 (cd2bbc3)
@@ -23,8 +23,8 @@ module ChupaText
 
       def initialize(options)
         super
-        @extension = "pptx"
-        @mime_type = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
+        @extensions = ["pptx"]
+        @mime_types = ["application/vnd.openxmlformats-officedocument.presentationml.presentation"]
       end
 
       private

  Deleted: chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/fixture/attributes.pptx (+0 -23) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/fixture/attributes.pptx    2019-02-26 14:17:18 +0900 (db82ab6)
+++ /dev/null
@@ -1,23 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.openxmlformats-officedocument.presentationml.presentation
-uri: file:/tmp/POFOfb_attributes.pptx
-path: /tmp/POFOfb_attributes.pptx
-size: 20658
-Content-Type: multipart/mixed; boundary=83d4f7a9605d0bc8bcec8ed01fead215da700a9e
-
---83d4f7a9605d0bc8bcec8ed01fead215da700a9e
-mime-type: text/plain
-uri: file:/tmp/POFOfb_attributes.txt
-path: /tmp/POFOfb_attributes.txt
-size: 6
-title: Title
-created_time: 2019-02-26 05:54:46 UTC
-source-mime-types: ["application/pdf", "application/vnd.openxmlformats-officedocument.presentationml.presentation"]
-subject: Subject
-keywords: Keyword1, Keyword2
-creator: Impress
-producer: LibreOffice 5.2
-
-Title
-
---83d4f7a9605d0bc8bcec8ed01fead215da700a9e--

  Deleted: chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/fixture/multi-slides.pptx (+0 -25) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/fixture/multi-slides.pptx    2019-02-26 14:17:18 +0900 (ad7fc0b)
+++ /dev/null
@@ -1,25 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.openxmlformats-officedocument.presentationml.presentation
-uri: file:/tmp/GzqH6U_multi-slides.pptx
-path: /tmp/GzqH6U_multi-slides.pptx
-size: 64545
-Content-Type: multipart/mixed; boundary=673ad536ea6c0b6aceeb8b96c52a551d724df40a
-
---673ad536ea6c0b6aceeb8b96c52a551d724df40a
-mime-type: text/plain
-uri: file:/tmp/GzqH6U_multi-slides.txt
-path: /tmp/GzqH6U_multi-slides.txt
-size: 84
-created_time: 2019-02-26 05:54:48 UTC
-source-mime-types: ["application/pdf", "application/vnd.openxmlformats-officedocument.presentationml.presentation"]
-creator: Impress
-producer: LibreOffice 5.2
-
-Slide1 title
-Slide1 content
-Slide2 title
-Slide2 content
-Slide3 title
-Slide3 content
-
---673ad536ea6c0b6aceeb8b96c52a551d724df40a--

  Deleted: chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/fixture/one-slide.pptx (+0 -21) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/fixture/one-slide.pptx    2019-02-26 14:17:18 +0900 (8ad2c0a)
+++ /dev/null
@@ -1,21 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.openxmlformats-officedocument.presentationml.presentation
-uri: file:/tmp/kRRTXu_one-slide.pptx
-path: /tmp/kRRTXu_one-slide.pptx
-size: 32488
-Content-Type: multipart/mixed; boundary=33ad64995943c3806a5f7aee4921657e0bc24065
-
---33ad64995943c3806a5f7aee4921657e0bc24065
-mime-type: text/plain
-uri: file:/tmp/kRRTXu_one-slide.txt
-path: /tmp/kRRTXu_one-slide.txt
-size: 28
-created_time: 2019-02-26 05:54:49 UTC
-source-mime-types: ["application/pdf", "application/vnd.openxmlformats-officedocument.presentationml.presentation"]
-creator: Impress
-producer: LibreOffice 5.2
-
-Slide1 title
-Slide1 content
-
---33ad64995943c3806a5f7aee4921657e0bc24065--

  Modified: chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/run-test.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/run-test.rb    2019-02-26 14:17:18 +0900 (6ebf1c3)
+++ chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/run-test.rb    2019-02-26 14:54:14 +0900 (be67c7d)
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 #
-# Copyright (C) 2013  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2013-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,6 @@ require "chupa-text"
 
 ChupaText::Decomposers.load
 
-require_relative "../../chupa-text-decomposer-libreoffice-general/test/helper"
+require_relative "../../chupa-text-decomposer-libreoffice/test/helper"
 
 exit(Test::Unit::AutoRunner.run(true))

  Modified: chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/test-pptx.rb (+3 -108)
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/test-pptx.rb    2019-02-26 14:17:18 +0900 (fcaf31e)
+++ chupa-text-decomposer-libreoffice-office-open-xml-presentation/test/test-pptx.rb    2019-02-26 14:54:14 +0900 (780ff06)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -15,114 +15,9 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 class TestPptx < Test::Unit::TestCase
+  include PptxTests
+
   def setup
     @decomposer = ChupaText::Decomposers::LibreOfficeOfficeOpenXMLPresentation.new({})
   end
-
-  def fixture_path(*components)
-    base_path = Pathname(__dir__) + "fixture"
-    base_path.join(*components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_pptx
-        assert_true(@decomposer.target?(create_data("slide.pptx")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_openxml_presentation
-        mime_type = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Impress"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.pptx")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one slide") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-slide.pptx"))
-      end
-    end
-
-    sub_test_case("multi slides") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-Slide2 title
-Slide2 content
-Slide3 title
-Slide3 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-slides.pptx"))
-      end
-    end
-  end
 end

  Modified: chupa-text-decomposer-libreoffice-office-open-xml-workbook/lib/chupa-text/decomposers/libreoffice-office-open-xml-workbook.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-workbook/lib/chupa-text/decomposers/libreoffice-office-open-xml-workbook.rb    2019-02-26 14:17:18 +0900 (72891bb)
+++ chupa-text-decomposer-libreoffice-office-open-xml-workbook/lib/chupa-text/decomposers/libreoffice-office-open-xml-workbook.rb    2019-02-26 14:54:14 +0900 (f932108)
@@ -23,8 +23,8 @@ module ChupaText
 
       def initialize(options)
         super
-        @extension = "xlsx"
-        @mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+        @extensions = ["xlsx"]
+        @mime_types = ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
       end
 
       private

  Deleted: chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/fixture/attributes.xlsx (+0 -24) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/fixture/attributes.xlsx    2019-02-26 14:17:18 +0900 (067cecc)
+++ /dev/null
@@ -1,24 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
-uri: file:/tmp/tHoqgb_attributes.xlsx
-path: /tmp/tHoqgb_attributes.xlsx
-size: 4610
-Content-Type: multipart/mixed; boundary=a134bf8ec4b3809f8a42f79f5f9630ef04b3c748
-
---a134bf8ec4b3809f8a42f79f5f9630ef04b3c748
-mime-type: text/plain
-uri: file:/tmp/tHoqgb_attributes.txt
-path: /tmp/tHoqgb_attributes.txt
-size: 12
-source-mime-types: ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
-name: Sheet1
-digest: b6906a8292c58517cf73036ad0ace729518f79b4
-size: 12
-first-row: 1
-last-row: 1
-first-column: A
-last-column: A
-
-"Sheet1 A1"
-
---a134bf8ec4b3809f8a42f79f5f9630ef04b3c748--

  Deleted: chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/fixture/multi-sheets.xlsx (+0 -59) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/fixture/multi-sheets.xlsx    2019-02-26 14:17:18 +0900 (d4161ba)
+++ /dev/null
@@ -1,59 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
-uri: file:/tmp/t2L43b_multi-sheets.xlsx
-path: /tmp/t2L43b_multi-sheets.xlsx
-size: 6643
-Content-Type: multipart/mixed; boundary=fadcb4169e73d6354050378a7c8c3171861ee01b
-
---fadcb4169e73d6354050378a7c8c3171861ee01b
-mime-type: text/plain
-uri: file:/tmp/t2L43b_multi-sheets.txt
-path: /tmp/t2L43b_multi-sheets.txt
-size: 56
-source-mime-types: ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
-name: Sheet1
-digest: 9011cb9443faf652afa3746acfae5c623ec5c7c9
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet1 - A1","Sheet1 - B1"
-"Sheet1 - A2","Sheet1 - B2"
-
---fadcb4169e73d6354050378a7c8c3171861ee01b
-mime-type: text/plain
-uri: file:/tmp/t2L43b_multi-sheets.txt
-path: /tmp/t2L43b_multi-sheets.txt
-size: 56
-source-mime-types: ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
-name: Sheet2
-digest: be99be632ea81d909f2d44b26e3dde28214cb126
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet2 - A1","Sheet2 - B1"
-"Sheet2 - A2","Sheet2 - B2"
-
---fadcb4169e73d6354050378a7c8c3171861ee01b
-mime-type: text/plain
-uri: file:/tmp/t2L43b_multi-sheets.txt
-path: /tmp/t2L43b_multi-sheets.txt
-size: 56
-source-mime-types: ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
-name: Sheet3
-digest: c03c013aba6e69703ba402967fc2a85cebbe9a28
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet3 - A1","Sheet3 - B1"
-"Sheet3 - A2","Sheet3 - B2"
-
---fadcb4169e73d6354050378a7c8c3171861ee01b--

  Deleted: chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/fixture/one-sheet.xlsx (+0 -25) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/fixture/one-sheet.xlsx    2019-02-26 14:17:18 +0900 (9a94623)
+++ /dev/null
@@ -1,25 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
-uri: file:/tmp/6Tr5nr_one-sheet.xlsx
-path: /tmp/6Tr5nr_one-sheet.xlsx
-size: 4593
-Content-Type: multipart/mixed; boundary=7b10f39f50ffa829e0882f2f27a4d0675449e674
-
---7b10f39f50ffa829e0882f2f27a4d0675449e674
-mime-type: text/plain
-uri: file:/tmp/6Tr5nr_one-sheet.txt
-path: /tmp/6Tr5nr_one-sheet.txt
-size: 56
-source-mime-types: ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
-name: Sheet1
-digest: 9011cb9443faf652afa3746acfae5c623ec5c7c9
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet1 - A1","Sheet1 - B1"
-"Sheet1 - A2","Sheet1 - B2"
-
---7b10f39f50ffa829e0882f2f27a4d0675449e674--

  Modified: chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/run-test.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/run-test.rb    2019-02-26 14:17:18 +0900 (6ebf1c3)
+++ chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/run-test.rb    2019-02-26 14:54:14 +0900 (be67c7d)
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 #
-# Copyright (C) 2013  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2013-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,6 @@ require "chupa-text"
 
 ChupaText::Decomposers.load
 
-require_relative "../../chupa-text-decomposer-libreoffice-general/test/helper"
+require_relative "../../chupa-text-decomposer-libreoffice/test/helper"
 
 exit(Test::Unit::AutoRunner.run(true))

  Modified: chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/test-xlsx.rb (+3 -116)
===================================================================
--- chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/test-xlsx.rb    2019-02-26 14:17:18 +0900 (1342e34)
+++ chupa-text-decomposer-libreoffice-office-open-xml-workbook/test/test-xlsx.rb    2019-02-26 14:54:14 +0900 (943dca0)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -15,122 +15,9 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 class TestXlsx < Test::Unit::TestCase
+  include XlsxTests
+
   def setup
     @decomposer = ChupaText::Decomposers::LibreOfficeOfficeOpenXMLWorkbook.new({})
   end
-
-  def fixture_path(*components)
-    base_path = Pathname(__dir__) + "fixture"
-    base_path.join(*components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_xlsx
-        assert_true(@decomposer.target?(create_data("workbook.xlsx")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_openxml_sheet
-        mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Calc"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.xlsx")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one sheet") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-sheet.xlsx"))
-      end
-    end
-
-    sub_test_case("multi sheets") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-Sheet2 - A1
-Sheet2 - A2
-Sheet2 - B1
-Sheet2 - B2
-Sheet3 - A1
-Sheet3 - A2
-Sheet3 - B1
-Sheet3 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-sheets.xlsx"))
-      end
-    end
-  end
 end

  Modified: chupa-text-decomposer-libreoffice-opendocument-presentation/lib/chupa-text/decomposers/libreoffice-opendocument-presentation.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-presentation/lib/chupa-text/decomposers/libreoffice-opendocument-presentation.rb    2019-02-26 14:17:18 +0900 (ff3e6a2)
+++ chupa-text-decomposer-libreoffice-opendocument-presentation/lib/chupa-text/decomposers/libreoffice-opendocument-presentation.rb    2019-02-26 14:54:14 +0900 (eda8656)
@@ -23,8 +23,8 @@ module ChupaText
 
       def initialize(options)
         super
-        @extension = "odp"
-        @mime_type = "application/vnd.oasis.opendocument.presentation"
+        @extensions = ["odp"]
+        @mime_types = ["application/vnd.oasis.opendocument.presentation"]
       end
 
       private

  Deleted: chupa-text-decomposer-libreoffice-opendocument-presentation/test/fixture/attributes.odp (+0 -23) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-presentation/test/fixture/attributes.odp    2019-02-26 14:17:18 +0900 (5a528e6)
+++ /dev/null
@@ -1,23 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.oasis.opendocument.presentation
-uri: file:/tmp/jIatsC_attributes.odp
-path: /tmp/jIatsC_attributes.odp
-size: 12808
-Content-Type: multipart/mixed; boundary=accecf2c60ac78ace4da6e100b715b4e8a94802a
-
---accecf2c60ac78ace4da6e100b715b4e8a94802a
-mime-type: text/plain
-uri: file:/tmp/jIatsC_attributes.txt
-path: /tmp/jIatsC_attributes.txt
-size: 6
-title: Title
-created_time: 2019-02-26 05:54:54 UTC
-source-mime-types: ["application/pdf", "application/vnd.oasis.opendocument.presentation"]
-subject: Subject
-keywords: Keyword1, Keyword2
-creator: Impress
-producer: LibreOffice 5.2
-
-Title
-
---accecf2c60ac78ace4da6e100b715b4e8a94802a--

  Deleted: chupa-text-decomposer-libreoffice-opendocument-presentation/test/fixture/multi-slides.odp (+0 -27) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-presentation/test/fixture/multi-slides.odp    2019-02-26 14:17:18 +0900 (3fc6087)
+++ /dev/null
@@ -1,27 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.oasis.opendocument.presentation
-uri: file:/tmp/lMkzrd_multi-slides.odp
-path: /tmp/lMkzrd_multi-slides.odp
-size: 13509
-Content-Type: multipart/mixed; boundary=fc02333b77c6e6a9ced71d62b527264dbf19f4c5
-
---fc02333b77c6e6a9ced71d62b527264dbf19f4c5
-mime-type: text/plain
-uri: file:/tmp/lMkzrd_multi-slides.txt
-path: /tmp/lMkzrd_multi-slides.txt
-size: 92
-created_time: 2019-02-26 05:54:56 UTC
-source-mime-types: ["application/pdf", "application/vnd.oasis.opendocument.presentation"]
-creator: Impress
-producer: LibreOffice 5.2
-
-Slide1 title
-Slide1 content
-Slide2 title
-●
-Slide2 content
-Slide3 title
-●
-Slide3 content
-
---fc02333b77c6e6a9ced71d62b527264dbf19f4c5--

  Deleted: chupa-text-decomposer-libreoffice-opendocument-presentation/test/fixture/one-slide.odp (+0 -21) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-presentation/test/fixture/one-slide.odp    2019-02-26 14:17:18 +0900 (d82d1b0)
+++ /dev/null
@@ -1,21 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.oasis.opendocument.presentation
-uri: file:/tmp/ym86x6_one-slide.odp
-path: /tmp/ym86x6_one-slide.odp
-size: 11868
-Content-Type: multipart/mixed; boundary=1b3b32f519c9c06c498efce8505acbd53e551e06
-
---1b3b32f519c9c06c498efce8505acbd53e551e06
-mime-type: text/plain
-uri: file:/tmp/ym86x6_one-slide.txt
-path: /tmp/ym86x6_one-slide.txt
-size: 28
-created_time: 2019-02-26 05:54:58 UTC
-source-mime-types: ["application/pdf", "application/vnd.oasis.opendocument.presentation"]
-creator: Impress
-producer: LibreOffice 5.2
-
-Slide1 title
-Slide1 content
-
---1b3b32f519c9c06c498efce8505acbd53e551e06--

  Modified: chupa-text-decomposer-libreoffice-opendocument-presentation/test/run-test.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-presentation/test/run-test.rb    2019-02-26 14:17:18 +0900 (6ebf1c3)
+++ chupa-text-decomposer-libreoffice-opendocument-presentation/test/run-test.rb    2019-02-26 14:54:14 +0900 (be67c7d)
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 #
-# Copyright (C) 2013  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2013-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,6 @@ require "chupa-text"
 
 ChupaText::Decomposers.load
 
-require_relative "../../chupa-text-decomposer-libreoffice-general/test/helper"
+require_relative "../../chupa-text-decomposer-libreoffice/test/helper"
 
 exit(Test::Unit::AutoRunner.run(true))

  Modified: chupa-text-decomposer-libreoffice-opendocument-presentation/test/test-odp.rb (+3 -112)
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-presentation/test/test-odp.rb    2019-02-26 14:17:18 +0900 (1ba6948)
+++ chupa-text-decomposer-libreoffice-opendocument-presentation/test/test-odp.rb    2019-02-26 14:54:14 +0900 (dec8c4c)
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -17,116 +15,9 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 class TestOdp < Test::Unit::TestCase
+  include OdpTests
+
   def setup
     @decomposer = ChupaText::Decomposers::LibreOfficeOpenDocumentPresentation.new({})
   end
-
-  def fixture_path(*components)
-    base_path = Pathname(__dir__) + "fixture"
-    base_path.join(*components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_odp
-        assert_true(@decomposer.target?(create_data("slide.odp")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_opendocument_presentation
-        mime_type = "application/vnd.oasis.opendocument.presentation"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Impress"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.odp")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one slide") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-slide.odp"))
-      end
-    end
-
-    sub_test_case("multi slides") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-Slide2 title
-●
-Slide2 content
-Slide3 title
-●
-Slide3 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-slides.odp"))
-      end
-    end
-  end
 end

  Modified: chupa-text-decomposer-libreoffice-opendocument-spreadsheet/lib/chupa-text/decomposers/libreoffice-opendocument-spreadsheet.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-spreadsheet/lib/chupa-text/decomposers/libreoffice-opendocument-spreadsheet.rb    2019-02-26 14:17:18 +0900 (23ca0a3)
+++ chupa-text-decomposer-libreoffice-opendocument-spreadsheet/lib/chupa-text/decomposers/libreoffice-opendocument-spreadsheet.rb    2019-02-26 14:54:14 +0900 (a23a635)
@@ -23,8 +23,8 @@ module ChupaText
 
       def initialize(options)
         super
-        @extension = "ods"
-        @mime_type = "application/vnd.oasis.opendocument.spreadsheet"
+        @extensions = ["ods"]
+        @mime_types = ["application/vnd.oasis.opendocument.spreadsheet"]
       end
 
       private

  Deleted: chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/fixture/attributes.ods (+0 -24) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/fixture/attributes.ods    2019-02-26 14:17:18 +0900 (7068c11)
+++ /dev/null
@@ -1,24 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.oasis.opendocument.spreadsheet
-uri: file:/tmp/OhsAIP_attributes.ods
-path: /tmp/OhsAIP_attributes.ods
-size: 8368
-Content-Type: multipart/mixed; boundary=3ef3055c6f898c5a4b769828715bf610d7e16725
-
---3ef3055c6f898c5a4b769828715bf610d7e16725
-mime-type: text/plain
-uri: file:/tmp/OhsAIP_attributes.txt
-path: /tmp/OhsAIP_attributes.txt
-size: 12
-source-mime-types: ["application/vnd.oasis.opendocument.spreadsheet"]
-name: Sheet1
-digest: b6906a8292c58517cf73036ad0ace729518f79b4
-size: 12
-first-row: 1
-last-row: 1
-first-column: A
-last-column: A
-
-"Sheet1 A1"
-
---3ef3055c6f898c5a4b769828715bf610d7e16725--

  Deleted: chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/fixture/multi-sheets.ods (+0 -59) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/fixture/multi-sheets.ods    2019-02-26 14:17:18 +0900 (107b73d)
+++ /dev/null
@@ -1,59 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.oasis.opendocument.spreadsheet
-uri: file:/tmp/h0Hex5_multi-sheets.ods
-path: /tmp/h0Hex5_multi-sheets.ods
-size: 8773
-Content-Type: multipart/mixed; boundary=c2f076174cb009d815eee69bf4aa3da7542185a3
-
---c2f076174cb009d815eee69bf4aa3da7542185a3
-mime-type: text/plain
-uri: file:/tmp/h0Hex5_multi-sheets.txt
-path: /tmp/h0Hex5_multi-sheets.txt
-size: 56
-source-mime-types: ["application/vnd.oasis.opendocument.spreadsheet"]
-name: Sheet1
-digest: 9011cb9443faf652afa3746acfae5c623ec5c7c9
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet1 - A1","Sheet1 - B1"
-"Sheet1 - A2","Sheet1 - B2"
-
---c2f076174cb009d815eee69bf4aa3da7542185a3
-mime-type: text/plain
-uri: file:/tmp/h0Hex5_multi-sheets.txt
-path: /tmp/h0Hex5_multi-sheets.txt
-size: 56
-source-mime-types: ["application/vnd.oasis.opendocument.spreadsheet"]
-name: Sheet2
-digest: be99be632ea81d909f2d44b26e3dde28214cb126
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet2 - A1","Sheet2 - B1"
-"Sheet2 - A2","Sheet2 - B2"
-
---c2f076174cb009d815eee69bf4aa3da7542185a3
-mime-type: text/plain
-uri: file:/tmp/h0Hex5_multi-sheets.txt
-path: /tmp/h0Hex5_multi-sheets.txt
-size: 56
-source-mime-types: ["application/vnd.oasis.opendocument.spreadsheet"]
-name: Sheet3
-digest: c03c013aba6e69703ba402967fc2a85cebbe9a28
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet3 - A1","Sheet3 - B1"
-"Sheet3 - A2","Sheet3 - B2"
-
---c2f076174cb009d815eee69bf4aa3da7542185a3--

  Deleted: chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/fixture/one-sheet.ods (+0 -25) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/fixture/one-sheet.ods    2019-02-26 14:17:18 +0900 (41336a6)
+++ /dev/null
@@ -1,25 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.oasis.opendocument.spreadsheet
-uri: file:/tmp/P6uk8n_one-sheet.ods
-path: /tmp/P6uk8n_one-sheet.ods
-size: 8695
-Content-Type: multipart/mixed; boundary=022e0d7c4b30aa932285a0e4d41e18340e4571f9
-
---022e0d7c4b30aa932285a0e4d41e18340e4571f9
-mime-type: text/plain
-uri: file:/tmp/P6uk8n_one-sheet.txt
-path: /tmp/P6uk8n_one-sheet.txt
-size: 56
-source-mime-types: ["application/vnd.oasis.opendocument.spreadsheet"]
-name: Sheet1
-digest: 9011cb9443faf652afa3746acfae5c623ec5c7c9
-size: 56
-first-row: 1
-last-row: 2
-first-column: A
-last-column: B
-
-"Sheet1 - A1","Sheet1 - B1"
-"Sheet1 - A2","Sheet1 - B2"
-
---022e0d7c4b30aa932285a0e4d41e18340e4571f9--

  Modified: chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/run-test.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/run-test.rb    2019-02-26 14:17:18 +0900 (6ebf1c3)
+++ chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/run-test.rb    2019-02-26 14:54:14 +0900 (be67c7d)
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 #
-# Copyright (C) 2013  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2013-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,6 @@ require "chupa-text"
 
 ChupaText::Decomposers.load
 
-require_relative "../../chupa-text-decomposer-libreoffice-general/test/helper"
+require_relative "../../chupa-text-decomposer-libreoffice/test/helper"
 
 exit(Test::Unit::AutoRunner.run(true))

  Modified: chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/test-ods.rb (+2 -115)
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/test-ods.rb    2019-02-26 14:17:18 +0900 (b66f1bb)
+++ chupa-text-decomposer-libreoffice-opendocument-spreadsheet/test/test-ods.rb    2019-02-26 14:54:14 +0900 (8dae9e5)
@@ -15,122 +15,9 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 class TestOds < Test::Unit::TestCase
+  include OdsTests
+
   def setup
     @decomposer = ChupaText::Decomposers::LibreOfficeOpenDocumentSpreadsheet.new({})
   end
-
-  def fixture_path(*components)
-    base_path = Pathname(__dir__) + "fixture"
-    base_path.join(*components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_ods
-        assert_true(@decomposer.target?(create_data("document.ods")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_opendocument_spareadsheet
-        mime_type = "application/vnd.oasis.opendocument.spreadsheet"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Calc"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.ods")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one sheet") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-sheet.ods"))
-      end
-    end
-
-    sub_test_case("multi sheets") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-Sheet2 - A1
-Sheet2 - A2
-Sheet2 - B1
-Sheet2 - B2
-Sheet3 - A1
-Sheet3 - A2
-Sheet3 - B1
-Sheet3 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-sheets.ods"))
-      end
-    end
-  end
 end

  Modified: chupa-text-decomposer-libreoffice-opendocument-text/lib/chupa-text/decomposers/libreoffice-opendocument-text.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-text/lib/chupa-text/decomposers/libreoffice-opendocument-text.rb    2019-02-26 14:17:18 +0900 (ba737d8)
+++ chupa-text-decomposer-libreoffice-opendocument-text/lib/chupa-text/decomposers/libreoffice-opendocument-text.rb    2019-02-26 14:54:14 +0900 (0f5f756)
@@ -23,8 +23,8 @@ module ChupaText
 
       def initialize(options)
         super
-        @extension = "odt"
-        @mime_type = "application/vnd.oasis.opendocument.text"
+        @extensions = ["odt"]
+        @mime_types = ["application/vnd.oasis.opendocument.text"]
       end
 
       private

  Deleted: chupa-text-decomposer-libreoffice-opendocument-text/test/fixture/attributes.odt (+0 -23) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-text/test/fixture/attributes.odt    2019-02-26 14:17:18 +0900 (e556230)
+++ /dev/null
@@ -1,23 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.oasis.opendocument.text
-uri: file:/tmp/8RkPZB_attributes.odt
-path: /tmp/8RkPZB_attributes.odt
-size: 7762
-Content-Type: multipart/mixed; boundary=6a8d44520df7b95ce9588dc84db98cd41b93e938
-
---6a8d44520df7b95ce9588dc84db98cd41b93e938
-mime-type: text/plain
-uri: file:/tmp/8RkPZB_attributes.txt
-path: /tmp/8RkPZB_attributes.txt
-size: 6
-title: Title
-created_time: 2019-02-26 05:55:03 UTC
-source-mime-types: ["application/pdf", "application/vnd.oasis.opendocument.text"]
-subject: Subject
-keywords: Keyword1, Keyword2
-creator: Writer
-producer: LibreOffice 5.2
-
-Page1
-
---6a8d44520df7b95ce9588dc84db98cd41b93e938--

  Deleted: chupa-text-decomposer-libreoffice-opendocument-text/test/fixture/multi-pages.odt (+0 -21) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-text/test/fixture/multi-pages.odt    2019-02-26 14:17:18 +0900 (c21c3ca)
+++ /dev/null
@@ -1,21 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.oasis.opendocument.text
-uri: file:/tmp/2Pjo4a_multi-pages.odt
-path: /tmp/2Pjo4a_multi-pages.odt
-size: 7874
-Content-Type: multipart/mixed; boundary=1f9a7580d94d2d057bf8fc41ac0a91464d717bed
-
---1f9a7580d94d2d057bf8fc41ac0a91464d717bed
-mime-type: text/plain
-uri: file:/tmp/2Pjo4a_multi-pages.txt
-path: /tmp/2Pjo4a_multi-pages.txt
-size: 12
-created_time: 2019-02-26 05:55:05 UTC
-source-mime-types: ["application/pdf", "application/vnd.oasis.opendocument.text"]
-creator: Writer
-producer: LibreOffice 5.2
-
-Page1
-Page2
-
---1f9a7580d94d2d057bf8fc41ac0a91464d717bed--

  Deleted: chupa-text-decomposer-libreoffice-opendocument-text/test/fixture/one-page.odt (+0 -20) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-text/test/fixture/one-page.odt    2019-02-26 14:17:18 +0900 (a77f44e)
+++ /dev/null
@@ -1,20 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.oasis.opendocument.text
-uri: file:/tmp/9U8510_one-page.odt
-path: /tmp/9U8510_one-page.odt
-size: 7662
-Content-Type: multipart/mixed; boundary=770b7c865027c0c66f2ef83fcd67785a2da374fe
-
---770b7c865027c0c66f2ef83fcd67785a2da374fe
-mime-type: text/plain
-uri: file:/tmp/9U8510_one-page.txt
-path: /tmp/9U8510_one-page.txt
-size: 6
-created_time: 2019-02-26 05:55:07 UTC
-source-mime-types: ["application/pdf", "application/vnd.oasis.opendocument.text"]
-creator: Writer
-producer: LibreOffice 5.2
-
-Page1
-
---770b7c865027c0c66f2ef83fcd67785a2da374fe--

  Modified: chupa-text-decomposer-libreoffice-opendocument-text/test/run-test.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-text/test/run-test.rb    2019-02-26 14:17:18 +0900 (6ebf1c3)
+++ chupa-text-decomposer-libreoffice-opendocument-text/test/run-test.rb    2019-02-26 14:54:14 +0900 (be67c7d)
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 #
-# Copyright (C) 2013  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2013-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,6 @@ require "chupa-text"
 
 ChupaText::Decomposers.load
 
-require_relative "../../chupa-text-decomposer-libreoffice-general/test/helper"
+require_relative "../../chupa-text-decomposer-libreoffice/test/helper"
 
 exit(Test::Unit::AutoRunner.run(true))

  Modified: chupa-text-decomposer-libreoffice-opendocument-text/test/test-odt.rb (+2 -100)
===================================================================
--- chupa-text-decomposer-libreoffice-opendocument-text/test/test-odt.rb    2019-02-26 14:17:18 +0900 (3d3d3d1)
+++ chupa-text-decomposer-libreoffice-opendocument-text/test/test-odt.rb    2019-02-26 14:54:14 +0900 (66f3015)
@@ -15,107 +15,9 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 class TestOdt < Test::Unit::TestCase
+  include OdtTests
+
   def setup
     @decomposer = ChupaText::Decomposers::LibreOfficeOpenDocumentText.new({})
   end
-
-  def fixture_path(*components)
-    base_path = Pathname(__dir__) + "fixture"
-    base_path.join(*components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_odt
-        assert_true(@decomposer.target?(create_data("document.odt")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_opendocument_text
-        mime_type = "application/vnd.oasis.opendocument.text"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Writer"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.odt")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one page") do
-      def test_body
-        assert_equal(["Page1\n"], decompose.collect(&:body))
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-page.odt"))
-      end
-    end
-
-    sub_test_case("multi pages") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Page1
-Page2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-pages.odt"))
-      end
-    end
-  end
 end

  Modified: chupa-text-decomposer-libreoffice-powerpoint/lib/chupa-text/decomposers/libreoffice-powerpoint.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-powerpoint/lib/chupa-text/decomposers/libreoffice-powerpoint.rb    2019-02-26 14:17:18 +0900 (b9dc785)
+++ chupa-text-decomposer-libreoffice-powerpoint/lib/chupa-text/decomposers/libreoffice-powerpoint.rb    2019-02-26 14:54:14 +0900 (4dc28d4)
@@ -23,8 +23,8 @@ module ChupaText
 
       def initialize(options)
         super
-        @extension = "ppt"
-        @mime_type = "application/vnd.ms-powerpoint"
+        @extensions = ["ppt"]
+        @mime_types = ["application/vnd.ms-powerpoint"]
       end
 
       private

  Deleted: chupa-text-decomposer-libreoffice-powerpoint/test/fixture/attributes.ppt (+0 -23) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-powerpoint/test/fixture/attributes.ppt    2019-02-26 14:17:18 +0900 (26021b8)
+++ /dev/null
@@ -1,23 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.ms-powerpoint
-uri: file:/tmp/NMiu4q_attributes.ppt
-path: /tmp/NMiu4q_attributes.ppt
-size: 72192
-Content-Type: multipart/mixed; boundary=c6db6f8b42bd101d480130d35f96d80ede3fd1f2
-
---c6db6f8b42bd101d480130d35f96d80ede3fd1f2
-mime-type: text/plain
-uri: file:/tmp/NMiu4q_attributes.txt
-path: /tmp/NMiu4q_attributes.txt
-size: 6
-title: Title
-created_time: 2019-02-26 05:55:08 UTC
-source-mime-types: ["application/pdf", "application/vnd.ms-powerpoint"]
-subject: Subject
-keywords: Keyword1, Keyword2
-creator: Impress
-producer: LibreOffice 5.2
-
-Title
-
---c6db6f8b42bd101d480130d35f96d80ede3fd1f2--

  Deleted: chupa-text-decomposer-libreoffice-powerpoint/test/fixture/multi-slides.ppt (+0 -27) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-powerpoint/test/fixture/multi-slides.ppt    2019-02-26 14:17:18 +0900 (84b7cb2)
+++ /dev/null
@@ -1,27 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.ms-powerpoint
-uri: file:/tmp/QnDt5f_multi-slides.ppt
-path: /tmp/QnDt5f_multi-slides.ppt
-size: 75776
-Content-Type: multipart/mixed; boundary=606cf9a01014c830b4c5873baa2d6c60336bf09f
-
---606cf9a01014c830b4c5873baa2d6c60336bf09f
-mime-type: text/plain
-uri: file:/tmp/QnDt5f_multi-slides.txt
-path: /tmp/QnDt5f_multi-slides.txt
-size: 92
-created_time: 2019-02-26 05:55:10 UTC
-source-mime-types: ["application/pdf", "application/vnd.ms-powerpoint"]
-creator: Impress
-producer: LibreOffice 5.2
-
-Slide1 title
-Slide1 content
-Slide2 title
-●
-Slide2 content
-Slide3 title
-●
-Slide3 content
-
---606cf9a01014c830b4c5873baa2d6c60336bf09f--

  Deleted: chupa-text-decomposer-libreoffice-powerpoint/test/fixture/one-slide.ppt (+0 -21) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-powerpoint/test/fixture/one-slide.ppt    2019-02-26 14:17:18 +0900 (7b2bae5)
+++ /dev/null
@@ -1,21 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/vnd.ms-powerpoint
-uri: file:/tmp/a1yZ2T_one-slide.ppt
-path: /tmp/a1yZ2T_one-slide.ppt
-size: 71680
-Content-Type: multipart/mixed; boundary=5c6389b3a6312f3396433cc19bca9faebc16f8f7
-
---5c6389b3a6312f3396433cc19bca9faebc16f8f7
-mime-type: text/plain
-uri: file:/tmp/a1yZ2T_one-slide.txt
-path: /tmp/a1yZ2T_one-slide.txt
-size: 28
-created_time: 2019-02-26 05:55:12 UTC
-source-mime-types: ["application/pdf", "application/vnd.ms-powerpoint"]
-creator: Impress
-producer: LibreOffice 5.2
-
-Slide1 title
-Slide1 content
-
---5c6389b3a6312f3396433cc19bca9faebc16f8f7--

  Modified: chupa-text-decomposer-libreoffice-powerpoint/test/run-test.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-powerpoint/test/run-test.rb    2019-02-26 14:17:18 +0900 (6ebf1c3)
+++ chupa-text-decomposer-libreoffice-powerpoint/test/run-test.rb    2019-02-26 14:54:14 +0900 (be67c7d)
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 #
-# Copyright (C) 2013  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2013-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,6 @@ require "chupa-text"
 
 ChupaText::Decomposers.load
 
-require_relative "../../chupa-text-decomposer-libreoffice-general/test/helper"
+require_relative "../../chupa-text-decomposer-libreoffice/test/helper"
 
 exit(Test::Unit::AutoRunner.run(true))

  Modified: chupa-text-decomposer-libreoffice-powerpoint/test/test-ppt.rb (+2 -109)
===================================================================
--- chupa-text-decomposer-libreoffice-powerpoint/test/test-ppt.rb    2019-02-26 14:17:18 +0900 (3f4be2a)
+++ chupa-text-decomposer-libreoffice-powerpoint/test/test-ppt.rb    2019-02-26 14:54:14 +0900 (fbb6de9)
@@ -15,116 +15,9 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 class TestPpt < Test::Unit::TestCase
+  include PptTests
+
   def setup
     @decomposer = ChupaText::Decomposers::LibreOfficePowerPoint.new({})
   end
-
-  def fixture_path(*components)
-    base_path = Pathname(__dir__) + "fixture"
-    base_path.join(*components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_ppt
-        assert_true(@decomposer.target?(create_data("document.ppt")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_ms_powerpoint
-        mime_type = "application/vnd.ms-powerpoint"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Impress"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.ppt")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one slide") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-slide.ppt"))
-      end
-    end
-
-    sub_test_case("multi slides") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-Slide2 title
-●
-Slide2 content
-Slide3 title
-●
-Slide3 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-slides.ppt"))
-      end
-    end
-  end
 end

  Modified: chupa-text-decomposer-libreoffice-word/lib/chupa-text/decomposers/libreoffice-word.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-word/lib/chupa-text/decomposers/libreoffice-word.rb    2019-02-26 14:17:18 +0900 (9d0a969)
+++ chupa-text-decomposer-libreoffice-word/lib/chupa-text/decomposers/libreoffice-word.rb    2019-02-26 14:54:14 +0900 (fd8ef61)
@@ -23,8 +23,8 @@ module ChupaText
 
       def initialize(options)
         super
-        @extension = "doc"
-        @mime_type = "application/msword"
+        @extensions = ["doc"]
+        @mime_types = ["application/msword"]
       end
 
       private

  Deleted: chupa-text-decomposer-libreoffice-word/test/fixture/attributes.doc (+0 -23) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-word/test/fixture/attributes.doc    2019-02-26 14:17:18 +0900 (f5fc1ef)
+++ /dev/null
@@ -1,23 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/msword
-uri: file:/tmp/sBdLWt_attributes.doc
-path: /tmp/sBdLWt_attributes.doc
-size: 9216
-Content-Type: multipart/mixed; boundary=0dcd1a17243720cc35bb4a5335e55d1e1189876b
-
---0dcd1a17243720cc35bb4a5335e55d1e1189876b
-mime-type: text/plain
-uri: file:/tmp/sBdLWt_attributes.txt
-path: /tmp/sBdLWt_attributes.txt
-size: 6
-title: Title
-created_time: 2019-02-26 05:55:13 UTC
-source-mime-types: ["application/pdf", "application/msword"]
-subject: Subject
-keywords: Keyword1, Keyword2
-creator: Writer
-producer: LibreOffice 5.2
-
-Page1
-
---0dcd1a17243720cc35bb4a5335e55d1e1189876b--

  Deleted: chupa-text-decomposer-libreoffice-word/test/fixture/multi-pages.doc (+0 -21) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-word/test/fixture/multi-pages.doc    2019-02-26 14:17:18 +0900 (991047b)
+++ /dev/null
@@ -1,21 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/msword
-uri: file:/tmp/BVN1bf_multi-pages.doc
-path: /tmp/BVN1bf_multi-pages.doc
-size: 9216
-Content-Type: multipart/mixed; boundary=af06e8cc5691651ff90046dab516fd44279e7002
-
---af06e8cc5691651ff90046dab516fd44279e7002
-mime-type: text/plain
-uri: file:/tmp/BVN1bf_multi-pages.txt
-path: /tmp/BVN1bf_multi-pages.txt
-size: 12
-created_time: 2019-02-26 05:55:15 UTC
-source-mime-types: ["application/pdf", "application/msword"]
-creator: Writer
-producer: LibreOffice 5.2
-
-Page1
-Page2
-
---af06e8cc5691651ff90046dab516fd44279e7002--

  Deleted: chupa-text-decomposer-libreoffice-word/test/fixture/one-page.doc (+0 -20) 100644
===================================================================
--- chupa-text-decomposer-libreoffice-word/test/fixture/one-page.doc    2019-02-26 14:17:18 +0900 (4216000)
+++ /dev/null
@@ -1,20 +0,0 @@
-MIME-Version: 1.0
-mime-type: application/msword
-uri: file:/tmp/9FDudL_one-page.doc
-path: /tmp/9FDudL_one-page.doc
-size: 9216
-Content-Type: multipart/mixed; boundary=a8d98b5531896b194781410ed622848251236e44
-
---a8d98b5531896b194781410ed622848251236e44
-mime-type: text/plain
-uri: file:/tmp/9FDudL_one-page.txt
-path: /tmp/9FDudL_one-page.txt
-size: 6
-created_time: 2019-02-26 05:55:17 UTC
-source-mime-types: ["application/pdf", "application/msword"]
-creator: Writer
-producer: LibreOffice 5.2
-
-Page1
-
---a8d98b5531896b194781410ed622848251236e44--

  Modified: chupa-text-decomposer-libreoffice-word/test/run-test.rb (+2 -2)
===================================================================
--- chupa-text-decomposer-libreoffice-word/test/run-test.rb    2019-02-26 14:17:18 +0900 (6ebf1c3)
+++ chupa-text-decomposer-libreoffice-word/test/run-test.rb    2019-02-26 14:54:14 +0900 (be67c7d)
@@ -1,6 +1,6 @@
 #!/usr/bin/env ruby
 #
-# Copyright (C) 2013  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2013-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,6 @@ require "chupa-text"
 
 ChupaText::Decomposers.load
 
-require_relative "../../chupa-text-decomposer-libreoffice-general/test/helper"
+require_relative "../../chupa-text-decomposer-libreoffice/test/helper"
 
 exit(Test::Unit::AutoRunner.run(true))

  Modified: chupa-text-decomposer-libreoffice-word/test/test-doc.rb (+2 -100)
===================================================================
--- chupa-text-decomposer-libreoffice-word/test/test-doc.rb    2019-02-26 14:17:18 +0900 (b5c8d01)
+++ chupa-text-decomposer-libreoffice-word/test/test-doc.rb    2019-02-26 14:54:14 +0900 (0970ced)
@@ -15,107 +15,9 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 class TestDoc < Test::Unit::TestCase
+  include DocTests
+
   def setup
     @decomposer = ChupaText::Decomposers::LibreOfficeWord.new({})
   end
-
-  def fixture_path(*components)
-    base_path = Pathname(__dir__) + "fixture"
-    base_path.join(*components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_doc
-        assert_true(@decomposer.target?(create_data("document.doc")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_ms_word
-        mime_type = "application/msword"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Writer"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.doc")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one page") do
-      def test_body
-        assert_equal(["Page1\n"], decompose.collect(&:body))
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-page.doc"))
-      end
-    end
-
-    sub_test_case("multi pages") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Page1
-Page2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-pages.doc"))
-      end
-    end
-  end
 end

  Modified: chupa-text-decomposer-libreoffice/Gemfile (+3 -0)
===================================================================
--- chupa-text-decomposer-libreoffice/Gemfile    2019-02-26 14:17:18 +0900 (9ab1bf4)
+++ chupa-text-decomposer-libreoffice/Gemfile    2019-02-26 14:54:14 +0900 (4c7a904)
@@ -22,6 +22,9 @@ gemspec
 
 base_dir = File.dirname(__FILE__)
 
+general = "chupa-text-decomposer-libreoffice-general"
+gem general, path: File.join(base_dir, "..", general)
+
 local_gem_names = [
   "chupa-text",
   "chupa-text-decomposer-pdf",

  Modified: chupa-text-decomposer-libreoffice/chupa-text-decomposer-libreoffice.gemspec (+1 -9)
===================================================================
--- chupa-text-decomposer-libreoffice/chupa-text-decomposer-libreoffice.gemspec    2019-02-26 14:17:18 +0900 (1797c99)
+++ chupa-text-decomposer-libreoffice/chupa-text-decomposer-libreoffice.gemspec    2019-02-26 14:54:14 +0900 (11bbbab)
@@ -39,15 +39,7 @@ Gem::Specification.new do |spec|
   spec.files += Dir.glob("doc/text/*")
   spec.files += Dir.glob("test/**/*")
 
-  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-opendocument-text")
-  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-opendocument-spreadsheet")
-  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-opendocument-presentation")
-  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-word")
-  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-excel")
-  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-powerpoint")
-  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-office-open-xml-document")
-  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-office-open-xml-workbook")
-  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-office-open-xml-presentation")
+  spec.add_runtime_dependency("chupa-text-decomposer-libreoffice-general")
 
   spec.add_development_dependency("bundler")
   spec.add_development_dependency("rake")

  Modified: chupa-text-decomposer-libreoffice/lib/chupa-text/decomposers/libreoffice.rb (+23 -122)
===================================================================
--- chupa-text-decomposer-libreoffice/lib/chupa-text/decomposers/libreoffice.rb    2019-02-26 14:17:18 +0900 (aea8593)
+++ chupa-text-decomposer-libreoffice/lib/chupa-text/decomposers/libreoffice.rb    2019-02-26 14:54:14 +0900 (d7fa48a)
@@ -14,136 +14,37 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "tempfile"
-require "tmpdir"
+require "chupa-text/decomposers/libreoffice-general"
 
 module ChupaText
   module Decomposers
-    class LibreOffice < Decomposer
-      include Loggable
-
+    class LibreOffice < LibreOfficeGeneral
       registry.register("libreoffice", self)
 
       def initialize(options)
         super
-        @command = find_command
-        debug do
-          if @command
-            "#{log_tag}[command][found] #{@command.path}"
-          else
-            "#{log_tag}[command][not-found]"
-          end
-        end
-      end
-
-      TARGET_EXTENSIONS = [
-        "odt",
-        "ods",
-        "odp",
-        "doc",
-        "xls",
-        "ppt",
-        "docx",
-        "xlsx",
-        "pptx",
-      ]
-      TARGET_MIME_TYPES = [
-        "application/vnd.oasis.opendocument.text",
-        "application/vnd.oasis.opendocument.presentation",
-        "application/vnd.oasis.opendocument.spreadsheet",
-        "application/msword",
-        "application/vnd.ms-excel",
-        "application/vnd.ms-powerpoint",
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
-        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-        "application/vnd.openxmlformats-officedocument.presentationml.presentation",
-      ]
-      def target?(data)
-        return false if****@comma*****?
-        TARGET_EXTENSIONS.include?(data.extension) or
-          TARGET_MIME_TYPES.include?(data.mime_type)
-      end
-
-      def decompose(data)
-        pdf_data = convert_to_pdf(data)
-        return if pdf_data.nil?
-        yield(pdf_data)
-      end
-
-      private
-      def find_command
-        candidates = [
-          @options[:libreoffice],
-          ENV["LIBREOFFICE"],
-          "libreoffice",
-          "soffice",
+        @extensions = [
+          "odt",
+          "ods",
+          "odp",
+          "doc",
+          "xls",
+          "ppt",
+          "docx",
+          "xlsx",
+          "pptx",
+        ]
+        @mime_types = [
+          "application/vnd.oasis.opendocument.text",
+          "application/vnd.oasis.opendocument.presentation",
+          "application/vnd.oasis.opendocument.spreadsheet",
+          "application/msword",
+          "application/vnd.ms-excel",
+          "application/vnd.ms-powerpoint",
+          "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+          "application/vnd.openxmlformats-officedocument.presentationml.presentation",
         ]
-        candidates.each do |candidate|
-          next if candidate.nil?
-          command = ExternalCommand.new(candidate)
-          return command if command.exist?
-          expanded_candidate = expand_candidate(candidate)
-          next if expanded_candidate.nil?
-          command = ExternalCommand.new(expanded_candidate)
-          return command if command.exist?
-        end
-        nil
-      end
-
-      def expand_candidate(candidate)
-        Dir.glob("/opt/libreoffice*/program/#{candidate}").first or
-          Dir.glob("C:/Program Files*/libreoffice*/program/#{candidate}.exe").first
-      end
-
-      def convert_to_pdf(data)
-        Dir.mktmpdir do |temporary_directory|
-          output = Tempfile.new("chupa-text-decomposer-libreoffice-output")
-          error_output = Tempfile.new("chupa-text-decomposer-libreoffice-error")
-          succeeded =****@comma*****("--headless",
-                                   "--nologo",
-                                   "--convert-to", "pdf",
-                                   "--outdir", temporary_directory,
-                                   data.path.to_s,
-                                   {
-                                     :spawn_options => {
-                                       :out => output.path,
-                                       :err => error_output.path,
-                                     },
-                                   })
-          unless succeeded
-            error do
-              tag = "#{log_tag}[convert][exited][abnormally]"
-              [
-                tag,
-                "output: <#{output.read}>",
-                "error: <#{error_output.read}>",
-              ].join("\n")
-            end
-            return nil
-          end
-          pdf_path, = Dir.glob("#{temporary_directory}/*.pdf")
-          if pdf_path.nil?
-            error do
-              tag = "#{log_tag}[convert][failed]"
-              message = [
-                "#{tag}: LibreOffice may be running",
-                "output: <#{output.read}>",
-                "error: <#{error_output.read}>",
-              ].join("\n")
-            end
-            return nil
-          end
-          normalized_pdf_uri = data.uri.to_s.gsub(/\.[^.]+\z/, ".pdf")
-          File.open(pdf_path, "rb") do |pdf|
-            ChupaText::VirtualFileData.new(normalized_pdf_uri,
-                                           pdf,
-                                           :source_data => data)
-          end
-        end
-      end
-
-      def log_tag
-        "[decomposer][libreoffice]"
       end
     end
   end

  Modified: chupa-text-decomposer-libreoffice/test/helper.rb (+13 -5)
===================================================================
--- chupa-text-decomposer-libreoffice/test/helper.rb    2019-02-26 14:17:18 +0900 (f2ce1ab)
+++ chupa-text-decomposer-libreoffice/test/helper.rb    2019-02-26 14:54:14 +0900 (4f43832)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -14,11 +14,9 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-module FixtureHelper
-  def setup_decomposer
-    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
-  end
+require "pathname"
 
+module FixtureHelper
   def fixture_path(*components)
     base_path = Pathname(__dir__) + "fixture"
     base_path.join(*components)
@@ -57,3 +55,13 @@ module DecomposeHelper
     end
   end
 end
+
+require_relative "tests/doc-tests"
+require_relative "tests/docx-tests"
+require_relative "tests/odp-tests"
+require_relative "tests/ods-tests"
+require_relative "tests/odt-tests"
+require_relative "tests/ppt-tests"
+require_relative "tests/pptx-tests"
+require_relative "tests/xls-tests"
+require_relative "tests/xlsx-tests"

  Modified: chupa-text-decomposer-libreoffice/test/test-doc.rb (+3 -104)
===================================================================
--- chupa-text-decomposer-libreoffice/test/test-doc.rb    2019-02-26 14:17:18 +0900 (b51b052)
+++ chupa-text-decomposer-libreoffice/test/test-doc.rb    2019-02-26 14:54:14 +0900 (71171b4)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -14,111 +14,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "pathname"
-
 class TestDoc < Test::Unit::TestCase
-  include FixtureHelper
+  include DocTests
 
   def setup
-    setup_decomposer
-  end
-
-  def fixture_path(*components)
-    super("doc", *components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_doc
-        assert_true(@decomposer.target?(create_data("document.doc")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_ms_word
-        mime_type = "application/msword"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Writer"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.doc")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one page") do
-      def test_body
-        assert_equal(["Page1\n"], decompose.collect(&:body))
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-page.doc"))
-      end
-    end
-
-    sub_test_case("multi pages") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Page1
-Page2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-pages.doc"))
-      end
-    end
+    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
   end
 end

  Modified: chupa-text-decomposer-libreoffice/test/test-docx.rb (+3 -104)
===================================================================
--- chupa-text-decomposer-libreoffice/test/test-docx.rb    2019-02-26 14:17:18 +0900 (dddc613)
+++ chupa-text-decomposer-libreoffice/test/test-docx.rb    2019-02-26 14:54:14 +0900 (ec57337)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -14,111 +14,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "pathname"
-
 class TestDocx < Test::Unit::TestCase
-  include FixtureHelper
+  include DocxTests
 
   def setup
-    setup_decomposer
-  end
-
-  def fixture_path(*components)
-    super("docx", *components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_docx
-        assert_true(@decomposer.target?(create_data("document.docx")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_openxml_document
-        mime_type = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Writer"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.docx")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one page") do
-      def test_body
-        assert_equal(["Page1\n"], decompose.collect(&:body))
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-page.docx"))
-      end
-    end
-
-    sub_test_case("multi pages") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Page1
-Page2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-pages.docx"))
-      end
-    end
+    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
   end
 end

  Modified: chupa-text-decomposer-libreoffice/test/test-odp.rb (+3 -115)
===================================================================
--- chupa-text-decomposer-libreoffice/test/test-odp.rb    2019-02-26 14:17:18 +0900 (3435b8f)
+++ chupa-text-decomposer-libreoffice/test/test-odp.rb    2019-02-26 14:54:14 +0900 (93d1347)
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -16,120 +14,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "pathname"
-
 class TestOdp < Test::Unit::TestCase
-  include FixtureHelper
+  include OdpTests
 
   def setup
-    setup_decomposer
-  end
-
-  def fixture_path(*components)
-    super("odp", *components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_odp
-        assert_true(@decomposer.target?(create_data("document.odp")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_opendocument_presentation
-        mime_type = "application/vnd.oasis.opendocument.presentation"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Impress"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.odp")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one slide") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-slide.odp"))
-      end
-    end
-
-    sub_test_case("multi slides") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-Slide2 title
-●
-Slide2 content
-Slide3 title
-●
-Slide3 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-slides.odp"))
-      end
-    end
+    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
   end
 end

  Modified: chupa-text-decomposer-libreoffice/test/test-ods.rb (+3 -119)
===================================================================
--- chupa-text-decomposer-libreoffice/test/test-ods.rb    2019-02-26 14:17:18 +0900 (ea41771)
+++ chupa-text-decomposer-libreoffice/test/test-ods.rb    2019-02-26 14:54:14 +0900 (45b2832)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -14,126 +14,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "pathname"
-
 class TestOds < Test::Unit::TestCase
-  include FixtureHelper
+  include OdsTests
 
   def setup
-    setup_decomposer
-  end
-
-  def fixture_path(*components)
-    super("ods", *components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_ods
-        assert_true(@decomposer.target?(create_data("document.ods")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_opendocument_spareadsheet
-        mime_type = "application/vnd.oasis.opendocument.spreadsheet"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Calc"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.ods")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one sheet") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-sheet.ods"))
-      end
-    end
-
-    sub_test_case("multi sheets") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-Sheet2 - A1
-Sheet2 - A2
-Sheet2 - B1
-Sheet2 - B2
-Sheet3 - A1
-Sheet3 - A2
-Sheet3 - B1
-Sheet3 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-sheets.ods"))
-      end
-    end
+    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
   end
 end

  Modified: chupa-text-decomposer-libreoffice/test/test-odt.rb (+3 -104)
===================================================================
--- chupa-text-decomposer-libreoffice/test/test-odt.rb    2019-02-26 14:17:18 +0900 (5e98d76)
+++ chupa-text-decomposer-libreoffice/test/test-odt.rb    2019-02-26 14:54:14 +0900 (06027e0)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -14,111 +14,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "pathname"
-
 class TestOdt < Test::Unit::TestCase
-  include FixtureHelper
+  include OdtTests
 
   def setup
-    setup_decomposer
-  end
-
-  def fixture_path(*components)
-    super("odt", *components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_odt
-        assert_true(@decomposer.target?(create_data("document.odt")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_opendocument_text
-        mime_type = "application/vnd.oasis.opendocument.text"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Writer"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.odt")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one page") do
-      def test_body
-        assert_equal(["Page1\n"], decompose.collect(&:body))
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-page.odt"))
-      end
-    end
-
-    sub_test_case("multi pages") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Page1
-Page2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-pages.odt"))
-      end
-    end
+    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
   end
 end

  Modified: chupa-text-decomposer-libreoffice/test/test-ppt.rb (+3 -115)
===================================================================
--- chupa-text-decomposer-libreoffice/test/test-ppt.rb    2019-02-26 14:17:18 +0900 (453fc4e)
+++ chupa-text-decomposer-libreoffice/test/test-ppt.rb    2019-02-26 14:54:14 +0900 (579f1a4)
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -16,120 +14,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "pathname"
-
 class TestPpt < Test::Unit::TestCase
-  include FixtureHelper
+  include PptTests
 
   def setup
-    setup_decomposer
-  end
-
-  def fixture_path(*components)
-    super("ppt", *components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_ppt
-        assert_true(@decomposer.target?(create_data("document.ppt")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_ms_powerpoint
-        mime_type = "application/vnd.ms-powerpoint"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Impress"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.ppt")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one slide") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-slide.ppt"))
-      end
-    end
-
-    sub_test_case("multi slides") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-Slide2 title
-●
-Slide2 content
-Slide3 title
-●
-Slide3 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-slides.ppt"))
-      end
-    end
+    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
   end
 end

  Modified: chupa-text-decomposer-libreoffice/test/test-pptx.rb (+3 -111)
===================================================================
--- chupa-text-decomposer-libreoffice/test/test-pptx.rb    2019-02-26 14:17:18 +0900 (b00e20b)
+++ chupa-text-decomposer-libreoffice/test/test-pptx.rb    2019-02-26 14:54:14 +0900 (b4efe5f)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -14,118 +14,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "pathname"
-
 class TestPptx < Test::Unit::TestCase
-  include FixtureHelper
+  include PptxTests
 
   def setup
-    setup_decomposer
-  end
-
-  def fixture_path(*components)
-    super("pptx", *components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_pptx
-        assert_true(@decomposer.target?(create_data("document.pptx")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_openxml_presentation
-        mime_type = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Impress"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.pptx")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one slide") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-slide.pptx"))
-      end
-    end
-
-    sub_test_case("multi slides") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Slide1 title
-Slide1 content
-Slide2 title
-Slide2 content
-Slide3 title
-Slide3 content
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-slides.pptx"))
-      end
-    end
+    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
   end
 end

  Modified: chupa-text-decomposer-libreoffice/test/test-xls.rb (+3 -119)
===================================================================
--- chupa-text-decomposer-libreoffice/test/test-xls.rb    2019-02-26 14:17:18 +0900 (81bfa35)
+++ chupa-text-decomposer-libreoffice/test/test-xls.rb    2019-02-26 14:54:14 +0900 (b2611cf)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -14,126 +14,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "pathname"
-
 class TestXls < Test::Unit::TestCase
-  include FixtureHelper
+  include XlsTests
 
   def setup
-    setup_decomposer
-  end
-
-  def fixture_path(*components)
-    super("xls", *components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_xls
-        assert_true(@decomposer.target?(create_data("document.xls")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_ms_excel
-        mime_type = "application/vnd.ms-excel"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Calc"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.xls")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one sheet") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-sheet.xls"))
-      end
-    end
-
-    sub_test_case("multi sheets") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-Sheet2 - A1
-Sheet2 - A2
-Sheet2 - B1
-Sheet2 - B2
-Sheet3 - A1
-Sheet3 - A2
-Sheet3 - B1
-Sheet3 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-sheets.xls"))
-      end
-    end
+    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
   end
 end

  Modified: chupa-text-decomposer-libreoffice/test/test-xlsx.rb (+3 -119)
===================================================================
--- chupa-text-decomposer-libreoffice/test/test-xlsx.rb    2019-02-26 14:17:18 +0900 (a9617a4)
+++ chupa-text-decomposer-libreoffice/test/test-xlsx.rb    2019-02-26 14:54:14 +0900 (30e5aa4)
@@ -1,4 +1,4 @@
-# Copyright (C) 2014  Kouhei Sutou <kou****@clear*****>
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -14,126 +14,10 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "pathname"
-
 class TestXlsx < Test::Unit::TestCase
-  include FixtureHelper
+  include XlsxTests
 
   def setup
-    setup_decomposer
-  end
-
-  def fixture_path(*components)
-    super("xlsx", *components)
-  end
-
-  sub_test_case("target?") do
-    sub_test_case("extension") do
-      def create_data(uri)
-        data = ChupaText::Data.new
-        data.body = ""
-        data.uri = uri
-        data
-      end
-
-      def test_xlsx
-        assert_true(@decomposer.target?(create_data("document.xlsx")))
-      end
-    end
-
-    sub_test_case("mime-type") do
-      def create_data(mime_type)
-        data = ChupaText::Data.new
-        data.mime_type = mime_type
-        data
-      end
-
-      def test_openxml_sheet
-        mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
-        assert_true(@decomposer.target?(create_data(mime_type)))
-      end
-    end
-  end
-
-  sub_test_case("decompose") do
-    include DecomposeHelper
-
-    sub_test_case("attributes") do
-      def test_title
-        assert_equal(["Title"], decompose("title"))
-      end
-
-      def test_author
-        assert_equal([nil], decompose("author"))
-      end
-
-      def test_subject
-        assert_equal(["Subject"], decompose("subject"))
-      end
-
-      def test_keywords
-        assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
-      end
-
-      def test_creator
-        assert_equal(["Calc"], decompose("creator"))
-      end
-
-      def test_producer
-        assert_equal(["LibreOffice X.Y"],
-                     normalize_producers(decompose("producer")))
-      end
-
-      def test_creation_date
-        assert_equal([nil], decompose("creation_date"))
-      end
-
-      private
-      def decompose(attribute_name)
-        super(fixture_path("attributes.xlsx")).collect do |data|
-          data[attribute_name]
-        end
-      end
-    end
-
-    sub_test_case("one sheet") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("one-sheet.xlsx"))
-      end
-    end
-
-    sub_test_case("multi sheets") do
-      def test_body
-        assert_equal([<<-BODY], decompose.collect(&:body))
-Sheet1 - A1
-Sheet1 - A2
-Sheet1 - B1
-Sheet1 - B2
-Sheet2 - A1
-Sheet2 - A2
-Sheet2 - B1
-Sheet2 - B2
-Sheet3 - A1
-Sheet3 - A2
-Sheet3 - B1
-Sheet3 - B2
-        BODY
-      end
-
-      private
-      def decompose
-        super(fixture_path("multi-sheets.xlsx"))
-      end
-    end
+    @decomposer = ChupaText::Decomposers::LibreOffice.new({})
   end
 end

  Added: chupa-text-decomposer-libreoffice/test/tests/doc-tests.rb (+124 -0) 100644
===================================================================
--- /dev/null
+++ chupa-text-decomposer-libreoffice/test/tests/doc-tests.rb    2019-02-26 14:54:14 +0900 (7438f02)
@@ -0,0 +1,124 @@
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module DocTests
+  class << self
+    def included(test_case)
+      test_case.class_eval do
+        include FixtureHelper
+
+        def fixture_path(*components)
+          super("doc", *components)
+        end
+
+        sub_test_case("target?") do
+          sub_test_case("extension") do
+            def create_data(uri)
+              data = ChupaText::Data.new
+              data.body = ""
+              data.uri = uri
+              data
+            end
+
+            def test_doc
+              assert_true(@decomposer.target?(create_data("document.doc")))
+            end
+          end
+
+          sub_test_case("mime-type") do
+            def create_data(mime_type)
+              data = ChupaText::Data.new
+              data.mime_type = mime_type
+              data
+            end
+
+            def test_ms_word
+              mime_type = "application/msword"
+              assert_true(@decomposer.target?(create_data(mime_type)))
+            end
+          end
+        end
+
+        sub_test_case("decompose") do
+          include DecomposeHelper
+
+          sub_test_case("attributes") do
+            def test_title
+              assert_equal(["Title"], decompose("title"))
+            end
+
+            def test_author
+              assert_equal([nil], decompose("author"))
+            end
+
+            def test_subject
+              assert_equal(["Subject"], decompose("subject"))
+            end
+
+            def test_keywords
+              assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
+            end
+
+            def test_creator
+              assert_equal(["Writer"], decompose("creator"))
+            end
+
+            def test_producer
+              assert_equal(["LibreOffice X.Y"],
+                           normalize_producers(decompose("producer")))
+            end
+
+            def test_creation_date
+              assert_equal([nil], decompose("creation_date"))
+            end
+
+            private
+            def decompose(attribute_name)
+              super(fixture_path("attributes.doc")).collect do |data|
+                data[attribute_name]
+              end
+            end
+          end
+
+          sub_test_case("one page") do
+            def test_body
+              assert_equal(["Page1\n"], decompose.collect(&:body))
+            end
+
+            private
+            def decompose
+              super(fixture_path("one-page.doc"))
+            end
+          end
+
+          sub_test_case("multi pages") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Page1
+Page2
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("multi-pages.doc"))
+            end
+          end
+        end
+      end
+    end
+  end
+end

  Added: chupa-text-decomposer-libreoffice/test/tests/docx-tests.rb (+124 -0) 100644
===================================================================
--- /dev/null
+++ chupa-text-decomposer-libreoffice/test/tests/docx-tests.rb    2019-02-26 14:54:14 +0900 (3677acb)
@@ -0,0 +1,124 @@
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module DocxTests
+  class << self
+    def included(test_case)
+      test_case.class_eval do
+        include FixtureHelper
+
+        def fixture_path(*components)
+          super("docx", *components)
+        end
+
+        sub_test_case("target?") do
+          sub_test_case("extension") do
+            def create_data(uri)
+              data = ChupaText::Data.new
+              data.body = ""
+              data.uri = uri
+              data
+            end
+
+            def test_docx
+              assert_true(@decomposer.target?(create_data("document.docx")))
+            end
+          end
+
+          sub_test_case("mime-type") do
+            def create_data(mime_type)
+              data = ChupaText::Data.new
+              data.mime_type = mime_type
+              data
+            end
+
+            def test_openxml_document
+              mime_type = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
+              assert_true(@decomposer.target?(create_data(mime_type)))
+            end
+          end
+        end
+
+        sub_test_case("decompose") do
+          include DecomposeHelper
+
+          sub_test_case("attributes") do
+            def test_title
+              assert_equal(["Title"], decompose("title"))
+            end
+
+            def test_author
+              assert_equal([nil], decompose("author"))
+            end
+
+            def test_subject
+              assert_equal(["Subject"], decompose("subject"))
+            end
+
+            def test_keywords
+              assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
+            end
+
+            def test_creator
+              assert_equal(["Writer"], decompose("creator"))
+            end
+
+            def test_producer
+              assert_equal(["LibreOffice X.Y"],
+                           normalize_producers(decompose("producer")))
+            end
+
+            def test_creation_date
+              assert_equal([nil], decompose("creation_date"))
+            end
+
+            private
+            def decompose(attribute_name)
+              super(fixture_path("attributes.docx")).collect do |data|
+                data[attribute_name]
+              end
+            end
+          end
+
+          sub_test_case("one page") do
+            def test_body
+              assert_equal(["Page1\n"], decompose.collect(&:body))
+            end
+
+            private
+            def decompose
+              super(fixture_path("one-page.docx"))
+            end
+          end
+
+          sub_test_case("multi pages") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Page1
+Page2
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("multi-pages.docx"))
+            end
+          end
+        end
+      end
+    end
+  end
+end

  Added: chupa-text-decomposer-libreoffice/test/tests/odp-tests.rb (+133 -0) 100644
===================================================================
--- /dev/null
+++ chupa-text-decomposer-libreoffice/test/tests/odp-tests.rb    2019-02-26 14:54:14 +0900 (2309349)
@@ -0,0 +1,133 @@
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module OdpTests
+  class << self
+    def included(test_case)
+      test_case.class_eval do
+        include FixtureHelper
+
+        def fixture_path(*components)
+          super("odp", *components)
+        end
+
+        sub_test_case("target?") do
+          sub_test_case("extension") do
+            def create_data(uri)
+              data = ChupaText::Data.new
+              data.body = ""
+              data.uri = uri
+              data
+            end
+
+            def test_odp
+              assert_true(@decomposer.target?(create_data("document.odp")))
+            end
+          end
+
+          sub_test_case("mime-type") do
+            def create_data(mime_type)
+              data = ChupaText::Data.new
+              data.mime_type = mime_type
+              data
+            end
+
+            def test_opendocument_presentation
+              mime_type = "application/vnd.oasis.opendocument.presentation"
+              assert_true(@decomposer.target?(create_data(mime_type)))
+            end
+          end
+        end
+
+        sub_test_case("decompose") do
+          include DecomposeHelper
+
+          sub_test_case("attributes") do
+            def test_title
+              assert_equal(["Title"], decompose("title"))
+            end
+
+            def test_author
+              assert_equal([nil], decompose("author"))
+            end
+
+            def test_subject
+              assert_equal(["Subject"], decompose("subject"))
+            end
+
+            def test_keywords
+              assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
+            end
+
+            def test_creator
+              assert_equal(["Impress"], decompose("creator"))
+            end
+
+            def test_producer
+              assert_equal(["LibreOffice X.Y"],
+                           normalize_producers(decompose("producer")))
+            end
+
+            def test_creation_date
+              assert_equal([nil], decompose("creation_date"))
+            end
+
+            private
+            def decompose(attribute_name)
+              super(fixture_path("attributes.odp")).collect do |data|
+                data[attribute_name]
+              end
+            end
+          end
+
+          sub_test_case("one slide") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Slide1 title
+Slide1 content
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("one-slide.odp"))
+            end
+          end
+
+          sub_test_case("multi slides") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Slide1 title
+Slide1 content
+Slide2 title
+●
+Slide2 content
+Slide3 title
+●
+Slide3 content
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("multi-slides.odp"))
+            end
+          end
+        end
+      end
+    end
+  end
+end

  Added: chupa-text-decomposer-libreoffice/test/tests/ods-tests.rb (+139 -0) 100644
===================================================================
--- /dev/null
+++ chupa-text-decomposer-libreoffice/test/tests/ods-tests.rb    2019-02-26 14:54:14 +0900 (041b4fa)
@@ -0,0 +1,139 @@
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module OdsTests
+  class << self
+    def included(test_case)
+      test_case.class_eval do
+        include FixtureHelper
+
+        def fixture_path(*components)
+          super("ods", *components)
+        end
+
+        sub_test_case("target?") do
+          sub_test_case("extension") do
+            def create_data(uri)
+              data = ChupaText::Data.new
+              data.body = ""
+              data.uri = uri
+              data
+            end
+
+            def test_ods
+              assert_true(@decomposer.target?(create_data("document.ods")))
+            end
+          end
+
+          sub_test_case("mime-type") do
+            def create_data(mime_type)
+              data = ChupaText::Data.new
+              data.mime_type = mime_type
+              data
+            end
+
+            def test_opendocument_spareadsheet
+              mime_type = "application/vnd.oasis.opendocument.spreadsheet"
+              assert_true(@decomposer.target?(create_data(mime_type)))
+            end
+          end
+        end
+
+        sub_test_case("decompose") do
+          include DecomposeHelper
+
+          sub_test_case("attributes") do
+            def test_title
+              assert_equal(["Title"], decompose("title"))
+            end
+
+            def test_author
+              assert_equal([nil], decompose("author"))
+            end
+
+            def test_subject
+              assert_equal(["Subject"], decompose("subject"))
+            end
+
+            def test_keywords
+              assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
+            end
+
+            def test_creator
+              assert_equal(["Calc"], decompose("creator"))
+            end
+
+            def test_producer
+              assert_equal(["LibreOffice X.Y"],
+                           normalize_producers(decompose("producer")))
+            end
+
+            def test_creation_date
+              assert_equal([nil], decompose("creation_date"))
+            end
+
+            private
+            def decompose(attribute_name)
+              super(fixture_path("attributes.ods")).collect do |data|
+                data[attribute_name]
+              end
+            end
+          end
+
+          sub_test_case("one sheet") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Sheet1 - A1
+Sheet1 - A2
+Sheet1 - B1
+Sheet1 - B2
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("one-sheet.ods"))
+            end
+          end
+
+          sub_test_case("multi sheets") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Sheet1 - A1
+Sheet1 - A2
+Sheet1 - B1
+Sheet1 - B2
+Sheet2 - A1
+Sheet2 - A2
+Sheet2 - B1
+Sheet2 - B2
+Sheet3 - A1
+Sheet3 - A2
+Sheet3 - B1
+Sheet3 - B2
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("multi-sheets.ods"))
+            end
+          end
+        end
+      end
+    end
+  end
+end

  Added: chupa-text-decomposer-libreoffice/test/tests/odt-tests.rb (+124 -0) 100644
===================================================================
--- /dev/null
+++ chupa-text-decomposer-libreoffice/test/tests/odt-tests.rb    2019-02-26 14:54:14 +0900 (668a15e)
@@ -0,0 +1,124 @@
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module OdtTests
+  class << self
+    def included(test_case)
+      test_case.class_eval do
+        include FixtureHelper
+
+        def fixture_path(*components)
+          super("odt", *components)
+        end
+
+        sub_test_case("target?") do
+          sub_test_case("extension") do
+            def create_data(uri)
+              data = ChupaText::Data.new
+              data.body = ""
+              data.uri = uri
+              data
+            end
+
+            def test_odt
+              assert_true(@decomposer.target?(create_data("document.odt")))
+            end
+          end
+
+          sub_test_case("mime-type") do
+            def create_data(mime_type)
+              data = ChupaText::Data.new
+              data.mime_type = mime_type
+              data
+            end
+
+            def test_opendocument_text
+              mime_type = "application/vnd.oasis.opendocument.text"
+              assert_true(@decomposer.target?(create_data(mime_type)))
+            end
+          end
+        end
+
+        sub_test_case("decompose") do
+          include DecomposeHelper
+
+          sub_test_case("attributes") do
+            def test_title
+              assert_equal(["Title"], decompose("title"))
+            end
+
+            def test_author
+              assert_equal([nil], decompose("author"))
+            end
+
+            def test_subject
+              assert_equal(["Subject"], decompose("subject"))
+            end
+
+            def test_keywords
+              assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
+            end
+
+            def test_creator
+              assert_equal(["Writer"], decompose("creator"))
+            end
+
+            def test_producer
+              assert_equal(["LibreOffice X.Y"],
+                           normalize_producers(decompose("producer")))
+            end
+
+            def test_creation_date
+              assert_equal([nil], decompose("creation_date"))
+            end
+
+            private
+            def decompose(attribute_name)
+              super(fixture_path("attributes.odt")).collect do |data|
+                data[attribute_name]
+              end
+            end
+          end
+
+          sub_test_case("one page") do
+            def test_body
+              assert_equal(["Page1\n"], decompose.collect(&:body))
+            end
+
+            private
+            def decompose
+              super(fixture_path("one-page.odt"))
+            end
+          end
+
+          sub_test_case("multi pages") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Page1
+Page2
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("multi-pages.odt"))
+            end
+          end
+        end
+      end
+    end
+  end
+end

  Added: chupa-text-decomposer-libreoffice/test/tests/ppt-tests.rb (+133 -0) 100644
===================================================================
--- /dev/null
+++ chupa-text-decomposer-libreoffice/test/tests/ppt-tests.rb    2019-02-26 14:54:14 +0900 (fbae074)
@@ -0,0 +1,133 @@
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module PptTests
+  class << self
+    def included(test_case)
+      test_case.class_eval do
+        include FixtureHelper
+
+        def fixture_path(*components)
+          super("ppt", *components)
+        end
+
+        sub_test_case("target?") do
+          sub_test_case("extension") do
+            def create_data(uri)
+              data = ChupaText::Data.new
+              data.body = ""
+              data.uri = uri
+              data
+            end
+
+            def test_ppt
+              assert_true(@decomposer.target?(create_data("document.ppt")))
+            end
+          end
+
+          sub_test_case("mime-type") do
+            def create_data(mime_type)
+              data = ChupaText::Data.new
+              data.mime_type = mime_type
+              data
+            end
+
+            def test_ms_powerpoint
+              mime_type = "application/vnd.ms-powerpoint"
+              assert_true(@decomposer.target?(create_data(mime_type)))
+            end
+          end
+        end
+
+        sub_test_case("decompose") do
+          include DecomposeHelper
+
+          sub_test_case("attributes") do
+            def test_title
+              assert_equal(["Title"], decompose("title"))
+            end
+
+            def test_author
+              assert_equal([nil], decompose("author"))
+            end
+
+            def test_subject
+              assert_equal(["Subject"], decompose("subject"))
+            end
+
+            def test_keywords
+              assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
+            end
+
+            def test_creator
+              assert_equal(["Impress"], decompose("creator"))
+            end
+
+            def test_producer
+              assert_equal(["LibreOffice X.Y"],
+                           normalize_producers(decompose("producer")))
+            end
+
+            def test_creation_date
+              assert_equal([nil], decompose("creation_date"))
+            end
+
+            private
+            def decompose(attribute_name)
+              super(fixture_path("attributes.ppt")).collect do |data|
+                data[attribute_name]
+              end
+            end
+          end
+
+          sub_test_case("one slide") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Slide1 title
+Slide1 content
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("one-slide.ppt"))
+            end
+          end
+
+          sub_test_case("multi slides") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Slide1 title
+Slide1 content
+Slide2 title
+●
+Slide2 content
+Slide3 title
+●
+Slide3 content
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("multi-slides.ppt"))
+            end
+          end
+        end
+      end
+    end
+  end
+end

  Added: chupa-text-decomposer-libreoffice/test/tests/pptx-tests.rb (+131 -0) 100644
===================================================================
--- /dev/null
+++ chupa-text-decomposer-libreoffice/test/tests/pptx-tests.rb    2019-02-26 14:54:14 +0900 (573e9d1)
@@ -0,0 +1,131 @@
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module PptxTests
+  class << self
+    def included(test_case)
+      test_case.class_eval do
+        include FixtureHelper
+
+        def fixture_path(*components)
+          super("pptx", *components)
+        end
+
+        sub_test_case("target?") do
+          sub_test_case("extension") do
+            def create_data(uri)
+              data = ChupaText::Data.new
+              data.body = ""
+              data.uri = uri
+              data
+            end
+
+            def test_pptx
+              assert_true(@decomposer.target?(create_data("document.pptx")))
+            end
+          end
+
+          sub_test_case("mime-type") do
+            def create_data(mime_type)
+              data = ChupaText::Data.new
+              data.mime_type = mime_type
+              data
+            end
+
+            def test_openxml_presentation
+              mime_type = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
+              assert_true(@decomposer.target?(create_data(mime_type)))
+            end
+          end
+        end
+
+        sub_test_case("decompose") do
+          include DecomposeHelper
+
+          sub_test_case("attributes") do
+            def test_title
+              assert_equal(["Title"], decompose("title"))
+            end
+
+            def test_author
+              assert_equal([nil], decompose("author"))
+            end
+
+            def test_subject
+              assert_equal(["Subject"], decompose("subject"))
+            end
+
+            def test_keywords
+              assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
+            end
+
+            def test_creator
+              assert_equal(["Impress"], decompose("creator"))
+            end
+
+            def test_producer
+              assert_equal(["LibreOffice X.Y"],
+                           normalize_producers(decompose("producer")))
+            end
+
+            def test_creation_date
+              assert_equal([nil], decompose("creation_date"))
+            end
+
+            private
+            def decompose(attribute_name)
+              super(fixture_path("attributes.pptx")).collect do |data|
+                data[attribute_name]
+              end
+            end
+          end
+
+          sub_test_case("one slide") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Slide1 title
+Slide1 content
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("one-slide.pptx"))
+            end
+          end
+
+          sub_test_case("multi slides") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Slide1 title
+Slide1 content
+Slide2 title
+Slide2 content
+Slide3 title
+Slide3 content
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("multi-slides.pptx"))
+            end
+          end
+        end
+      end
+    end
+  end
+end

  Added: chupa-text-decomposer-libreoffice/test/tests/xls-tests.rb (+139 -0) 100644
===================================================================
--- /dev/null
+++ chupa-text-decomposer-libreoffice/test/tests/xls-tests.rb    2019-02-26 14:54:14 +0900 (76e1405)
@@ -0,0 +1,139 @@
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module XlsTests
+  class << self
+    def included(test_case)
+      test_case.class_eval do
+        include FixtureHelper
+
+        def fixture_path(*components)
+          super("xls", *components)
+        end
+
+        sub_test_case("target?") do
+          sub_test_case("extension") do
+            def create_data(uri)
+              data = ChupaText::Data.new
+              data.body = ""
+              data.uri = uri
+              data
+            end
+
+            def test_xls
+              assert_true(@decomposer.target?(create_data("document.xls")))
+            end
+          end
+
+          sub_test_case("mime-type") do
+            def create_data(mime_type)
+              data = ChupaText::Data.new
+              data.mime_type = mime_type
+              data
+            end
+
+            def test_ms_excel
+              mime_type = "application/vnd.ms-excel"
+              assert_true(@decomposer.target?(create_data(mime_type)))
+            end
+          end
+        end
+
+        sub_test_case("decompose") do
+          include DecomposeHelper
+
+          sub_test_case("attributes") do
+            def test_title
+              assert_equal(["Title"], decompose("title"))
+            end
+
+            def test_author
+              assert_equal([nil], decompose("author"))
+            end
+
+            def test_subject
+              assert_equal(["Subject"], decompose("subject"))
+            end
+
+            def test_keywords
+              assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
+            end
+
+            def test_creator
+              assert_equal(["Calc"], decompose("creator"))
+            end
+
+            def test_producer
+              assert_equal(["LibreOffice X.Y"],
+                           normalize_producers(decompose("producer")))
+            end
+
+            def test_creation_date
+              assert_equal([nil], decompose("creation_date"))
+            end
+
+            private
+            def decompose(attribute_name)
+              super(fixture_path("attributes.xls")).collect do |data|
+                data[attribute_name]
+              end
+            end
+          end
+
+          sub_test_case("one sheet") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Sheet1 - A1
+Sheet1 - A2
+Sheet1 - B1
+Sheet1 - B2
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("one-sheet.xls"))
+            end
+          end
+
+          sub_test_case("multi sheets") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Sheet1 - A1
+Sheet1 - A2
+Sheet1 - B1
+Sheet1 - B2
+Sheet2 - A1
+Sheet2 - A2
+Sheet2 - B1
+Sheet2 - B2
+Sheet3 - A1
+Sheet3 - A2
+Sheet3 - B1
+Sheet3 - B2
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("multi-sheets.xls"))
+            end
+          end
+        end
+      end
+    end
+  end
+end

  Added: chupa-text-decomposer-libreoffice/test/tests/xlsx-tests.rb (+139 -0) 100644
===================================================================
--- /dev/null
+++ chupa-text-decomposer-libreoffice/test/tests/xlsx-tests.rb    2019-02-26 14:54:14 +0900 (7bf98d5)
@@ -0,0 +1,139 @@
+# Copyright (C) 2014-2019  Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module XlsxTests
+  class << self
+    def included(test_case)
+      test_case.class_eval do
+        include FixtureHelper
+
+        def fixture_path(*components)
+          super("xlsx", *components)
+        end
+
+        sub_test_case("target?") do
+          sub_test_case("extension") do
+            def create_data(uri)
+              data = ChupaText::Data.new
+              data.body = ""
+              data.uri = uri
+              data
+            end
+
+            def test_xlsx
+              assert_true(@decomposer.target?(create_data("document.xlsx")))
+            end
+          end
+
+          sub_test_case("mime-type") do
+            def create_data(mime_type)
+              data = ChupaText::Data.new
+              data.mime_type = mime_type
+              data
+            end
+
+            def test_openxml_sheet
+              mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+              assert_true(@decomposer.target?(create_data(mime_type)))
+            end
+          end
+        end
+
+        sub_test_case("decompose") do
+          include DecomposeHelper
+
+          sub_test_case("attributes") do
+            def test_title
+              assert_equal(["Title"], decompose("title"))
+            end
+
+            def test_author
+              assert_equal([nil], decompose("author"))
+            end
+
+            def test_subject
+              assert_equal(["Subject"], decompose("subject"))
+            end
+
+            def test_keywords
+              assert_equal(["Keyword1, Keyword2"], decompose("keywords"))
+            end
+
+            def test_creator
+              assert_equal(["Calc"], decompose("creator"))
+            end
+
+            def test_producer
+              assert_equal(["LibreOffice X.Y"],
+                           normalize_producers(decompose("producer")))
+            end
+
+            def test_creation_date
+              assert_equal([nil], decompose("creation_date"))
+            end
+
+            private
+            def decompose(attribute_name)
+              super(fixture_path("attributes.xlsx")).collect do |data|
+                data[attribute_name]
+              end
+            end
+          end
+
+          sub_test_case("one sheet") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Sheet1 - A1
+Sheet1 - A2
+Sheet1 - B1
+Sheet1 - B2
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("one-sheet.xlsx"))
+            end
+          end
+
+          sub_test_case("multi sheets") do
+            def test_body
+              assert_equal([<<-BODY], decompose.collect(&:body))
+Sheet1 - A1
+Sheet1 - A2
+Sheet1 - B1
+Sheet1 - B2
+Sheet2 - A1
+Sheet2 - A2
+Sheet2 - B1
+Sheet2 - B2
+Sheet3 - A1
+Sheet3 - A2
+Sheet3 - B1
+Sheet3 - B2
+              BODY
+            end
+
+            private
+            def decompose
+              super(fixture_path("multi-sheets.xlsx"))
+            end
+          end
+        end
+      end
+    end
+  end
+end



More information about the Groonga-commit mailing list
Back to archive index